diff --git a/framework-t/src/android/app/usage/NetworkStats.java b/framework-t/src/android/app/usage/NetworkStats.java index d33666d744..2b6570a6ec 100644 --- a/framework-t/src/android/app/usage/NetworkStats.java +++ b/framework-t/src/android/app/usage/NetworkStats.java @@ -556,7 +556,7 @@ public final class NetworkStats implements AutoCloseable { /** * Collects history results for uid and resets history enumeration index. */ - void startHistoryEnumeration(int uid, int tag, int state) { + void startHistoryUidEnumeration(int uid, int tag, int state) { mHistory = null; try { mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid, @@ -570,6 +570,20 @@ public final class NetworkStats implements AutoCloseable { mEnumerationIndex = 0; } + /** + * Collects history results for network and resets history enumeration index. + */ + void startHistoryDeviceEnumeration() { + try { + mHistory = mSession.getHistoryIntervalForNetwork( + mTemplate, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp); + } catch (RemoteException e) { + Log.w(TAG, e); + mHistory = null; + } + mEnumerationIndex = 0; + } + /** * Starts uid enumeration for current user. * @throws RemoteException diff --git a/framework-t/src/android/app/usage/NetworkStatsManager.java b/framework-t/src/android/app/usage/NetworkStatsManager.java index d00de3679e..4b906c9089 100644 --- a/framework-t/src/android/app/usage/NetworkStatsManager.java +++ b/framework-t/src/android/app/usage/NetworkStatsManager.java @@ -433,6 +433,42 @@ public class NetworkStatsManager { return null; // To make the compiler happy. } + /** + * Query usage statistics details for networks matching a given {@link NetworkTemplate}. + * + * Result is not aggregated over time. This means buckets' start and + * end timestamps will be between 'startTime' and 'endTime' parameters. + *

Only includes buckets whose entire time period is included between + * startTime and endTime. Doesn't interpolate or return partial buckets. + * Since bucket length is in the order of hours, this + * method cannot be used to measure data usage on a fine grained time scale. + * This may take a long time, and apps should avoid calling this on their main thread. + * + * @param template Template used to match networks. See {@link NetworkTemplate}. + * @param startTime Start of period, in milliseconds since the Unix epoch, see + * {@link java.lang.System#currentTimeMillis}. + * @param endTime End of period, in milliseconds since the Unix epoch, see + * {@link java.lang.System#currentTimeMillis}. + * @return Statistics which is described above. + * @hide + */ + @NonNull + // @SystemApi(client = MODULE_LIBRARIES) + @WorkerThread + public NetworkStats queryDetailsForDevice(@NonNull NetworkTemplate template, + long startTime, long endTime) { + try { + final NetworkStats result = + new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); + result.startHistoryDeviceEnumeration(); + return result; + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + + return null; // To make the compiler happy. + } + /** * Query network usage statistics details for a given uid. * This may take a long time, and apps should avoid calling this on their main thread. @@ -499,7 +535,8 @@ public class NetworkStatsManager { * @param endTime End of period. Defined in terms of "Unix time", see * {@link java.lang.System#currentTimeMillis}. * @param uid UID of app - * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags. + * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data + * across all the tags. * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate * traffic from all states. * @return Statistics object or null if an error happened during statistics collection. @@ -514,21 +551,51 @@ public class NetworkStatsManager { return queryDetailsForUidTagState(template, startTime, endTime, uid, tag, state); } - /** @hide */ - public NetworkStats queryDetailsForUidTagState(NetworkTemplate template, + /** + * Query network usage statistics details for a given template, uid, tag, and state. + * + * Only usable for uids belonging to calling user. Result is not aggregated over time. + * This means buckets' start and end timestamps are going to be between 'startTime' and + * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag + * the same as the 'tag' parameter, and the state the same as the 'state' parameter. + * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, + * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and + * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}. + *

Only includes buckets that atomically occur in the inclusive time range. Doesn't + * interpolate across partial buckets. Since bucket length is in the order of hours, this + * method cannot be used to measure data usage on a fine grained time scale. + * This may take a long time, and apps should avoid calling this on their main thread. + * + * @param template Template used to match networks. See {@link NetworkTemplate}. + * @param startTime Start of period, in milliseconds since the Unix epoch, see + * {@link java.lang.System#currentTimeMillis}. + * @param endTime End of period, in milliseconds since the Unix epoch, see + * {@link java.lang.System#currentTimeMillis}. + * @param uid UID of app + * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data + * across all the tags. + * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate + * traffic from all states. + * @return Statistics which is described above. + * @hide + */ + @NonNull + // @SystemApi(client = MODULE_LIBRARIES) + @WorkerThread + public NetworkStats queryDetailsForUidTagState(@NonNull NetworkTemplate template, long startTime, long endTime, int uid, int tag, int state) throws SecurityException { - - NetworkStats result; try { - result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); - result.startHistoryEnumeration(uid, tag, state); + final NetworkStats result = new NetworkStats( + mContext, template, mFlags, startTime, endTime, mService); + result.startHistoryUidEnumeration(uid, tag, state); + return result; } catch (RemoteException e) { Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag + " state=" + state, e); - return null; + e.rethrowFromSystemServer(); } - return result; + return null; // To make the compiler happy. } /** diff --git a/framework-t/src/android/net/INetworkStatsSession.aidl b/framework-t/src/android/net/INetworkStatsSession.aidl index babe0bfb97..ab70be826f 100644 --- a/framework-t/src/android/net/INetworkStatsSession.aidl +++ b/framework-t/src/android/net/INetworkStatsSession.aidl @@ -32,6 +32,11 @@ interface INetworkStatsSession { /** Return historical network layer stats for traffic that matches template. */ @UnsupportedAppUsage NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields); + /** + * Return historical network layer stats for traffic that matches template, start and end + * timestamp. + */ + NetworkStatsHistory getHistoryIntervalForNetwork(in NetworkTemplate template, int fields, long start, long end); /** * Return network layer usage summary per UID for traffic that matches template. diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java index 0abc52309a..a0710f77b4 100644 --- a/service-t/src/com/android/server/net/NetworkStatsService.java +++ b/service-t/src/com/android/server/net/NetworkStatsService.java @@ -154,6 +154,7 @@ import com.android.net.module.util.BaseNetdUnsolicitedEventListener; import com.android.net.module.util.BestClock; import com.android.net.module.util.BinderUtils; import com.android.net.module.util.CollectionUtils; +import com.android.net.module.util.LocationPermissionChecker; import com.android.net.module.util.NetworkStatsUtils; import com.android.net.module.util.PermissionUtils; @@ -365,6 +366,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @NonNull private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor; + @NonNull + private final LocationPermissionChecker mLocationPermissionChecker; + private static @NonNull File getDefaultSystemDir() { return new File(Environment.getDataDirectory(), "system"); } @@ -461,6 +465,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContentResolver = mContext.getContentResolver(); mContentObserver = mDeps.makeContentObserver(mHandler, mSettings, mNetworkStatsSubscriptionsMonitor); + mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext); } /** @@ -508,6 +513,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } }; } + + /** + * @see LocationPermissionChecker + */ + public LocationPermissionChecker makeLocationPermissionChecker(final Context context) { + return new LocationPermissionChecker(context); + } } /** @@ -773,6 +785,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getDeviceSummaryForNetwork( NetworkTemplate template, long start, long end) { + enforceTemplatePermissions(template, callingPackage); return internalGetSummaryForNetwork(template, restrictedFlags, start, end, mAccessLevel, mCallingUid); } @@ -780,19 +793,33 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getSummaryForNetwork( NetworkTemplate template, long start, long end) { + enforceTemplatePermissions(template, callingPackage); return internalGetSummaryForNetwork(template, restrictedFlags, start, end, mAccessLevel, mCallingUid); } + // TODO: Remove this after all callers are removed. @Override public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) { + enforceTemplatePermissions(template, callingPackage); return internalGetHistoryForNetwork(template, restrictedFlags, fields, - mAccessLevel, mCallingUid); + mAccessLevel, mCallingUid, Long.MIN_VALUE, Long.MAX_VALUE); + } + + @Override + public NetworkStatsHistory getHistoryIntervalForNetwork(NetworkTemplate template, + int fields, long start, long end) { + enforceTemplatePermissions(template, callingPackage); + // TODO(b/200768422): Redact returned history if the template is location + // sensitive but the caller is not privileged. + return internalGetHistoryForNetwork(template, restrictedFlags, fields, + mAccessLevel, mCallingUid, start, end); } @Override public NetworkStats getSummaryForAllUid( NetworkTemplate template, long start, long end, boolean includeTags) { + enforceTemplatePermissions(template, callingPackage); try { final NetworkStats stats = getUidComplete() .getSummary(template, start, end, mAccessLevel, mCallingUid); @@ -810,6 +837,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getTaggedSummaryForAllUid( NetworkTemplate template, long start, long end) { + enforceTemplatePermissions(template, callingPackage); try { final NetworkStats tagStats = getUidTagComplete() .getSummary(template, start, end, mAccessLevel, mCallingUid); @@ -822,6 +850,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStatsHistory getHistoryForUid( NetworkTemplate template, int uid, int set, int tag, int fields) { + enforceTemplatePermissions(template, callingPackage); // NOTE: We don't augment UID-level statistics if (tag == TAG_NONE) { return getUidComplete().getHistory(template, null, uid, set, tag, fields, @@ -836,6 +865,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsHistory getHistoryIntervalForUid( NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) { + enforceTemplatePermissions(template, callingPackage); + // TODO(b/200768422): Redact returned history if the template is location + // sensitive but the caller is not privileged. // NOTE: We don't augment UID-level statistics if (tag == TAG_NONE) { return getUidComplete().getHistory(template, null, uid, set, tag, fields, @@ -857,6 +889,26 @@ public class NetworkStatsService extends INetworkStatsService.Stub { }; } + private void enforceTemplatePermissions(@NonNull NetworkTemplate template, + @NonNull String callingPackage) { + // For a template with wifi network keys, it is possible for a malicious + // client to track the user locations via querying data usage. Thus, enforce + // fine location permission check. + if (!template.getWifiNetworkKeys().isEmpty()) { + final boolean canAccessFineLocation = mLocationPermissionChecker + .checkCallersLocationPermission(callingPackage, + null /* featureId */, + Binder.getCallingUid(), + false /* coarseForTargetSdkLessThanQ */, + null /* message */); + if (!canAccessFineLocation) { + throw new SecurityException("Access fine location is required when querying" + + " with wifi network keys, make sure the app has the necessary" + + "permissions and the location toggle is on."); + } + } + } + private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) { return NetworkStatsAccess.checkAccessLevel( mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage); @@ -893,7 +945,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // We've been using pure XT stats long enough that we no longer need to // splice DEV and XT together. final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL, - accessLevel, callingUid); + accessLevel, callingUid, start, end); final long now = System.currentTimeMillis(); final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); @@ -910,14 +962,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * appropriate. */ private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, - int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) { + int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid, + long start, long end) { // We've been using pure XT stats long enough that we no longer need to // splice DEV and XT together. 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); + UID_ALL, SET_ALL, TAG_NONE, fields, start, end, accessLevel, callingUid); } }