diff options
11 files changed, 462 insertions, 114 deletions
diff --git a/api/current.txt b/api/current.txt index 9273b71..6693647 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11240,7 +11240,8 @@ package android.net { method public static long getUidUdpRxPackets(int); method public static long getUidUdpTxBytes(int); method public static long getUidUdpTxPackets(int); - method public static void setThreadStatsTag(java.lang.String); + method public static void setThreadStatsTag(int); + method public static deprecated void setThreadStatsTag(java.lang.String); method public static void tagSocket(java.net.Socket) throws java.net.SocketException; method public static void untagSocket(java.net.Socket) throws java.net.SocketException; field public static final int UNSUPPORTED = -1; // 0xffffffff diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 91af16d..21fad2c 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,14 +16,21 @@ package android.net; +import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.text.format.Time.MONTH_DAY; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; import android.os.RemoteException; import android.text.format.Time; +import com.google.android.collect.Sets; + import java.io.PrintWriter; +import java.util.HashSet; /** * Manager for creating and modifying network policy rules. @@ -210,8 +217,35 @@ public class NetworkPolicyManager { * usually to protect critical system services. */ public static boolean isUidValidForPolicy(Context context, int uid) { - return (uid >= android.os.Process.FIRST_APPLICATION_UID - && uid <= android.os.Process.LAST_APPLICATION_UID); + // first, quick-reject non-applications + if (uid < android.os.Process.FIRST_APPLICATION_UID + || uid > android.os.Process.LAST_APPLICATION_UID) { + return false; + } + + final PackageManager pm = context.getPackageManager(); + final HashSet<Signature> systemSignature; + try { + systemSignature = Sets.newHashSet( + pm.getPackageInfo("android", GET_SIGNATURES).signatures); + } catch (NameNotFoundException e) { + throw new RuntimeException("problem finding system signature", e); + } + + try { + // reject apps signed with system cert + for (String packageName : pm.getPackagesForUid(uid)) { + final HashSet<Signature> packageSignature = Sets.newHashSet( + pm.getPackageInfo(packageName, GET_SIGNATURES).signatures); + if (packageSignature.containsAll(systemSignature)) { + return false; + } + } + } catch (NameNotFoundException e) { + } + + // nothing found above; we can apply policy to UID + return true; } /** {@hide} */ diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index cb47193..040489e 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -16,7 +16,10 @@ package android.net; +import android.app.DownloadManager; +import android.app.backup.BackupManager; import android.content.Context; +import android.media.MediaPlayer; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.RemoteException; @@ -50,6 +53,27 @@ public class TrafficStats { public static final int UID_REMOVED = -4; /** + * Default tag value for {@link DownloadManager} traffic. + * + * @hide + */ + public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFF0001; + + /** + * Default tag value for {@link MediaPlayer} traffic. + * + * @hide + */ + public static final int TAG_SYSTEM_MEDIA = 0xFFFF0002; + + /** + * Default tag value for {@link BackupManager} traffic. + * + * @hide + */ + public static final int TAG_SYSTEM_BACKUP = 0xFFFF0003; + + /** * Snapshot of {@link NetworkStats} when the currently active profiling * session started, or {@code null} if no session active. * @@ -67,12 +91,20 @@ public class TrafficStats { * Changes only take effect during subsequent calls to * {@link #tagSocket(Socket)}. */ - public static void setThreadStatsTag(String tag) { + public static void setThreadStatsTag(int tag) { BlockGuard.setThreadSocketStatsTag(tag); } + /** + * @deprecated unsupported, will eventually be removed + */ + @Deprecated + public static void setThreadStatsTag(String tag) { + setThreadStatsTag(tag.hashCode()); + } + public static void clearThreadStatsTag() { - BlockGuard.setThreadSocketStatsTag(null); + BlockGuard.setThreadSocketStatsTag(-1); } /** @@ -103,7 +135,7 @@ public class TrafficStats { * parameters. When finished, call {@link #untagSocket(Socket)} to remove * statistics parameters. * - * @see #setThreadStatsTag(String) + * @see #setThreadStatsTag(int) * @see #setThreadStatsUid(int) */ public static void tagSocket(Socket socket) throws SocketException { diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index e730d0d..630aaf9 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -38,6 +38,11 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Slog; +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; + +import dalvik.system.BlockGuard; + import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; @@ -48,6 +53,7 @@ import java.io.InputStreamReader; import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.HashMap; import java.util.NoSuchElementException; import java.util.StringTokenizer; import java.util.concurrent.CountDownLatch; @@ -65,9 +71,18 @@ class NetworkManagementService extends INetworkManagementService.Stub { private static final int ADD = 1; private static final int REMOVE = 2; + /** Path to {@code /proc/uid_stat}. */ @Deprecated - private static final File STATS_UIDSTAT = new File("/proc/uid_stat"); - private static final File STATS_NETFILTER = new File("/proc/net/xt_qtaguid/stats"); + private final File mProcStatsUidstat; + /** Path to {@code /proc/net/xt_qtaguid/stats}. */ + private final File mProcStatsNetfilter; + + /** {@link #mProcStatsNetfilter} headers. */ + private static final String KEY_IFACE = "iface"; + private static final String KEY_TAG_HEX = "acct_tag_hex"; + private static final String KEY_UID = "uid_tag_int"; + private static final String KEY_RX = "rx_bytes"; + private static final String KEY_TX = "tx_bytes"; class NetdResponseCode { public static final int InterfaceListResult = 110; @@ -107,10 +122,13 @@ class NetworkManagementService extends INetworkManagementService.Stub { * * @param context Binder context for this service */ - private NetworkManagementService(Context context) { + private NetworkManagementService(Context context, File procRoot) { mContext = context; mObservers = new ArrayList<INetworkManagementEventObserver>(); + mProcStatsUidstat = new File(procRoot, "uid_stat"); + mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats"); + if ("simulator".equals(SystemProperties.get("ro.product.device"))) { return; } @@ -121,7 +139,8 @@ class NetworkManagementService extends INetworkManagementService.Stub { } public static NetworkManagementService create(Context context) throws InterruptedException { - NetworkManagementService service = new NetworkManagementService(context); + NetworkManagementService service = new NetworkManagementService( + context, new File("/proc/")); if (DBG) Slog.d(TAG, "Creating NetworkManagementService"); service.mThread.start(); if (DBG) Slog.d(TAG, "Awaiting socket connection"); @@ -130,6 +149,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { return service; } + // @VisibleForTesting + public static NetworkManagementService createForTest(Context context, File procRoot) { + // TODO: eventually connect with mock netd + return new NetworkManagementService(context, procRoot); + } + public void registerObserver(INetworkManagementEventObserver obs) { Slog.d(TAG, "Registering observer"); mObservers.add(obs); @@ -888,7 +913,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - if (STATS_NETFILTER.exists()) { + if (mProcStatsNetfilter.exists()) { return getNetworkStatsDetailNetfilter(UID_ALL); } else { return getNetworkStatsDetailUidstat(UID_ALL); @@ -902,7 +927,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); } - if (STATS_NETFILTER.exists()) { + if (mProcStatsNetfilter.exists()) { return getNetworkStatsDetailNetfilter(uid); } else { return getNetworkStatsDetailUidstat(uid); @@ -914,35 +939,35 @@ class NetworkManagementService extends INetworkManagementService.Stub { */ private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) { final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); + final ArrayList<String> keys = Lists.newArrayList(); + final ArrayList<String> values = Lists.newArrayList(); + final HashMap<String, String> parsed = Maps.newHashMap(); BufferedReader reader = null; try { - reader = new BufferedReader(new FileReader(STATS_NETFILTER)); + reader = new BufferedReader(new FileReader(mProcStatsNetfilter)); - // assumes format from kernel: - // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes - - // skip first line, which is legend + // parse first line as header String line = reader.readLine(); - while ((line = reader.readLine()) != null) { - final StringTokenizer t = new StringTokenizer(line); + splitLine(line, keys); - final String idx = t.nextToken(); - final String iface = t.nextToken(); + // parse remaining lines + while ((line = reader.readLine()) != null) { + splitLine(line, values); + parseLine(keys, values, parsed); try { - // TODO: kernel currently emits tag in upper half of long; - // eventually switch to directly using int. - final int tag = (int) (Long.parseLong(t.nextToken().substring(2), 16) >> 32); - final int uid = Integer.parseInt(t.nextToken()); - final long rx = Long.parseLong(t.nextToken()); - final long tx = Long.parseLong(t.nextToken()); + final String iface = parsed.get(KEY_IFACE); + final int tag = BlockGuard.kernelToTag(parsed.get(KEY_TAG_HEX)); + final int uid = Integer.parseInt(parsed.get(KEY_UID)); + final long rx = Long.parseLong(parsed.get(KEY_RX)); + final long tx = Long.parseLong(parsed.get(KEY_TX)); if (limitUid == UID_ALL || limitUid == uid) { stats.addEntry(iface, uid, tag, rx, tx); } } catch (NumberFormatException e) { - Slog.w(TAG, "problem parsing stats for idx " + idx + ": " + e); + Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); } } } catch (IOException e) { @@ -964,7 +989,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { private NetworkStats getNetworkStatsDetailUidstat(int limitUid) { final String[] knownUids; if (limitUid == UID_ALL) { - knownUids = STATS_UIDSTAT.list(); + knownUids = mProcStatsUidstat.list(); } else { knownUids = new String[] { String.valueOf(limitUid) }; } @@ -973,7 +998,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { SystemClock.elapsedRealtime(), knownUids.length); for (String uid : knownUids) { final int uidInt = Integer.parseInt(uid); - final File uidPath = new File(STATS_UIDSTAT, uid); + final File uidPath = new File(mProcStatsUidstat, uid); final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx); @@ -1048,6 +1073,32 @@ class NetworkManagementService extends INetworkManagementService.Stub { } /** + * Split given line into {@link ArrayList}. + */ + private static void splitLine(String line, ArrayList<String> outSplit) { + outSplit.clear(); + + final StringTokenizer t = new StringTokenizer(line); + while (t.hasMoreTokens()) { + outSplit.add(t.nextToken()); + } + } + + /** + * Zip the two given {@link ArrayList} as key and value pairs into + * {@link HashMap}. + */ + private static void parseLine( + ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) { + outParsed.clear(); + + final int size = Math.min(keys.size(), values.size()); + for (int i = 0; i < size; i++) { + outParsed.put(keys.get(i), values.get(i)); + } + } + + /** * Utility method to read a single plain-text {@link Long} from the given * {@link File}, usually from a {@code /proc/} filesystem. */ diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 584cd03..12d3ed8 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -72,6 +72,7 @@ import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IPowerManager; +import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.TelephonyManager; @@ -148,6 +149,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; + private static final int MSG_RULES_CHANGED = 0x1; + private static final int MSG_METERED_IFACES_CHANGED = 0x2; + private final Context mContext; private final IActivityManager mActivityManager; private final IPowerManager mPowerManager; @@ -210,7 +214,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); + mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback); mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml")); } @@ -269,9 +273,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like AMS should only be calling us mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG); - // skip when UID couldn't have any policy - if (!isUidValidForPolicy(mContext, uid)) return; - synchronized (mRulesLock) { // because a uid can have multiple pids running inside, we need to // remember all pid states and summarize foreground at uid level. @@ -292,9 +293,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like AMS should only be calling us mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG); - // skip when UID couldn't have any policy - if (!isUidValidForPolicy(mContext, uid)) return; - synchronized (mRulesLock) { // clear records and recompute, when they exist final SparseBooleanArray pidForeground = mUidPidForeground.get(uid); @@ -599,20 +597,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - // dispatch changed rule to existing listeners - // TODO: dispatch outside of holding lock final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); - final int length = mListeners.beginBroadcast(); - for (int i = 0; i < length; i++) { - final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); - if (listener != null) { - try { - listener.onMeteredIfacesChanged(meteredIfaces); - } catch (RemoteException e) { - } - } - } - mListeners.finishBroadcast(); + mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget(); } /** @@ -804,32 +790,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mListeners.register(listener); - synchronized (mRulesLock) { - // dispatch any existing rules to new listeners - // TODO: dispatch outside of holding lock - final int size = mUidRules.size(); - for (int i = 0; i < size; i++) { - final int uid = mUidRules.keyAt(i); - final int uidRules = mUidRules.valueAt(i); - if (uidRules != RULE_ALLOW_ALL) { - try { - listener.onUidRulesChanged(uid, uidRules); - } catch (RemoteException e) { - } - } - } - - // dispatch any metered ifaces to new listeners - // TODO: dispatch outside of holding lock - if (mMeteredIfaces.size() > 0) { - final String[] meteredIfaces = mMeteredIfaces.toArray( - new String[mMeteredIfaces.size()]); - try { - listener.onMeteredIfacesChanged(meteredIfaces); - } catch (RemoteException e) { - } - } - } + // TODO: consider dispatching existing rules to new listeners } @Override @@ -978,8 +939,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } private void updateRulesForUidLocked(int uid) { - if (!isUidValidForPolicy(mContext, uid)) return; - final int uidPolicy = getUidPolicy(uid); final boolean uidForeground = isUidForeground(uid); @@ -999,19 +958,50 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { //kernelSetUidRejectPaid(uid, rejectPaid); // dispatch changed rule to existing listeners - // TODO: dispatch outside of holding lock - final int length = mListeners.beginBroadcast(); - for (int i = 0; i < length; i++) { - final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); - if (listener != null) { - try { - listener.onUidRulesChanged(uid, uidRules); - } catch (RemoteException e) { + mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget(); + } + + private Handler.Callback mHandlerCallback = new Handler.Callback() { + /** {@inheritDoc} */ + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_RULES_CHANGED: { + final int uid = msg.arg1; + final int uidRules = msg.arg2; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + if (listener != null) { + try { + listener.onUidRulesChanged(uid, uidRules); + } catch (RemoteException e) { + } + } + } + mListeners.finishBroadcast(); + return true; + } + case MSG_METERED_IFACES_CHANGED: { + final String[] meteredIfaces = (String[]) msg.obj; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + if (listener != null) { + try { + listener.onMeteredIfacesChanged(meteredIfaces); + } catch (RemoteException e) { + } + } + } + mListeners.finishBroadcast(); + return true; + } + default: { + return false; } } } - mListeners.finishBroadcast(); - } + }; private String getActiveSubscriberId() { final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService( diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 4a79d17..7610a11 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -124,8 +124,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private PendingIntent mPollIntent; // TODO: listen for kernel push events through netd instead of polling - // TODO: watch for UID uninstall, and transfer stats into single bucket - // TODO: trim empty history objects entirely private static final long KB_IN_BYTES = 1024; @@ -506,8 +504,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { try { networkSnapshot = mNetworkManager.getNetworkStatsSummary(); uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null; + } catch (IllegalStateException e) { + Slog.w(TAG, "problem reading network stats: " + e); + return; } catch (RemoteException e) { - Slog.w(TAG, "problem reading network stats"); + Slog.w(TAG, "problem reading network stats: " + e); return; } diff --git a/services/tests/servicestests/res/raw/xt_qtaguid_extended b/services/tests/servicestests/res/raw/xt_qtaguid_extended new file mode 100644 index 0000000..5bef3dd --- /dev/null +++ b/services/tests/servicestests/res/raw/xt_qtaguid_extended @@ -0,0 +1,3 @@ +acct_tag_hex uid_tag_int iface rx_bytes rx_packets tx_bytes tx_packets teleported_goats +0x0 1000 test0 1024 10 2048 20 2716057 +0x0000F00D00000000 1000 test0 512 5 512 5 3370318 diff --git a/services/tests/servicestests/res/raw/xt_qtaguid_typical b/services/tests/servicestests/res/raw/xt_qtaguid_typical new file mode 100644 index 0000000..7c4f04e --- /dev/null +++ b/services/tests/servicestests/res/raw/xt_qtaguid_typical @@ -0,0 +1,32 @@ +idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes +1 wlan0 0x0 0 14615 4270 +2 wlan0 0x0 1000 5175 915 +3 wlan0 0x0 1021 3381 903 +4 wlan0 0x0 10004 333821 53558 +5 wlan0 0x0 10010 4888 37363 +6 wlan0 0x0 10013 52 104 +7 wlan0 0x74182ada00000000 10004 18725 1066 +8 rmnet0 0x0 0 301274 30244 +9 rmnet0 0x0 1000 304 441 +10 rmnet0 0x0 1013 2880 2272 +11 rmnet0 0x0 1021 31407 8430 +12 rmnet0 0x0 10003 32665 3814 +13 rmnet0 0x0 10004 2373141 420112 +14 rmnet0 0x0 10010 870370 1111727 +15 rmnet0 0x0 10013 240 240 +16 rmnet0 0x0 10016 16703 13512 +17 rmnet0 0x0 10017 3990 3269 +18 rmnet0 0x0 10018 474504 14516062 +19 rmnet0 0x0 10019 782804 71077 +20 rmnet0 0x0 10022 70671 49684 +21 rmnet0 0x0 10029 5785354 397159 +22 rmnet0 0x0 10033 2102 1686 +23 rmnet0 0x0 10034 15495464 227694 +24 rmnet0 0x0 10037 31184994 684122 +25 rmnet0 0x0 10051 298687 113485 +26 rmnet0 0x0 10056 29504 20669 +27 rmnet0 0x0 10069 683 596 +28 rmnet0 0x0 10072 34051 12453 +29 rmnet0 0x0 10077 7025393 213866 +30 rmnet0 0x0 10081 354 1178 +31 rmnet0 0x74182ada00000000 10037 28507378 437004 diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java new file mode 100644 index 0000000..8b752ee --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java @@ -0,0 +1,121 @@ +/* + * 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 com.android.server; + +import static dalvik.system.BlockGuard.kernelToTag; +import static dalvik.system.BlockGuard.tagToKernel; + +import android.content.res.Resources; +import android.net.NetworkStats; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; + +import com.android.frameworks.servicestests.R; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import libcore.io.IoUtils; +import libcore.io.Streams; + +/** + * Tests for {@link NetworkManagementService}. + */ +@LargeTest +public class NetworkManagementServiceTest extends AndroidTestCase { + private File mTestProc; + private NetworkManagementService mService; + + @Override + public void setUp() throws Exception { + super.setUp(); + + mTestProc = getContext().getFilesDir(); + mService = NetworkManagementService.createForTest(mContext, mTestProc); + } + + @Override + public void tearDown() throws Exception { + mService = null; + + super.tearDown(); + } + + public void testNetworkStatsDetail() throws Exception { + stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats")); + + final NetworkStats stats = mService.getNetworkStatsDetail(); + assertEquals(31, stats.size); + assertStatsEntry(stats, "wlan0", 0, 0, 14615L, 4270L); + assertStatsEntry(stats, "wlan0", 10004, 0, 333821L, 53558L); + assertStatsEntry(stats, "wlan0", 10004, 1947740890, 18725L, 1066L); + assertStatsEntry(stats, "rmnet0", 10037, 0, 31184994L, 684122L); + assertStatsEntry(stats, "rmnet0", 10037, 1947740890, 28507378L, 437004L); + } + + public void testNetworkStatsDetailExtended() throws Exception { + stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats")); + + final NetworkStats stats = mService.getNetworkStatsDetail(); + assertEquals(2, stats.size); + assertStatsEntry(stats, "test0", 1000, 0, 1024L, 2048L); + assertStatsEntry(stats, "test0", 1000, 0xF00D, 512L, 512L); + } + + public void testKernelTags() throws Exception { + assertEquals("0", tagToKernel(0x0)); + assertEquals("214748364800", tagToKernel(0x32)); + assertEquals("9223372032559808512", tagToKernel(Integer.MAX_VALUE)); + assertEquals("0", tagToKernel(Integer.MIN_VALUE)); + assertEquals("9223369837831520256", tagToKernel(Integer.MIN_VALUE - 512)); + + assertEquals(0, kernelToTag("0x0000000000000000")); + assertEquals(0x32, kernelToTag("0x0000003200000000")); + assertEquals(2147483647, kernelToTag("0x7fffffff00000000")); + assertEquals(0, kernelToTag("0x0000000000000000")); + assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000")); + + } + + /** + * Copy a {@link Resources#openRawResource(int)} into {@link File} for + * testing purposes. + */ + private void stageFile(int rawId, File file) throws Exception { + new File(file.getParent()).mkdirs(); + InputStream in = null; + OutputStream out = null; + try { + in = getContext().getResources().openRawResource(rawId); + out = new FileOutputStream(file); + Streams.copy(in, out); + } finally { + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(out); + } + } + + private static void assertStatsEntry( + NetworkStats stats, String iface, int uid, int tag, long rx, long tx) { + final int i = stats.findIndex(iface, uid, tag); + assertEquals(rx, stats.rx[i]); + assertEquals(tx, stats.tx[i]); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 07e5425..324e896 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -41,7 +41,9 @@ import android.app.IActivityManager; import android.app.INotificationManager; import android.app.IProcessObserver; import android.content.Intent; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.INetworkPolicyListener; @@ -63,12 +65,17 @@ import android.text.format.Time; import android.util.TrustedTime; import com.android.server.net.NetworkPolicyManagerService; +import com.google.common.util.concurrent.AbstractFuture; import org.easymock.Capture; import org.easymock.EasyMock; +import org.easymock.IAnswer; import java.io.File; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * Tests for {@link NetworkPolicyManagerService}. @@ -118,11 +125,27 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { public String[] getPackagesForUid(int uid) { return new String[] { "com.example" }; } + + @Override + public PackageInfo getPackageInfo(String packageName, int flags) { + final PackageInfo info = new PackageInfo(); + final Signature signature; + if ("android".equals(packageName)) { + signature = new Signature("F00D"); + } else { + signature = new Signature("DEAD"); + } + info.signatures = new Signature[] { signature }; + return info; + } }; } }; mPolicyDir = getContext().getFilesDir(); + for (File file : mPolicyDir.listFiles()) { + file.delete(); + } mActivityManager = createMock(IActivityManager.class); mPowerManager = createMock(IPowerManager.class); @@ -228,81 +251,110 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } public void testScreenChangesRules() throws Exception { - // push strict policy for foreground uid, verify ALLOW rule - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + Future<Void> future; + + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); + future.get(); + verifyAndReset(); + + // push strict policy for foreground uid, verify ALLOW rule + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); + replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); + future.get(); verifyAndReset(); // now turn screen off and verify REJECT rule expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce(); - expectRulesChanged(UID_A, RULE_REJECT_METERED); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF)); + future.get(); verifyAndReset(); // and turn screen back on, verify ALLOW rule restored expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce(); - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON)); + future.get(); verifyAndReset(); } public void testPolicyNone() throws Exception { + Future<Void> future; + + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); + replay(); + mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); + future.get(); + verifyAndReset(); + // POLICY_NONE should RULE_ALLOW in foreground - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mService.setUidPolicy(UID_A, POLICY_NONE); - mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); + future.get(); verifyAndReset(); // POLICY_NONE should RULE_ALLOW in background - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); + future.get(); verifyAndReset(); } public void testPolicyReject() throws Exception { + Future<Void> future; + // POLICY_REJECT should RULE_ALLOW in background - expectRulesChanged(UID_A, RULE_REJECT_METERED); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); + future.get(); verifyAndReset(); // POLICY_REJECT should RULE_ALLOW in foreground - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); + future.get(); verifyAndReset(); // POLICY_REJECT should RULE_REJECT in background - expectRulesChanged(UID_A, RULE_REJECT_METERED); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); + future.get(); verifyAndReset(); } public void testPolicyRejectAddRemove() throws Exception { + Future<Void> future; + // POLICY_NONE should have RULE_ALLOW in background - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); - mService.setUidPolicy(UID_A, POLICY_NONE); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); + mService.setUidPolicy(UID_A, POLICY_NONE); + future.get(); verifyAndReset(); // adding POLICY_REJECT should cause RULE_REJECT - expectRulesChanged(UID_A, RULE_REJECT_METERED); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); + future.get(); verifyAndReset(); // removing POLICY_REJECT should return us to RULE_ALLOW - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mService.setUidPolicy(UID_A, POLICY_NONE); + future.get(); verifyAndReset(); } @@ -350,6 +402,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { long elapsedRealtime = 0; NetworkState[] state = null; NetworkStats stats = null; + Future<Void> future; final long TIME_FEB_15 = 1171497600000L; final long TIME_MAR_10 = 1173484800000L; @@ -360,10 +413,11 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { state = new NetworkState[] { buildWifi() }; expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); expectTime(TIME_MAR_10 + elapsedRealtime); - expectMeteredIfacesChanged(); + future = expectMeteredIfacesChanged(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + future.get(); verifyAndReset(); // now change cycle to be on 15th, and test in early march, to verify we @@ -381,26 +435,31 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // TODO: write up NetworkManagementService mock expectClearNotifications(); - expectMeteredIfacesChanged(TEST_IFACE); + future = expectMeteredIfacesChanged(TEST_IFACE); replay(); setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L)); + future.get(); verifyAndReset(); } public void testUidRemovedPolicyCleared() throws Exception { + Future<Void> future; + // POLICY_REJECT should RULE_REJECT in background - expectRulesChanged(UID_A, RULE_REJECT_METERED); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); + future.get(); verifyAndReset(); // uninstall should clear RULE_REJECT - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); final Intent intent = new Intent(ACTION_UID_REMOVED); intent.putExtra(EXTRA_UID, UID_A); mServiceContext.sendBroadcast(intent); + future.get(); verifyAndReset(); } @@ -435,14 +494,35 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expectLastCall().anyTimes(); } - private void expectRulesChanged(int uid, int policy) throws Exception { + private Future<Void> expectRulesChanged(int uid, int policy) throws Exception { + final FutureAnswer future = new FutureAnswer(); mPolicyListener.onUidRulesChanged(eq(uid), eq(policy)); - expectLastCall().atLeastOnce(); + expectLastCall().andAnswer(future); + return future; } - private void expectMeteredIfacesChanged(String... ifaces) throws Exception { + private Future<Void> expectMeteredIfacesChanged(String... ifaces) throws Exception { + final FutureAnswer future = new FutureAnswer(); mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces)); - expectLastCall().atLeastOnce(); + expectLastCall().andAnswer(future); + return future; + } + + private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> { + @Override + public Void get() throws InterruptedException, ExecutionException { + try { + return get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + + @Override + public Void answer() { + set(null); + return null; + } } private void replay() { diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index 636d059..903f2b0 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -59,7 +59,6 @@ import android.os.INetworkManagementService; import android.telephony.TelephonyManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; -import android.util.Log; import android.util.TrustedTime; import com.android.server.net.NetworkStatsService; @@ -611,6 +610,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mAlarmManager.setInexactRepeating( eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class)); expectLastCall().atLeastOnce(); + + mNetManager.setBandwidthControlEnabled(true); + expectLastCall().atLeastOnce(); } private void expectNetworkState(NetworkState... state) throws Exception { @@ -631,6 +633,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory) throws Exception { + expect(mSettings.getEnabled()).andReturn(true).anyTimes(); expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes(); expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes(); expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes(); |