From 8af5ad1575e7a2afef21fd2abf7b4232c91ee68a Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 26 Jun 2017 19:50:41 -0600 Subject: [PATCH 1/9] Allocate well-known tag for app store updates. This way an app store can shift blame for update-related network traffic onto the app that is being updated. Using a well-known tag makes it easy for developers to identify that they didn't explicitly request the traffic at runtime, similar to how backup/restore traffic is handled. Bug: 38282350 Test: builds, boots Change-Id: I003dd7c9615d4ab318250f1e44fa5d195ac94d23 --- core/java/android/net/TrafficStats.java | 44 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index f9346165df..19857076a0 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -109,25 +109,26 @@ public class TrafficStats { */ public static final int TAG_SYSTEM_RESTORE = 0xFFFFFF04; - /** @hide */ - public static final int TAG_SYSTEM_DHCP = 0xFFFFFF05; - /** @hide */ - public static final int TAG_SYSTEM_NTP = 0xFFFFFF06; - /** @hide */ - public static final int TAG_SYSTEM_PROBE = 0xFFFFFF07; - /** @hide */ - public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFF08; - /** @hide */ - public static final int TAG_SYSTEM_GPS = 0xFFFFFF09; - /** @hide */ - public static final int TAG_SYSTEM_PAC = 0xFFFFFF0A; - /** - * Sockets that are strictly local on device; never hits network. + * Default tag value for code or resources downloaded by an app store on + * behalf of the app, such as app updates. * * @hide */ - public static final int TAG_SYSTEM_LOCAL = 0xFFFFFFAA; + public static final int TAG_SYSTEM_CODE = 0xFFFFFF05; + + /** @hide */ + public static final int TAG_SYSTEM_DHCP = 0xFFFFFF40; + /** @hide */ + public static final int TAG_SYSTEM_NTP = 0xFFFFFF41; + /** @hide */ + public static final int TAG_SYSTEM_PROBE = 0xFFFFFF42; + /** @hide */ + public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFF43; + /** @hide */ + public static final int TAG_SYSTEM_GPS = 0xFFFFFF44; + /** @hide */ + public static final int TAG_SYSTEM_PAC = 0xFFFFFF45; private static INetworkStatsService sStatsService; @@ -209,6 +210,19 @@ public class TrafficStats { setThreadStatsTag(TAG_SYSTEM_RESTORE); } + /** + * Set active tag to use when accounting {@link Socket} traffic originating + * from the current thread. The tag used internally is well-defined to + * distinguish all code-related traffic, such as updates performed by an app + * store. + * + * @hide + */ + @SystemApi + public static void setThreadStatsTagCode() { + setThreadStatsTag(TAG_SYSTEM_CODE); + } + /** * Get the active tag used when accounting {@link Socket} traffic originating * from the current thread. Only one active tag per thread is supported. From 65501f8094e5671b13c1697db93700aea4b3ad29 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 27 Jun 2017 11:01:36 -0600 Subject: [PATCH 2/9] Only require that system UIDs tag their sockets. Apps with a normal UID are typically isolated enough to not require socket tagging; we're mostly interested in tracking down internal UIDs that have lots of code sharing the same UID. Also fix up everyone doing manual string checks of Build.TYPE, since we now have first-class fields for those. Bug: 38126076 Test: builds, boots Change-Id: I3a40348196bd8459289f2b9355d9783a07f1e7dd --- core/java/android/net/NetworkIdentity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 0775bdaaf3..acd7b56027 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -157,7 +157,7 @@ public class NetworkIdentity implements Comparable { * Scrub given IMSI on production builds. */ public static String scrubSubscriberId(String subscriberId) { - if ("eng".equals(Build.TYPE)) { + if (Build.IS_ENG) { return subscriberId; } else if (subscriberId != null) { // TODO: parse this as MCC+MNC instead of hard-coding From 4fd1783314c32ae291caccb43d39dc38263f2105 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 26 Jun 2017 11:24:47 -0600 Subject: [PATCH 3/9] Unify permissions under UPDATE_DEVICE_STATS. The UPDATE_DEVICE_STATS permission has become the de-facto mechanism that platform components use to shift blame for resource usage, so it's confusing to also have a separate MODIFY_NETWORK_ACCOUNTING permission. So this change replaces MODIFY_NETWORK_ACCOUNTING with UPDATE_DEVICE_STATS. Bug: 62483389 Test: builds, boots Exempt-From-Owner-Approval: Bug 63673347 Change-Id: I872759f02327b6d531ec2338bd876890aded60ad --- .../java/com/android/server/net/NetworkStatsService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index ab685ca28b..8209adeb89 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -18,7 +18,6 @@ package com.android.server.net; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; -import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.content.Intent.ACTION_SHUTDOWN; import static android.content.Intent.ACTION_UID_REMOVED; @@ -689,7 +688,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public void incrementOperationCount(int uid, int tag, int operationCount) { if (Binder.getCallingUid() != uid) { - mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, TAG); } if (operationCount < 0) { @@ -710,7 +710,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public void setUidForeground(int uid, boolean uidForeground) { - mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG); + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); synchronized (mStatsLock) { final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT; @@ -750,7 +750,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public void advisePersistThreshold(long thresholdBytes) { - mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG); + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); assertBandwidthControlEnabled(); // clamp threshold into safe range From e71cc98cb107ee845fb2e1c267545879b0e3de6e Mon Sep 17 00:00:00 2001 From: Hugo Benichi Date: Tue, 2 May 2017 13:36:28 +0900 Subject: [PATCH 4/9] NsdManager: remove duplicated argument validation This patch simplifies argument validation in NsdManager public api and regroup duplicated validation into common methods. This makes stack traces more actionable as now specific errors will cause the api to throw exception from specific methods, whereas before IllegalArgumentException would be thrown from inside the same api method for different reasons. This patch also includes a couple of other small cleanups. Test: $ runtest -x frameworks/base/tests/net/../NsdManagerTest.java Bug: 35362108 Bug: 37013369 Bug: 62044295 Bug: 63826516 Merged-In: Iaad13e13976e9bf8f508d7188f823f8184ac414b (cherry pick from commit f2c64f87259679c44ef151b08cccab43ca5d4342) Change-Id: I5e6a1ecc6b98069ef0089bbceecf73f7692df227 --- core/java/android/net/nsd/NsdManager.java | 176 +++++++++------------- 1 file changed, 73 insertions(+), 103 deletions(-) diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index ace3748664..1e41eea925 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -16,6 +16,10 @@ package android.net.nsd; +import static com.android.internal.util.Preconditions.checkArgument; +import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.internal.util.Preconditions.checkStringNotEmpty; + import android.annotation.SdkConstant; import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; @@ -240,12 +244,12 @@ public final class NsdManager { return name; } + private static int FIRST_LISTENER_KEY = 1; + private final INsdManager mService; private final Context mContext; - private static final int INVALID_LISTENER_KEY = 0; - private static final int BUSY_LISTENER_KEY = -1; - private int mListenerKey = 1; + private int mListenerKey = FIRST_LISTENER_KEY; private final SparseArray mListenerMap = new SparseArray(); private final SparseArray mServiceMap = new SparseArray<>(); private final Object mMapLock = new Object(); @@ -311,7 +315,6 @@ public final class NsdManager { public void onServiceFound(NsdServiceInfo serviceInfo); public void onServiceLost(NsdServiceInfo serviceInfo); - } /** Interface for callback invocation for service registration */ @@ -342,8 +345,9 @@ public final class NsdManager { @Override public void handleMessage(Message message) { - if (DBG) Log.d(TAG, "received " + nameOf(message.what)); - switch (message.what) { + final int what = message.what; + final int key = message.arg2; + switch (what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); return; @@ -356,19 +360,26 @@ public final class NsdManager { default: break; } - Object listener = getListener(message.arg2); + final Object listener; + final NsdServiceInfo ns; + synchronized (mMapLock) { + listener = mListenerMap.get(key); + ns = mServiceMap.get(key); + } if (listener == null) { Log.d(TAG, "Stale key " + message.arg2); return; } - NsdServiceInfo ns = getNsdService(message.arg2); - switch (message.what) { + if (DBG) { + Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns); + } + switch (what) { case DISCOVER_SERVICES_STARTED: String s = getNsdServiceInfoType((NsdServiceInfo) message.obj); ((DiscoveryListener) listener).onDiscoveryStarted(s); break; case DISCOVER_SERVICES_FAILED: - removeListener(message.arg2); + removeListener(key); ((DiscoveryListener) listener).onStartDiscoveryFailed(getNsdServiceInfoType(ns), message.arg1); break; @@ -381,16 +392,16 @@ public final class NsdManager { case STOP_DISCOVERY_FAILED: // TODO: failure to stop discovery should be internal and retried internally, as // the effect for the client is indistinguishable from STOP_DISCOVERY_SUCCEEDED - removeListener(message.arg2); + removeListener(key); ((DiscoveryListener) listener).onStopDiscoveryFailed(getNsdServiceInfoType(ns), message.arg1); break; case STOP_DISCOVERY_SUCCEEDED: - removeListener(message.arg2); + removeListener(key); ((DiscoveryListener) listener).onDiscoveryStopped(getNsdServiceInfoType(ns)); break; case REGISTER_SERVICE_FAILED: - removeListener(message.arg2); + removeListener(key); ((RegistrationListener) listener).onRegistrationFailed(ns, message.arg1); break; case REGISTER_SERVICE_SUCCEEDED: @@ -398,7 +409,7 @@ public final class NsdManager { (NsdServiceInfo) message.obj); break; case UNREGISTER_SERVICE_FAILED: - removeListener(message.arg2); + removeListener(key); ((RegistrationListener) listener).onUnregistrationFailed(ns, message.arg1); break; case UNREGISTER_SERVICE_SUCCEEDED: @@ -408,11 +419,11 @@ public final class NsdManager { ((RegistrationListener) listener).onServiceUnregistered(ns); break; case RESOLVE_SERVICE_FAILED: - removeListener(message.arg2); + removeListener(key); ((ResolveListener) listener).onResolveFailed(ns, message.arg1); break; case RESOLVE_SERVICE_SUCCEEDED: - removeListener(message.arg2); + removeListener(key); ((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj); break; default: @@ -422,40 +433,27 @@ public final class NsdManager { } } - // if the listener is already in the map, reject it. Otherwise, add it and - // return its key. + private int nextListenerKey() { + // Ensure mListenerKey >= FIRST_LISTENER_KEY; + mListenerKey = Math.max(FIRST_LISTENER_KEY, mListenerKey + 1); + return mListenerKey; + } + + // Assert that the listener is not in the map, then add it and returns its key private int putListener(Object listener, NsdServiceInfo s) { - if (listener == null) return INVALID_LISTENER_KEY; - int key; + checkListener(listener); + final int key; synchronized (mMapLock) { int valueIndex = mListenerMap.indexOfValue(listener); - if (valueIndex != -1) { - return BUSY_LISTENER_KEY; - } - do { - key = mListenerKey++; - } while (key == INVALID_LISTENER_KEY); + checkArgument(valueIndex == -1, "listener already in use"); + key = nextListenerKey(); mListenerMap.put(key, listener); mServiceMap.put(key, s); } return key; } - private Object getListener(int key) { - if (key == INVALID_LISTENER_KEY) return null; - synchronized (mMapLock) { - return mListenerMap.get(key); - } - } - - private NsdServiceInfo getNsdService(int key) { - synchronized (mMapLock) { - return mServiceMap.get(key); - } - } - private void removeListener(int key) { - if (key == INVALID_LISTENER_KEY) return; synchronized (mMapLock) { mListenerMap.remove(key); mServiceMap.remove(key); @@ -463,16 +461,15 @@ public final class NsdManager { } private int getListenerKey(Object listener) { + checkListener(listener); synchronized (mMapLock) { int valueIndex = mListenerMap.indexOfValue(listener); - if (valueIndex != -1) { - return mListenerMap.keyAt(valueIndex); - } + checkArgument(valueIndex != -1, "listener not registered"); + return mListenerMap.keyAt(valueIndex); } - return INVALID_LISTENER_KEY; } - private String getNsdServiceInfoType(NsdServiceInfo s) { + private static String getNsdServiceInfoType(NsdServiceInfo s) { if (s == null) return "?"; return s.getServiceType(); } @@ -482,7 +479,9 @@ public final class NsdManager { */ private void init() { final Messenger messenger = getMessenger(); - if (messenger == null) throw new RuntimeException("Failed to initialize"); + if (messenger == null) { + fatal("Failed to obtain service Messenger"); + } HandlerThread t = new HandlerThread("NsdManager"); t.start(); mHandler = new ServiceHandler(t.getLooper()); @@ -490,10 +489,15 @@ public final class NsdManager { try { mConnected.await(); } catch (InterruptedException e) { - Log.e(TAG, "interrupted wait at init"); + fatal("Interrupted wait at init"); } } + private static void fatal(String msg) { + Log.e(TAG, msg); + throw new RuntimeException(msg); + } + /** * Register a service to be discovered by other services. * @@ -513,23 +517,10 @@ public final class NsdManager { */ public void registerService(NsdServiceInfo serviceInfo, int protocolType, RegistrationListener listener) { - if (TextUtils.isEmpty(serviceInfo.getServiceName()) || - TextUtils.isEmpty(serviceInfo.getServiceType())) { - throw new IllegalArgumentException("Service name or type cannot be empty"); - } - if (serviceInfo.getPort() <= 0) { - throw new IllegalArgumentException("Invalid port number"); - } - if (listener == null) { - throw new IllegalArgumentException("listener cannot be null"); - } - if (protocolType != PROTOCOL_DNS_SD) { - throw new IllegalArgumentException("Unsupported protocol"); - } + checkArgument(serviceInfo.getPort() > 0, "Invalid port number"); + checkServiceInfo(serviceInfo); + checkProtocol(protocolType); int key = putListener(listener, serviceInfo); - if (key == BUSY_LISTENER_KEY) { - throw new IllegalArgumentException("listener already in use"); - } mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo); } @@ -548,12 +539,6 @@ public final class NsdManager { */ public void unregisterService(RegistrationListener listener) { int id = getListenerKey(listener); - if (id == INVALID_LISTENER_KEY) { - throw new IllegalArgumentException("listener not registered"); - } - if (listener == null) { - throw new IllegalArgumentException("listener cannot be null"); - } mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id); } @@ -586,25 +571,13 @@ public final class NsdManager { * Cannot be null. Cannot be in use for an active service discovery. */ public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener cannot be null"); - } - if (TextUtils.isEmpty(serviceType)) { - throw new IllegalArgumentException("Service type cannot be empty"); - } - - if (protocolType != PROTOCOL_DNS_SD) { - throw new IllegalArgumentException("Unsupported protocol"); - } + checkStringNotEmpty(serviceType, "Service type cannot be empty"); + checkProtocol(protocolType); NsdServiceInfo s = new NsdServiceInfo(); s.setServiceType(serviceType); int key = putListener(listener, s); - if (key == BUSY_LISTENER_KEY) { - throw new IllegalArgumentException("listener already in use"); - } - mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s); } @@ -626,12 +599,6 @@ public final class NsdManager { */ public void stopServiceDiscovery(DiscoveryListener listener) { int id = getListenerKey(listener); - if (id == INVALID_LISTENER_KEY) { - throw new IllegalArgumentException("service discovery not active on listener"); - } - if (listener == null) { - throw new IllegalArgumentException("listener cannot be null"); - } mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id); } @@ -645,19 +612,8 @@ public final class NsdManager { * Cannot be in use for an active service resolution. */ public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { - if (TextUtils.isEmpty(serviceInfo.getServiceName()) || - TextUtils.isEmpty(serviceInfo.getServiceType())) { - throw new IllegalArgumentException("Service name or type cannot be empty"); - } - if (listener == null) { - throw new IllegalArgumentException("listener cannot be null"); - } - + checkServiceInfo(serviceInfo); int key = putListener(listener, serviceInfo); - - if (key == BUSY_LISTENER_KEY) { - throw new IllegalArgumentException("listener already in use"); - } mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo); } @@ -671,10 +627,10 @@ public final class NsdManager { } /** - * Get a reference to NetworkService handler. This is used to establish + * Get a reference to NsdService handler. This is used to establish * an AsyncChannel communication with the service * - * @return Messenger pointing to the NetworkService handler + * @return Messenger pointing to the NsdService handler */ private Messenger getMessenger() { try { @@ -683,4 +639,18 @@ public final class NsdManager { throw e.rethrowFromSystemServer(); } } + + private static void checkListener(Object listener) { + checkNotNull(listener, "listener cannot be null"); + } + + private static void checkProtocol(int protocolType) { + checkArgument(protocolType == PROTOCOL_DNS_SD, "Unsupported protocol"); + } + + private static void checkServiceInfo(NsdServiceInfo serviceInfo) { + checkNotNull(serviceInfo, "NsdServiceInfo cannot be null"); + checkStringNotEmpty(serviceInfo.getServiceName(),"Service name cannot be empty"); + checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty"); + } } From b1f97acf195516e2105dcc026e95dd309202f02a Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 11 Aug 2017 15:04:12 -0600 Subject: [PATCH 5/9] Rename API to match StorageStats. Since they're both measuring app code (APKs), name this API consistently with StorageStats.getAppBytes(). Bug: 64331226 Test: builds, boots Change-Id: I1b00427b619a78c043b1b5fac2d0e6406b51d454 --- core/java/android/net/TrafficStats.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 19857076a0..c339856f43 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -110,12 +110,12 @@ public class TrafficStats { public static final int TAG_SYSTEM_RESTORE = 0xFFFFFF04; /** - * Default tag value for code or resources downloaded by an app store on - * behalf of the app, such as app updates. + * Default tag value for code (typically APKs) downloaded by an app store on + * behalf of the app, such as updates. * * @hide */ - public static final int TAG_SYSTEM_CODE = 0xFFFFFF05; + public static final int TAG_SYSTEM_APP = 0xFFFFFF05; /** @hide */ public static final int TAG_SYSTEM_DHCP = 0xFFFFFF40; @@ -213,14 +213,14 @@ public class TrafficStats { /** * Set active tag to use when accounting {@link Socket} traffic originating * from the current thread. The tag used internally is well-defined to - * distinguish all code-related traffic, such as updates performed by an app - * store. + * distinguish all code (typically APKs) downloaded by an app store on + * behalf of the app, such as updates. * * @hide */ @SystemApi - public static void setThreadStatsTagCode() { - setThreadStatsTag(TAG_SYSTEM_CODE); + public static void setThreadStatsTagApp() { + setThreadStatsTag(TAG_SYSTEM_APP); } /** From f31c942e890dac68c257c347538bac23c772393d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 17 Aug 2017 19:23:08 +0900 Subject: [PATCH 6/9] Add tether offload traffic to interface stats as well. Currently, we only count add tethering traffic to per-UID stats, but not to total data usage (i.e., dev and XT stats). This is correct for software tethering, because all software forwarded packets are already included in interface counters, but it is incorrect for hardware offload, because such packets do not increment interface counters. To fix this: 1. Add an argument to ITetheringStatsProvider#getTetherStats to indicate whether per-UID stats are requested. For clarity, define integer constants STATS_PER_IFACE and STATS_PER_UID to represent these operations. 2. Make NetdTetheringStatsProvider return stats only if per-UID stats are requested. (Otherwise tethering traffic would be double-counted). 3. Make OffloadController's stats provider return the same stats regardless of whether per-UID stats were requested or not. 4. Make NetworkStatsService add non-per-UID tethering stats to the dev and XT snapshots. The per-UID snapshots were already correctly adding in per-UID stats. Bug: 29337859 Bug: 32163131 Test: runtest frameworks-net Test: runtest frameworks-telephony Change-Id: I7a4d04ab47694d754874136179f8edad71099638 --- core/java/android/net/NetworkStats.java | 5 +++++ .../android/server/net/NetworkStatsService.java | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index be9e809738..171adc054b 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -82,6 +82,11 @@ public class NetworkStats implements Parcelable { /** {@link #roaming} value where roaming data is accounted. */ public static final int ROAMING_YES = 1; + /** Denotes a request for stats at the interface level. */ + public static final int STATS_PER_IFACE = 0; + /** Denotes a request for stats at the interface and UID level. */ + public static final int STATS_PER_UID = 1; + // TODO: move fields to "mVariable" notation /** diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 8209adeb89..b14aa13bfb 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -29,6 +29,8 @@ import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.SET_FOREGROUND; +import static android.net.NetworkStats.STATS_PER_IFACE; +import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkTemplate.buildTemplateMobileWildcard; @@ -1041,6 +1043,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats xtSnapshot = getNetworkStatsXt(); final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev(); + // Tethering snapshot for dev and xt stats. Counts per-interface data from tethering stats + // providers that isn't already counted by dev and XT stats. + final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE); + xtSnapshot.combineAllValues(tetherSnapshot); + devSnapshot.combineAllValues(tetherSnapshot); // For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic // can't be reattributed to responsible apps. @@ -1371,14 +1378,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL); // fold tethering stats and operations into uid snapshot - final NetworkStats tetherSnapshot = getNetworkStatsTethering(); + final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID); uidSnapshot.combineAllValues(tetherSnapshot); final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService( Context.TELEPHONY_SERVICE); // fold video calling data usage stats into uid snapshot - final NetworkStats vtStats = telephonyManager.getVtDataUsage(true); + final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID); if (vtStats != null) { uidSnapshot.combineAllValues(vtStats); } @@ -1397,7 +1404,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Context.TELEPHONY_SERVICE); // Merge video calling data usage into XT - final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(false); + final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(STATS_PER_IFACE); if (vtSnapshot != null) { xtSnapshot.combineAllValues(vtSnapshot); } @@ -1409,9 +1416,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Return snapshot of current tethering statistics. Will return empty * {@link NetworkStats} if any problems are encountered. */ - private NetworkStats getNetworkStatsTethering() throws RemoteException { + private NetworkStats getNetworkStatsTethering(int how) throws RemoteException { try { - return mNetworkManager.getNetworkStatsTethering(); + return mNetworkManager.getNetworkStatsTethering(how); } catch (IllegalStateException e) { Log.wtf(TAG, "problem reading network stats", e); return new NetworkStats(0L, 10); From 2f0f8f418ffb18cb9c933c25ff6f9152a72088d2 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 29 Aug 2017 15:32:13 -0600 Subject: [PATCH 7/9] Augment network stats based on SubscriptionPlan. When a carrier provides an "anchor" of data usage at a specific moment in time, augment the network statistics used by warning/limit thresholds and Settings UI. For example, if the OS measured 500MB of usage, but the carrier says only 400MB has been used, we "squish" down the OS measured usage to match that anchor. Callers using the hidden API will have their data augmented by default, and the public API offers a way to opt-into augmentation. Thorough testing to verify behavior. Test: bit FrameworksNetTests:android.net.,com.android.server.net. Test: cts-tradefed run commandAndExit cts-dev -m CtsUsageStatsTestCases -t android.app.usage.cts.NetworkUsageStatsTest Bug: 64534190 Change-Id: Id3d4d7625bbf04f57643e51dbf376e3fa0ea8eca --- core/java/android/app/usage/NetworkStats.java | 4 +- .../app/usage/NetworkStatsManager.java | 45 +++-- .../android/net/INetworkStatsService.aidl | 2 +- .../java/android/net/NetworkStatsHistory.java | 19 ++ core/java/android/net/NetworkTemplate.java | 4 + .../server/net/NetworkStatsCollection.java | 162 +++++++++++++---- .../server/net/NetworkStatsObservers.java | 14 +- .../server/net/NetworkStatsRecorder.java | 9 +- .../server/net/NetworkStatsService.java | 165 ++++++++++++------ 9 files changed, 305 insertions(+), 119 deletions(-) diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java index 3670b914ec..222e9a0e5e 100644 --- a/core/java/android/app/usage/NetworkStats.java +++ b/core/java/android/app/usage/NetworkStats.java @@ -97,12 +97,12 @@ public final class NetworkStats implements AutoCloseable { private NetworkStatsHistory.Entry mRecycledHistoryEntry = null; /** @hide */ - NetworkStats(Context context, NetworkTemplate template, long startTimestamp, + NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp, long endTimestamp) throws RemoteException, SecurityException { final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); // Open network stats session - mSession = statsService.openSessionForUsageStats(context.getOpPackageName()); + mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName()); mCloseGuard.open("close"); mTemplate = template; mStartTimeStamp = startTimestamp; diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index ef262e0460..853b00331a 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -24,15 +24,14 @@ import android.app.usage.NetworkStats.Bucket; import android.content.Context; import android.net.ConnectivityManager; import android.net.DataUsageRequest; +import android.net.INetworkStatsService; import android.net.NetworkIdentity; import android.net.NetworkTemplate; -import android.net.INetworkStatsService; import android.os.Binder; -import android.os.Build; -import android.os.Message; -import android.os.Messenger; import android.os.Handler; import android.os.Looper; +import android.os.Message; +import android.os.Messenger; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; @@ -79,7 +78,7 @@ import android.util.Log; * In addition to tethering usage, usage by removed users and apps, and usage by the system * is also included in the results for callers with one of these higher levels of access. *

- * NOTE: Prior to API level {@value Build.VERSION_CODES#N}, all calls to these APIs required + * NOTE: Prior to API level {@value android.os.Build.VERSION_CODES#N}, all calls to these APIs required * the above permission, even to access an app's own data usage, and carrier-privileged apps were * not included. */ @@ -96,6 +95,13 @@ public class NetworkStatsManager { private final Context mContext; private final INetworkStatsService mService; + /** @hide */ + public static final int FLAG_POLL_ON_OPEN = 1 << 0; + /** @hide */ + public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 1; + + private int mFlags; + /** * {@hide} */ @@ -103,6 +109,25 @@ public class NetworkStatsManager { mContext = context; mService = INetworkStatsService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)); + setPollOnOpen(true); + } + + /** @hide */ + public void setPollOnOpen(boolean pollOnOpen) { + if (pollOnOpen) { + mFlags |= FLAG_POLL_ON_OPEN; + } else { + mFlags &= ~FLAG_POLL_ON_OPEN; + } + } + + /** @hide */ + public void setAugmentWithSubscriptionPlan(boolean augmentWithSubscriptionPlan) { + if (augmentWithSubscriptionPlan) { + mFlags |= FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN; + } else { + mFlags &= ~FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN; + } } /** @@ -136,7 +161,7 @@ public class NetworkStatsManager { } Bucket bucket = null; - NetworkStats stats = new NetworkStats(mContext, template, startTime, endTime); + NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime); bucket = stats.getDeviceSummaryForNetwork(); stats.close(); @@ -174,7 +199,7 @@ public class NetworkStatsManager { } NetworkStats stats; - stats = new NetworkStats(mContext, template, startTime, endTime); + stats = new NetworkStats(mContext, template, mFlags, startTime, endTime); stats.startSummaryEnumeration(); stats.close(); @@ -211,7 +236,7 @@ public class NetworkStatsManager { } NetworkStats result; - result = new NetworkStats(mContext, template, startTime, endTime); + result = new NetworkStats(mContext, template, mFlags, startTime, endTime); result.startSummaryEnumeration(); return result; @@ -260,7 +285,7 @@ public class NetworkStatsManager { NetworkStats result; try { - result = new NetworkStats(mContext, template, startTime, endTime); + result = new NetworkStats(mContext, template, mFlags, startTime, endTime); result.startHistoryEnumeration(uid, tag); } catch (RemoteException e) { Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e); @@ -305,7 +330,7 @@ public class NetworkStatsManager { } NetworkStats result; - result = new NetworkStats(mContext, template, startTime, endTime); + result = new NetworkStats(mContext, template, mFlags, startTime, endTime); result.startUserUidEnumeration(); return result; } diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index e693009c33..91801127fd 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -36,7 +36,7 @@ interface INetworkStatsService { * PACKAGE_USAGE_STATS permission is always checked. If PACKAGE_USAGE_STATS is not granted * READ_NETWORK_USAGE_STATS is checked for. */ - INetworkStatsSession openSessionForUsageStats(String callingPackage); + INetworkStatsSession openSessionForUsageStats(int flags, String callingPackage); /** Return network layer usage total for traffic that matches template. */ long getNetworkTotalBytes(in NetworkTemplate template, long start, long end); diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index 5f521de63c..433f9410cc 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -27,6 +27,7 @@ import static android.net.NetworkStatsHistory.Entry.UNKNOWN; import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray; import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray; import static android.text.format.DateUtils.SECOND_IN_MILLIS; + import static com.android.internal.util.ArrayUtils.total; import android.os.Parcel; @@ -282,6 +283,24 @@ public class NetworkStatsHistory implements Parcelable { return entry; } + public void setValues(int i, Entry entry) { + // Unwind old values + if (rxBytes != null) totalBytes -= rxBytes[i]; + if (txBytes != null) totalBytes -= txBytes[i]; + + bucketStart[i] = entry.bucketStart; + setLong(activeTime, i, entry.activeTime); + setLong(rxBytes, i, entry.rxBytes); + setLong(rxPackets, i, entry.rxPackets); + setLong(txBytes, i, entry.txBytes); + setLong(txPackets, i, entry.txPackets); + setLong(operations, i, entry.operations); + + // Apply new values + if (rxBytes != null) totalBytes += rxBytes[i]; + if (txBytes != null) totalBytes += txBytes[i]; + } + /** * Record that data traffic occurred in the given time range. Will * distribute across internal buckets, creating new buckets as needed. diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 0d2fcd0740..b307c5d6fc 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -326,6 +326,10 @@ public class NetworkTemplate implements Parcelable { } } + public boolean matchesSubscriberId(String subscriberId) { + return ArrayUtils.contains(mMatchSubscriberIds, subscriberId); + } + /** * Check if mobile network with matching IMSI. */ diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java index 03543007c8..8837c157ae 100644 --- a/services/core/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java @@ -28,6 +28,8 @@ import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_REMOVED; import static android.text.format.DateUtils.WEEK_IN_MILLIS; +import static com.android.server.net.NetworkStatsService.TAG; + import android.net.NetworkIdentity; import android.net.NetworkStats; import android.net.NetworkStatsHistory; @@ -37,20 +39,24 @@ import android.os.Binder; import android.service.NetworkStatsCollectionKeyProto; import android.service.NetworkStatsCollectionProto; import android.service.NetworkStatsCollectionStatsProto; +import android.telephony.SubscriptionPlan; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.IntArray; +import android.util.Pair; +import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; +import libcore.io.IoUtils; + import com.google.android.collect.Lists; import com.google.android.collect.Maps; -import libcore.io.IoUtils; - import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -60,9 +66,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.net.ProtocolException; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.Objects; /** @@ -140,6 +148,35 @@ public class NetworkStatsCollection implements FileRotator.Reader { return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE; } + @VisibleForTesting + public long roundUp(long time) { + if (time == Long.MIN_VALUE || time == Long.MAX_VALUE + || time == SubscriptionPlan.TIME_UNKNOWN) { + return time; + } else { + final long mod = time % mBucketDuration; + if (mod > 0) { + time -= mod; + time += mBucketDuration; + } + return time; + } + } + + @VisibleForTesting + public long roundDown(long time) { + if (time == Long.MIN_VALUE || time == Long.MAX_VALUE + || time == SubscriptionPlan.TIME_UNKNOWN) { + return time; + } else { + final long mod = time % mBucketDuration; + if (mod > 0) { + time -= mod; + } + return time; + } + } + public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) { return getRelevantUids(accessLevel, Binder.getCallingUid()); } @@ -165,60 +202,110 @@ public class NetworkStatsCollection implements FileRotator.Reader { * Combine all {@link NetworkStatsHistory} in this collection which match * the requested parameters. */ - public NetworkStatsHistory getHistory( - NetworkTemplate template, int uid, int set, int tag, int fields, - @NetworkStatsAccess.Level int accessLevel) { - return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE, - accessLevel); - } - - /** - * Combine all {@link NetworkStatsHistory} in this collection which match - * the requested parameters. - */ - public NetworkStatsHistory getHistory( - NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end, - @NetworkStatsAccess.Level int accessLevel) { - return getHistory(template, uid, set, tag, fields, start, end, accessLevel, - Binder.getCallingUid()); - } - - /** - * Combine all {@link NetworkStatsHistory} in this collection which match - * the requested parameters. - */ - public NetworkStatsHistory getHistory( - NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end, + public NetworkStatsHistory getHistory(NetworkTemplate template, SubscriptionPlan augmentPlan, + int uid, int set, int tag, int fields, long start, long end, @NetworkStatsAccess.Level int accessLevel, int callerUid) { if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) { throw new SecurityException("Network stats history of uid " + uid + " is forbidden for caller " + callerUid); } + final int bucketEstimate = (int) ((end - start) / mBucketDuration); final NetworkStatsHistory combined = new NetworkStatsHistory( - mBucketDuration, start == end ? 1 : estimateBuckets(), fields); + mBucketDuration, bucketEstimate, fields); // shortcut when we know stats will be empty if (start == end) return combined; + // Figure out the window of time that we should be augmenting (if any) + long augmentStart = SubscriptionPlan.TIME_UNKNOWN; + long augmentEnd = (augmentPlan != null) ? augmentPlan.getDataUsageTime() + : SubscriptionPlan.TIME_UNKNOWN; + // And if augmenting, we might need to collect more data to adjust with + long collectStart = start; + long collectEnd = end; + + if (augmentEnd != SubscriptionPlan.TIME_UNKNOWN) { + final Iterator> it = augmentPlan.cycleIterator(); + while (it.hasNext()) { + final Pair cycle = it.next(); + final long cycleStart = cycle.first.toInstant().toEpochMilli(); + final long cycleEnd = cycle.second.toInstant().toEpochMilli(); + if (cycleStart <= augmentEnd && augmentEnd < cycleEnd) { + augmentStart = cycleStart; + collectStart = Long.min(collectStart, augmentStart); + collectEnd = Long.max(collectEnd, augmentEnd); + break; + } + } + } + + if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) { + // Shrink augmentation window so we don't risk undercounting. + augmentStart = roundUp(augmentStart); + augmentEnd = roundDown(augmentEnd); + // Grow collection window so we get all the stats needed. + collectStart = roundDown(collectStart); + collectEnd = roundUp(collectEnd); + } + for (int i = 0; i < mStats.size(); i++) { final Key key = mStats.keyAt(i); if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag && templateMatches(template, key.ident)) { final NetworkStatsHistory value = mStats.valueAt(i); - combined.recordHistory(value, start, end); + combined.recordHistory(value, collectStart, collectEnd); } } - return combined; - } - /** - * Summarize all {@link NetworkStatsHistory} in this collection which match - * the requested parameters. - */ - public NetworkStats getSummary(NetworkTemplate template, long start, long end, - @NetworkStatsAccess.Level int accessLevel) { - return getSummary(template, start, end, accessLevel, Binder.getCallingUid()); + if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) { + final NetworkStatsHistory.Entry entry = combined.getValues( + augmentStart, augmentEnd, null); + + // If we don't have any recorded data for this time period, give + // ourselves something to scale with. + if (entry.rxBytes == 0 || entry.txBytes == 0) { + combined.recordData(augmentStart, augmentEnd, + new NetworkStats.Entry(1, 0, 1, 0, 0)); + combined.getValues(augmentStart, augmentEnd, entry); + } + + final long rawBytes = entry.rxBytes + entry.txBytes; + final long rawRxBytes = entry.rxBytes; + final long rawTxBytes = entry.txBytes; + final long targetBytes = augmentPlan.getDataUsageBytes(); + final long targetRxBytes = (rawRxBytes * targetBytes) / rawBytes; + final long targetTxBytes = (rawTxBytes * targetBytes) / rawBytes; + + // Scale all matching buckets to reach anchor target + final long beforeTotal = combined.getTotalBytes(); + for (int i = 0; i < combined.size(); i++) { + combined.getValues(i, entry); + if (entry.bucketStart >= augmentStart + && entry.bucketStart + entry.bucketDuration <= augmentEnd) { + entry.rxBytes = (entry.rxBytes * targetRxBytes) / rawRxBytes; + entry.txBytes = (entry.txBytes * targetTxBytes) / rawTxBytes; + // We purposefully clear out packet counters to indicate + // that this data has been augmented. + entry.rxPackets = 0; + entry.txPackets = 0; + combined.setValues(i, entry); + } + } + + final long deltaTotal = combined.getTotalBytes() - beforeTotal; + if (deltaTotal != 0) { + Slog.d(TAG, "Augmented network usage by " + deltaTotal + " bytes"); + } + + // Finally we can slice data as originally requested + final NetworkStatsHistory sliced = new NetworkStatsHistory( + mBucketDuration, bucketEstimate, fields); + sliced.recordHistory(combined, start, end); + return sliced; + } else { + return combined; + } } /** @@ -230,6 +317,7 @@ public class NetworkStatsCollection implements FileRotator.Reader { final long now = System.currentTimeMillis(); final NetworkStats stats = new NetworkStats(end - start, 24); + // shortcut when we know stats will be empty if (start == end) return stats; diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java index a256cbcf90..741c2062bd 100644 --- a/services/core/java/com/android/server/net/NetworkStatsObservers.java +++ b/services/core/java/com/android/server/net/NetworkStatsObservers.java @@ -17,28 +17,26 @@ package com.android.server.net; import static android.net.TrafficStats.MB_IN_BYTES; + import static com.android.internal.util.Preconditions.checkArgument; import android.app.usage.NetworkStatsManager; import android.net.DataUsageRequest; import android.net.NetworkStats; -import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; -import android.os.Binder; import android.os.Bundle; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; import android.os.Process; import android.os.RemoteException; import android.util.ArrayMap; -import android.util.IntArray; -import android.util.SparseArray; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.net.VpnInfo; @@ -410,7 +408,7 @@ class NetworkStatsObservers { */ private long getTotalBytesForNetworkUid(NetworkTemplate template, int uid) { try { - NetworkStatsHistory history = mCollection.getHistory(template, uid, + NetworkStatsHistory history = mCollection.getHistory(template, null, uid, NetworkStats.SET_ALL, NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL, Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */, diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java index 80309e19eb..4bee55ef8b 100644 --- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java @@ -20,6 +20,7 @@ import static android.net.NetworkStats.TAG_NONE; import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import static android.text.format.DateUtils.YEAR_IN_MILLIS; + import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.Nullable; @@ -28,6 +29,7 @@ import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; +import android.os.Binder; import android.os.DropBoxManager; import android.service.NetworkStatsRecorderProto; import android.util.Log; @@ -38,6 +40,9 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.net.VpnInfo; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; + +import libcore.io.IoUtils; + import com.google.android.collect.Sets; import java.io.ByteArrayOutputStream; @@ -52,8 +57,6 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Map; -import libcore.io.IoUtils; - /** * Logic to record deltas between periodic {@link NetworkStats} snapshots into * {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}. @@ -150,7 +153,7 @@ public class NetworkStatsRecorder { public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) { return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE, - NetworkStatsAccess.Level.DEVICE).getTotal(null); + NetworkStatsAccess.Level.DEVICE, Binder.getCallingUid()).getTotal(null); } public NetworkStatsCollection getSinceBoot() { diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index b14aa13bfb..4bd927d497 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -26,6 +26,8 @@ import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.isNetworkTypeMobile; import static android.net.NetworkStats.IFACE_ALL; +import static android.net.NetworkStats.METERED_ALL; +import static android.net.NetworkStats.ROAMING_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.SET_FOREGROUND; @@ -33,10 +35,12 @@ import static android.net.NetworkStats.STATS_PER_IFACE; import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; +import static android.net.NetworkStatsHistory.FIELD_ALL; import static android.net.NetworkTemplate.buildTemplateMobileWildcard; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; +import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED; import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE; import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES; @@ -65,6 +69,7 @@ import static com.android.server.NetworkManagementSocketTagger.setKernelCounterS import android.app.AlarmManager; import android.app.PendingIntent; +import android.app.usage.NetworkStatsManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -104,6 +109,8 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.service.NetworkInterfaceProto; import android.service.NetworkStatsServiceDumpProto; +import android.telephony.SubscriptionManager; +import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.text.format.DateUtils; import android.util.ArrayMap; @@ -139,8 +146,8 @@ import java.util.List; * other system services. */ public class NetworkStatsService extends INetworkStatsService.Stub { - private static final String TAG = "NetworkStats"; - private static final boolean LOGV = false; + static final String TAG = "NetworkStats"; + static final boolean LOGV = false; private static final int MSG_PERFORM_POLL = 1; private static final int MSG_UPDATE_IFACES = 2; @@ -194,6 +201,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public long getPollInterval(); public long getTimeCacheMaxAge(); public boolean getSampleEnabled(); + public boolean getAugmentEnabled(); public static class Config { public final long bucketDuration; @@ -466,18 +474,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public INetworkStatsSession openSession() { - return createSession(null, /* poll on create */ false); + // NOTE: if callers want to get non-augmented data, they should go + // through the public API + return openSessionInternal(NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, null); } @Override - public INetworkStatsSession openSessionForUsageStats(final String callingPackage) { - return createSession(callingPackage, /* poll on create */ true); + public INetworkStatsSession openSessionForUsageStats(int flags, String callingPackage) { + return openSessionInternal(flags, callingPackage); } - private INetworkStatsSession createSession(final String callingPackage, boolean pollOnCreate) { + private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) { assertBandwidthControlEnabled(); - if (pollOnCreate) { + if ((flags & NetworkStatsManager.FLAG_POLL_ON_OPEN) != 0) { final long ident = Binder.clearCallingIdentity(); try { performPoll(FLAG_PERSIST_ALL); @@ -490,9 +500,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // for its lifetime; when caller closes only weak references remain. return new INetworkStatsSession.Stub() { + private final int mCallingUid = Binder.getCallingUid(); + private final String mCallingPackage = callingPackage; + private final @NetworkStatsAccess.Level int mAccessLevel = checkAccessLevel( + callingPackage); + private NetworkStatsCollection mUidComplete; private NetworkStatsCollection mUidTagComplete; - private String mCallingPackage = callingPackage; private NetworkStatsCollection getUidComplete() { synchronized (mStatsLock) { @@ -514,55 +528,38 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public int[] getRelevantUids() { - return getUidComplete().getRelevantUids(checkAccessLevel(mCallingPackage)); + return getUidComplete().getRelevantUids(mAccessLevel); } @Override - public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start, - long end) { - @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); - if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) { - throw new SecurityException("Calling package " + mCallingPackage - + " cannot access device summary network stats"); - } - NetworkStats result = new NetworkStats(end - start, 1); - final long ident = Binder.clearCallingIdentity(); - try { - // Using access level higher than the one we checked for above. - // Reason is that we are combining usage data in a way that is not PII - // anymore. - result.combineAllValues( - internalGetSummaryForNetwork(template, start, end, - NetworkStatsAccess.Level.DEVICE)); - } finally { - Binder.restoreCallingIdentity(ident); - } - return result; + public NetworkStats getDeviceSummaryForNetwork( + NetworkTemplate template, long start, long end) { + return internalGetSummaryForNetwork(template, flags, start, end, mAccessLevel, + mCallingUid); } @Override public NetworkStats getSummaryForNetwork( NetworkTemplate template, long start, long end) { - @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); - return internalGetSummaryForNetwork(template, start, end, accessLevel); + return internalGetSummaryForNetwork(template, flags, start, end, mAccessLevel, + mCallingUid); } @Override public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) { - @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); - return internalGetHistoryForNetwork(template, fields, accessLevel); + return internalGetHistoryForNetwork(template, flags, fields, mAccessLevel, + mCallingUid); } @Override public NetworkStats getSummaryForAllUid( NetworkTemplate template, long start, long end, boolean includeTags) { try { - @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); - final NetworkStats stats = - getUidComplete().getSummary(template, start, end, accessLevel); + final NetworkStats stats = getUidComplete() + .getSummary(template, start, end, mAccessLevel, mCallingUid); if (includeTags) { final NetworkStats tagStats = getUidTagComplete() - .getSummary(template, start, end, accessLevel); + .getSummary(template, start, end, mAccessLevel, mCallingUid); stats.combineAllValues(tagStats); } return stats; @@ -576,13 +573,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStatsHistory getHistoryForUid( NetworkTemplate template, int uid, int set, int tag, int fields) { - @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); + // NOTE: We don't augment UID-level statistics if (tag == TAG_NONE) { - return getUidComplete().getHistory(template, uid, set, tag, fields, - accessLevel); + return getUidComplete().getHistory(template, null, uid, set, tag, fields, + Long.MIN_VALUE, Long.MAX_VALUE, mAccessLevel, mCallingUid); } else { - return getUidTagComplete().getHistory(template, uid, set, tag, fields, - accessLevel); + return getUidTagComplete().getHistory(template, null, uid, set, tag, fields, + Long.MIN_VALUE, Long.MAX_VALUE, mAccessLevel, mCallingUid); } } @@ -590,13 +587,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsHistory getHistoryIntervalForUid( NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) { - @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); + // NOTE: We don't augment UID-level statistics if (tag == TAG_NONE) { - return getUidComplete().getHistory(template, uid, set, tag, fields, start, end, - accessLevel); + return getUidComplete().getHistory(template, null, uid, set, tag, fields, + start, end, mAccessLevel, mCallingUid); } else if (uid == Binder.getCallingUid()) { - return getUidTagComplete().getHistory(template, uid, set, tag, fields, - start, end, accessLevel); + return getUidTagComplete().getHistory(template, null, uid, set, tag, fields, + start, end, mAccessLevel, mCallingUid); } else { throw new SecurityException("Calling package " + mCallingPackage + " cannot access tag information from a different uid"); @@ -616,37 +613,85 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext, Binder.getCallingUid(), callingPackage); } + /** + * Find the most relevant {@link SubscriptionPlan} for the given + * {@link NetworkTemplate} and flags. This is typically used to augment + * local measurement results to match a known anchor from the carrier. + */ + private SubscriptionPlan resolveSubscriptionPlan(NetworkTemplate template, int flags) { + SubscriptionPlan plan = null; + if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0 + && (template.getMatchRule() == NetworkTemplate.MATCH_MOBILE_ALL) + && mSettings.getAugmentEnabled()) { + Slog.d(TAG, "Resolving plan for " + template); + final long token = Binder.clearCallingIdentity(); + try { + final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class); + final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + for (int subId : sm.getActiveSubscriptionIdList()) { + if (template.matchesSubscriberId(tm.getSubscriberId(subId))) { + Slog.d(TAG, "Found active matching subId " + subId); + final List plans = sm.getSubscriptionPlans(subId); + if (!plans.isEmpty()) { + plan = plans.get(0); + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + Slog.d(TAG, "Resolved to plan " + plan); + } + return plan; + } + /** * Return network summary, splicing between DEV and XT stats when * appropriate. */ - private NetworkStats internalGetSummaryForNetwork( - NetworkTemplate template, long start, long end, - @NetworkStatsAccess.Level int accessLevel) { + private NetworkStats internalGetSummaryForNetwork(NetworkTemplate template, int flags, + long start, long end, @NetworkStatsAccess.Level int accessLevel, int callingUid) { // We've been using pure XT stats long enough that we no longer need to // splice DEV and XT together. - return mXtStatsCached.getSummary(template, start, end, accessLevel); + final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL, + accessLevel, callingUid); + + final long now = System.currentTimeMillis(); + final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); + + final NetworkStats stats = new NetworkStats(end - start, 1); + stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, + ROAMING_ALL, entry.rxBytes, entry.rxPackets, entry.txBytes, entry.txPackets, + entry.operations)); + return stats; } /** * Return network history, splicing between DEV and XT stats when * appropriate. */ - private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields, - @NetworkStatsAccess.Level int accessLevel) { + private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, + int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) { // We've been using pure XT stats long enough that we no longer need to // splice DEV and XT together. - return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields, accessLevel); + return mXtStatsCached.getHistory(template, resolveSubscriptionPlan(template, flags), + UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE, + accessLevel, callingUid); } @Override public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) { - // Special case - since this is for internal use only, don't worry about a full access level - // check and just require the signature/privileged permission. + // Special case - since this is for internal use only, don't worry about + // a full access level check and just require the signature/privileged + // permission. mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); assertBandwidthControlEnabled(); - return internalGetSummaryForNetwork(template, start, end, NetworkStatsAccess.Level.DEVICE) - .getTotalBytes(); + + // NOTE: if callers want to get non-augmented data, they should go + // through the public API + return internalGetSummaryForNetwork(template, + NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, start, end, + NetworkStatsAccess.Level.DEVICE, Binder.getCallingUid()).getTotalBytes(); } @Override @@ -1530,6 +1575,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true); } @Override + public boolean getAugmentEnabled() { + return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true); + } + @Override public Config getDevConfig() { return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS), getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS), From 5f1befb42e60ec1ceb93e99a3bb2dc31adbdbc97 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 1 Sep 2017 11:27:13 -0600 Subject: [PATCH 8/9] Don't over-acquire NPMS locks. We only need to hold mNetworkPoliciesSecondLock when working with subscription plans; before this CL we could end up acquiring the two NPMS locks out of order, resulting in a deadlock. Also annotate objects in NSS that require mStatsLock to be held. Test: builds, boots Bug: 65268076 Change-Id: I06497564424316ef895dc8dceba72ae784781dc3 --- .../server/net/NetworkStatsService.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 4bd927d497..3af5265e6f 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -124,6 +124,7 @@ import android.util.SparseIntArray; import android.util.TrustedTime; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.net.VpnInfo; import com.android.internal.util.ArrayUtils; @@ -241,12 +242,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final DropBoxNonMonotonicObserver mNonMonotonicObserver = new DropBoxNonMonotonicObserver(); + @GuardedBy("mStatsLock") private NetworkStatsRecorder mDevRecorder; + @GuardedBy("mStatsLock") private NetworkStatsRecorder mXtRecorder; + @GuardedBy("mStatsLock") private NetworkStatsRecorder mUidRecorder; + @GuardedBy("mStatsLock") private NetworkStatsRecorder mUidTagRecorder; /** Cached {@link #mXtRecorder} stats. */ + @GuardedBy("mStatsLock") private NetworkStatsCollection mXtStatsCached; /** Current counter sets for each UID. */ @@ -328,15 +334,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return; } - // create data recorders along with historical rotators - mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false); - mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false); - mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false); - mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true); - - updatePersistThresholds(); - synchronized (mStatsLock) { + // create data recorders along with historical rotators + mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false); + mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false); + mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false); + mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true); + + updatePersistThresholdsLocked(); + // upgrade any legacy stats, migrating them to rotated files maybeUpgradeLegacyStatsLocked(); @@ -674,9 +680,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) { // We've been using pure XT stats long enough that we no longer need to // splice DEV and XT together. - return mXtStatsCached.getHistory(template, resolveSubscriptionPlan(template, flags), - UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE, - accessLevel, callingUid); + final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags); + synchronized (mStatsLock) { + return mXtStatsCached.getHistory(template, augmentPlan, + UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE, + accessLevel, callingUid); + } } @Override @@ -813,7 +822,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { synchronized (mStatsLock) { if (!mSystemReady) return; - updatePersistThresholds(); + updatePersistThresholdsLocked(); mDevRecorder.maybePersistLocked(currentTime); mXtRecorder.maybePersistLocked(currentTime); @@ -869,7 +878,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * reflect current {@link #mPersistThreshold} value. Always defers to * {@link Global} values when defined. */ - private void updatePersistThresholds() { + private void updatePersistThresholdsLocked() { mDevRecorder.setPersistThreshold(mSettings.getDevPersistBytes(mPersistThreshold)); mXtRecorder.setPersistThreshold(mSettings.getXtPersistBytes(mPersistThreshold)); mUidRecorder.setPersistThreshold(mSettings.getUidPersistBytes(mPersistThreshold)); @@ -1311,7 +1320,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { synchronized (mStatsLock) { if (args.length > 0 && "--proto".equals(args[0])) { // In this case ignore all other arguments. - dumpProto(fd); + dumpProtoLocked(fd); return; } @@ -1387,7 +1396,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private void dumpProto(FileDescriptor fd) { + private void dumpProtoLocked(FileDescriptor fd) { final ProtoOutputStream proto = new ProtoOutputStream(fd); // TODO Right now it writes all history. Should it limit to the "since-boot" log? From 121a1fcbb2f3649a829c09118d29fa643244fcce Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 1 Sep 2017 12:51:51 -0600 Subject: [PATCH 9/9] Gracefully handle integer overflows. Try sticking with integer-based math as much as possible for speed, but switch to double-based math if we detect that we'd end up causing an overflow. New tests to verify. Test: bit FrameworksNetTests:com.android.server.net.NetworkStatsCollectionTest Bug: 65257769 Change-Id: I1ae35599be134f81850c0a3d86928b057fba1eff --- .../server/net/NetworkStatsCollection.java | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java index 8837c157ae..4ceb592af0 100644 --- a/services/core/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java @@ -177,6 +177,34 @@ public class NetworkStatsCollection implements FileRotator.Reader { } } + /** + * Safely multiple a value by a rational. + *

+ * Internally it uses integer-based math whenever possible, but switches + * over to double-based math if values would overflow. + */ + @VisibleForTesting + public static long multiplySafe(long value, long num, long den) { + long x = value; + long y = num; + + // Logic shamelessly borrowed from Math.multiplyExact() + long r = x * y; + long ax = Math.abs(x); + long ay = Math.abs(y); + if (((ax | ay) >>> 31 != 0)) { + // Some bits greater than 2^31 that might cause overflow + // Check the result using the divide operator + // and check for the special case of Long.MIN_VALUE * -1 + if (((y != 0) && (r / y != x)) || + (x == Long.MIN_VALUE && y == -1)) { + // Use double math to avoid overflowing + return (long) (((double) num / den) * value); + } + } + return r / den; + } + public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) { return getRelevantUids(accessLevel, Binder.getCallingUid()); } @@ -274,8 +302,8 @@ public class NetworkStatsCollection implements FileRotator.Reader { final long rawRxBytes = entry.rxBytes; final long rawTxBytes = entry.txBytes; final long targetBytes = augmentPlan.getDataUsageBytes(); - final long targetRxBytes = (rawRxBytes * targetBytes) / rawBytes; - final long targetTxBytes = (rawTxBytes * targetBytes) / rawBytes; + final long targetRxBytes = multiplySafe(targetBytes, rawRxBytes, rawBytes); + final long targetTxBytes = multiplySafe(targetBytes, rawTxBytes, rawBytes); // Scale all matching buckets to reach anchor target final long beforeTotal = combined.getTotalBytes(); @@ -283,8 +311,8 @@ public class NetworkStatsCollection implements FileRotator.Reader { combined.getValues(i, entry); if (entry.bucketStart >= augmentStart && entry.bucketStart + entry.bucketDuration <= augmentEnd) { - entry.rxBytes = (entry.rxBytes * targetRxBytes) / rawRxBytes; - entry.txBytes = (entry.txBytes * targetTxBytes) / rawTxBytes; + entry.rxBytes = multiplySafe(targetRxBytes, entry.rxBytes, rawRxBytes); + entry.txBytes = multiplySafe(targetTxBytes, entry.txBytes, rawTxBytes); // We purposefully clear out packet counters to indicate // that this data has been augmented. entry.rxPackets = 0;