summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/Context.java3
-rw-r--r--core/java/android/net/NetworkPolicyManager.java5
-rw-r--r--core/java/android/net/NetworkStats.java54
-rw-r--r--core/java/android/net/TrafficStats.java72
-rw-r--r--core/java/android/os/INetworkManagementService.aidl6
-rw-r--r--core/tests/coretests/src/android/net/NetworkStatsTest.java97
-rw-r--r--services/java/com/android/server/NetworkManagementService.java89
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java2
9 files changed, 275 insertions, 57 deletions
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4c7d87f..a660bd7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1544,6 +1544,9 @@ public abstract class Context {
*/
public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
+ /** {@hide} */
+ public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.net.wifi.WifiManager} for handling management of
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2312bd9..1913aa7 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -16,6 +16,7 @@
package android.net;
+import android.content.Context;
import android.os.RemoteException;
/**
@@ -43,6 +44,10 @@ public class NetworkPolicyManager {
mService = service;
}
+ public static NetworkPolicyManager getSystemService(Context context) {
+ return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
+ }
+
/**
* Set policy flags for specific UID.
*
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 4430e00..0f207bc 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -36,6 +36,9 @@ public class NetworkStats implements Parcelable {
/** {@link #uid} value when entry is summarized over all UIDs. */
public static final int UID_ALL = 0;
+ // NOTE: data should only be accounted for once in this structure; if data
+ // is broken out, the summarized version should not be included.
+
/**
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
* generated.
@@ -81,12 +84,13 @@ public class NetworkStats implements Parcelable {
mTx = new long[size];
}
- public void addEntry(String iface, int uid, long rx, long tx) {
+ public Builder addEntry(String iface, int uid, long rx, long tx) {
mIface[mIndex] = iface;
mUid[mIndex] = uid;
mRx[mIndex] = rx;
mTx[mIndex] = tx;
mIndex++;
+ return this;
}
public NetworkStats build() {
@@ -97,11 +101,17 @@ public class NetworkStats implements Parcelable {
}
}
+ public int length() {
+ // length is identical for all fields
+ return iface.length;
+ }
+
/**
* Find first stats index that matches the requested parameters.
*/
public int findIndex(String iface, int uid) {
- for (int i = 0; i < this.iface.length; i++) {
+ final int length = length();
+ for (int i = 0; i < length; i++) {
if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
return i;
}
@@ -109,13 +119,38 @@ public class NetworkStats implements Parcelable {
return -1;
}
- private static boolean equal(Object a, Object b) {
- return a == b || (a != null && a.equals(b));
+ /**
+ * Subtract the given {@link NetworkStats}, effectively leaving the delta
+ * between two snapshots in time. Assumes that statistics rows collect over
+ * time, and that none of them have disappeared.
+ */
+ public NetworkStats subtract(NetworkStats value) {
+ // result will have our rows, but no meaningful timestamp
+ final int length = length();
+ final NetworkStats.Builder result = new NetworkStats.Builder(-1, length);
+
+ for (int i = 0; i < length; i++) {
+ final String iface = this.iface[i];
+ final int uid = this.uid[i];
+
+ // find remote row that matches, and subtract
+ final int j = value.findIndex(iface, uid);
+ if (j == -1) {
+ // newly appearing row, return entire value
+ result.addEntry(iface, uid, this.rx[i], this.tx[i]);
+ } else {
+ // existing row, subtract remote value
+ final long rx = this.rx[i] - value.rx[j];
+ final long tx = this.tx[i] - value.tx[j];
+ result.addEntry(iface, uid, rx, tx);
+ }
+ }
+
+ return result.build();
}
- /** {@inheritDoc} */
- public int describeContents() {
- return 0;
+ private static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
}
public void dump(String prefix, PrintWriter pw) {
@@ -138,6 +173,11 @@ public class NetworkStats implements Parcelable {
}
/** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(elapsedRealtime);
dest.writeStringArray(iface);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 7ee7a81..c0ff734 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,6 +16,12 @@
package android.net;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
import dalvik.system.BlockGuard;
import java.net.Socket;
@@ -36,6 +42,17 @@ public class TrafficStats {
public final static int UNSUPPORTED = -1;
/**
+ * Snapshot of {@link NetworkStats} when the currently active profiling
+ * session started, or {@code null} if no session active.
+ *
+ * @see #startDataProfiling(Context)
+ * @see #stopDataProfiling(Context)
+ */
+ private static NetworkStats sActiveProfilingStart;
+
+ private static Object sProfilingLock = new Object();
+
+ /**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* <p>
@@ -93,6 +110,44 @@ public class TrafficStats {
}
/**
+ * Start profiling data usage for current UID. Only one profiling session
+ * can be active at a time.
+ *
+ * @hide
+ */
+ public static void startDataProfiling(Context context) {
+ synchronized (sProfilingLock) {
+ if (sActiveProfilingStart != null) {
+ throw new IllegalStateException("already profiling data");
+ }
+
+ // take snapshot in time; we calculate delta later
+ sActiveProfilingStart = getNetworkStatsForUid(context);
+ }
+ }
+
+ /**
+ * Stop profiling data usage for current UID.
+ *
+ * @return Detailed {@link NetworkStats} of data that occurred since last
+ * {@link #startDataProfiling(Context)} call.
+ * @hide
+ */
+ public static NetworkStats stopDataProfiling(Context context) {
+ synchronized (sProfilingLock) {
+ if (sActiveProfilingStart == null) {
+ throw new IllegalStateException("not profiling data");
+ }
+
+ // subtract starting values and return delta
+ final NetworkStats profilingStop = getNetworkStatsForUid(context);
+ final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
+ sActiveProfilingStart = null;
+ return profilingDelta;
+ }
+ }
+
+ /**
* Get the total number of packets transmitted through the mobile interface.
*
* @return number of packets. If the statistics are not supported by this device,
@@ -350,4 +405,21 @@ public class TrafficStats {
* {@link #UNSUPPORTED} will be returned.
*/
public static native long getUidUdpRxPackets(int uid);
+
+ /**
+ * Return detailed {@link NetworkStats} for the current UID. Requires no
+ * special permission.
+ */
+ private static NetworkStats getNetworkStatsForUid(Context context) {
+ final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ final INetworkManagementService service = INetworkManagementService.Stub.asInterface(
+ binder);
+
+ final int uid = android.os.Process.myUid();
+ try {
+ return service.getNetworkStatsUidDetail(uid);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index ecc111b..f17a6f2 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -214,6 +214,12 @@ interface INetworkManagementService
NetworkStats getNetworkStatsDetail();
/**
+ * Return detailed network statistics for the requested UID,
+ * including interface and tag details.
+ */
+ NetworkStats getNetworkStatsUidDetail(int uid);
+
+ /**
* Configures bandwidth throttling on an interface.
*/
void setInterfaceThrottle(String iface, int rxKbps, int txKbps);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
new file mode 100644
index 0000000..45719c2
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+@SmallTest
+public class NetworkStatsTest extends TestCase {
+
+ private static final String TEST_IFACE = "test0";
+
+ public void testFindIndex() throws Exception {
+ final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024)
+ .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102));
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102));
+ assertEquals(0, stats.findIndex(TEST_IFACE, 100));
+ assertEquals(-1, stats.findIndex(TEST_IFACE, 6));
+ }
+
+ public void testSubtractIdenticalData() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ assertEquals(0, result.rx[0]);
+ assertEquals(0, result.tx[0]);
+ assertEquals(0, result.rx[1]);
+ assertEquals(0, result.tx[1]);
+ }
+
+ public void testSubtractIdenticalRows() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1025, 2)
+ .addEntry(TEST_IFACE, 101, 3, 1028).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ // expect delta between measurements
+ assertEquals(1, result.rx[0]);
+ assertEquals(2, result.tx[0]);
+ assertEquals(3, result.rx[1]);
+ assertEquals(4, result.tx[1]);
+ }
+
+ public void testSubtractNewRows() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024)
+ .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ // its okay to have new rows
+ assertEquals(0, result.rx[0]);
+ assertEquals(0, result.tx[0]);
+ assertEquals(0, result.rx[1]);
+ assertEquals(0, result.tx[1]);
+ assertEquals(1024, result.rx[2]);
+ assertEquals(1024, result.tx[2]);
+ }
+
+}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 7c613c1..8f179f5 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,51 +16,34 @@
package com.android.server;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
import android.content.pm.PackageManager;
-import android.net.NetworkStats;
-import android.net.Uri;
-import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
+import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
+import android.net.NetworkStats;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.Binder;
import android.os.INetworkManagementService;
-import android.os.Handler;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
-import java.util.StringTokenizer;
-import android.provider.Settings;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.IOException;
-import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.RandomAccessFile;
-import java.io.Reader;
-import java.lang.IllegalStateException;
-import java.net.InetAddress;
import java.net.Inet4Address;
-import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
import libcore.io.IoUtils;
@@ -69,14 +52,16 @@ import libcore.io.IoUtils;
* @hide
*/
class NetworkManagementService extends INetworkManagementService.Stub {
-
- private static final String TAG = "NetworkManagmentService";
+ private static final String TAG = "NetworkManagementService";
private static final boolean DBG = false;
private static final String NETD_TAG = "NetdConnector";
private static final int ADD = 1;
private static final int REMOVE = 2;
+ /** Base path to UID-granularity network statistics. */
+ private static final File PATH_PROC_UID_STAT = new File("/proc/uid_stat");
+
class NetdResponseCode {
public static final int InterfaceListResult = 110;
public static final int TetherInterfaceListResult = 111;
@@ -891,7 +876,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
return -1;
}
- /** {@inheritDoc} */
+ @Override
public NetworkStats getNetworkStatsSummary() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
@@ -909,31 +894,46 @@ class NetworkManagementService extends INetworkManagementService.Stub {
return stats.build();
}
- /** {@inheritDoc} */
+ @Override
public NetworkStats getNetworkStatsDetail() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- final File procPath = new File("/proc/uid_stat");
- final String[] knownUids = procPath.list();
+ final String[] knownUids = PATH_PROC_UID_STAT.list();
final NetworkStats.Builder stats = new NetworkStats.Builder(
SystemClock.elapsedRealtime(), knownUids.length);
- // TODO: kernel module will provide interface-level stats in future
- // TODO: migrate these stats to come across netd in bulk, instead of all
- // these individual file reads.
for (String uid : knownUids) {
- final File uidPath = new File(procPath, uid);
- final int rx = readSingleIntFromFile(new File(uidPath, "tcp_rcv"));
- final int tx = readSingleIntFromFile(new File(uidPath, "tcp_snd"));
-
final int uidInt = Integer.parseInt(uid);
- stats.addEntry(NetworkStats.IFACE_ALL, uidInt, rx, tx);
+ collectNetworkStatsDetail(stats, uidInt);
}
return stats.build();
}
+ @Override
+ public NetworkStats getNetworkStatsUidDetail(int uid) {
+ if (Binder.getCallingUid() != uid) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ }
+
+ final NetworkStats.Builder stats = new NetworkStats.Builder(
+ SystemClock.elapsedRealtime(), 1);
+ collectNetworkStatsDetail(stats, uid);
+ return stats.build();
+ }
+
+ private void collectNetworkStatsDetail(NetworkStats.Builder stats, int uid) {
+ // TODO: kernel module will provide interface-level stats in future
+ // TODO: migrate these stats to come across netd in bulk, instead of all
+ // these individual file reads.
+ final File uidPath = new File(PATH_PROC_UID_STAT, Integer.toString(uid));
+ final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
+ final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
+ stats.addEntry(NetworkStats.IFACE_ALL, uid, rx, tx);
+ }
+
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
@@ -994,22 +994,17 @@ class NetworkManagementService extends INetworkManagementService.Stub {
}
/**
- * Utility method to read a single plain-text {@link Integer} from the given
+ * Utility method to read a single plain-text {@link Long} from the given
* {@link File}, usually from a {@code /proc/} filesystem.
*/
- private static int readSingleIntFromFile(File file) {
- RandomAccessFile f = null;
+ private static long readSingleLongFromFile(File file) {
try {
- f = new RandomAccessFile(file, "r");
- byte[] buffer = new byte[(int) f.length()];
- f.readFully(buffer);
- return Integer.parseInt(new String(buffer).trim());
+ final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
+ return Long.parseLong(new String(buffer).trim());
} catch (NumberFormatException e) {
return -1;
} catch (IOException e) {
return -1;
- } finally {
- IoUtils.closeQuietly(f);
}
}
}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a7a4f07..312c32b 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -38,8 +38,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String TAG = "NetworkPolicy";
private static final boolean LOGD = true;
- private static final String SERVICE_NAME = "netpolicy";
-
private Context mContext;
/** Current network policy for each UID. */
@@ -56,7 +54,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public void publish(Context context) {
mContext = context;
- ServiceManager.addService(SERVICE_NAME, asBinder());
+ ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, asBinder());
mUidPolicy = new SparseIntArray();
mUidForeground = new SparseBooleanArray();
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index f20d5e5..ca33d32 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -42,6 +42,7 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.Suppress;
import android.text.format.DateUtils;
import android.util.Log;
@@ -54,6 +55,7 @@ import java.util.concurrent.Future;
/**
* Tests for {@link ThrottleService}.
*/
+@LargeTest
public class ThrottleServiceTest extends AndroidTestCase {
private static final String TAG = "ThrottleServiceTest";