From 4414cea13908b8230640f84ef39603d68ff9c377 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 24 Jun 2011 17:05:24 -0700 Subject: Better network stats parsing, integer tags, async. Change NMS parsing to handle extended /proc/ stats formats by pairing values with header keys. Move TrafficStats to integer tags to match kernel internals, and offer well-known tags for system services. Async policy event dispatch from NPMS, and update tests to block for event dispatch. Narrow app policy to exclude apps signed with system key, which are usually critical. Bug: 4948913, 4903489, 4585280 Change-Id: Idb357227ccaa617906411f309371cea18d7bc519 --- api/current.txt | 3 +- core/java/android/net/NetworkPolicyManager.java | 38 ++++++- core/java/android/net/TrafficStats.java | 38 ++++++- .../android/server/NetworkManagementService.java | 99 ++++++++++++---- .../server/net/NetworkPolicyManagerService.java | 106 ++++++++---------- .../android/server/net/NetworkStatsService.java | 7 +- .../servicestests/res/raw/xt_qtaguid_extended | 3 + .../tests/servicestests/res/raw/xt_qtaguid_typical | 32 ++++++ .../server/NetworkManagementServiceTest.java | 121 ++++++++++++++++++++ .../server/NetworkPolicyManagerServiceTest.java | 124 +++++++++++++++++---- .../android/server/NetworkStatsServiceTest.java | 5 +- 11 files changed, 462 insertions(+), 114 deletions(-) create mode 100644 services/tests/servicestests/res/raw/xt_qtaguid_extended create mode 100644 services/tests/servicestests/res/raw/xt_qtaguid_typical create mode 100644 services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java 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 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 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(); + 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 keys = Lists.newArrayList(); + final ArrayList values = Lists.newArrayList(); + final HashMap 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 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 keys, ArrayList values, HashMap 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 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 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 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 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 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 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 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 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 implements IAnswer { + @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(); -- cgit v1.1