[MS41.1] Prepare for APIs for querying usage with template

This CL includes:
1. Prepare for queryDetailsForDevice which allows callers
   to query history of networks that matches the given template.
2. Prepare for queryDetailsForUidTagState, which allows
   callers to query history uid stats with the given template,
   uid and other conditions.
3. Enforce fine location permission if the caller is querying
   data usage with a template which contains wifi network keys.

Test: atest NetworkStatsServiceTest NetworkStatsManagerTest
Bug: 204830222
Bug: 200768422
Change-Id: I6783d6bfd6e075e0b3ec8a3c91836377f1d71c7a
This commit is contained in:
Junyu Lai
2022-01-05 13:45:29 +00:00
parent 589a43475c
commit bb459aeaba
4 changed files with 153 additions and 15 deletions

View File

@@ -556,7 +556,7 @@ public final class NetworkStats implements AutoCloseable {
/** /**
* Collects history results for uid and resets history enumeration index. * 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; mHistory = null;
try { try {
mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid, mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
@@ -570,6 +570,20 @@ public final class NetworkStats implements AutoCloseable {
mEnumerationIndex = 0; 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. * Starts uid enumeration for current user.
* @throws RemoteException * @throws RemoteException

View File

@@ -433,6 +433,42 @@ public class NetworkStatsManager {
return null; // To make the compiler happy. 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.
* <p>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. * 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. * 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 * @param endTime End of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}. * {@link java.lang.System#currentTimeMillis}.
* @param uid UID of app * @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 * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
* traffic from all states. * traffic from all states.
* @return Statistics object or null if an error happened during statistics collection. * @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); 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}.
* <p>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 { long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
NetworkStats result;
try { try {
result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); final NetworkStats result = new NetworkStats(
result.startHistoryEnumeration(uid, tag, state); mContext, template, mFlags, startTime, endTime, mService);
result.startHistoryUidEnumeration(uid, tag, state);
return result;
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag
+ " state=" + state, e); + " state=" + state, e);
return null; e.rethrowFromSystemServer();
} }
return result; return null; // To make the compiler happy.
} }
/** /**

View File

@@ -32,6 +32,11 @@ interface INetworkStatsSession {
/** Return historical network layer stats for traffic that matches template. */ /** Return historical network layer stats for traffic that matches template. */
@UnsupportedAppUsage @UnsupportedAppUsage
NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields); 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. * Return network layer usage summary per UID for traffic that matches template.

View File

@@ -154,6 +154,7 @@ import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.BestClock; import com.android.net.module.util.BestClock;
import com.android.net.module.util.BinderUtils; import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.CollectionUtils; 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.NetworkStatsUtils;
import com.android.net.module.util.PermissionUtils; import com.android.net.module.util.PermissionUtils;
@@ -365,6 +366,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@NonNull @NonNull
private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor; private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
@NonNull
private final LocationPermissionChecker mLocationPermissionChecker;
private static @NonNull File getDefaultSystemDir() { private static @NonNull File getDefaultSystemDir() {
return new File(Environment.getDataDirectory(), "system"); return new File(Environment.getDataDirectory(), "system");
} }
@@ -461,6 +465,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mContentResolver = mContext.getContentResolver(); mContentResolver = mContext.getContentResolver();
mContentObserver = mDeps.makeContentObserver(mHandler, mSettings, mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
mNetworkStatsSubscriptionsMonitor); 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 @Override
public NetworkStats getDeviceSummaryForNetwork( public NetworkStats getDeviceSummaryForNetwork(
NetworkTemplate template, long start, long end) { NetworkTemplate template, long start, long end) {
enforceTemplatePermissions(template, callingPackage);
return internalGetSummaryForNetwork(template, restrictedFlags, start, end, return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
mAccessLevel, mCallingUid); mAccessLevel, mCallingUid);
} }
@@ -780,19 +793,33 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public NetworkStats getSummaryForNetwork( public NetworkStats getSummaryForNetwork(
NetworkTemplate template, long start, long end) { NetworkTemplate template, long start, long end) {
enforceTemplatePermissions(template, callingPackage);
return internalGetSummaryForNetwork(template, restrictedFlags, start, end, return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
mAccessLevel, mCallingUid); mAccessLevel, mCallingUid);
} }
// TODO: Remove this after all callers are removed.
@Override @Override
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) { public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
enforceTemplatePermissions(template, callingPackage);
return internalGetHistoryForNetwork(template, restrictedFlags, fields, 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 @Override
public NetworkStats getSummaryForAllUid( public NetworkStats getSummaryForAllUid(
NetworkTemplate template, long start, long end, boolean includeTags) { NetworkTemplate template, long start, long end, boolean includeTags) {
enforceTemplatePermissions(template, callingPackage);
try { try {
final NetworkStats stats = getUidComplete() final NetworkStats stats = getUidComplete()
.getSummary(template, start, end, mAccessLevel, mCallingUid); .getSummary(template, start, end, mAccessLevel, mCallingUid);
@@ -810,6 +837,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public NetworkStats getTaggedSummaryForAllUid( public NetworkStats getTaggedSummaryForAllUid(
NetworkTemplate template, long start, long end) { NetworkTemplate template, long start, long end) {
enforceTemplatePermissions(template, callingPackage);
try { try {
final NetworkStats tagStats = getUidTagComplete() final NetworkStats tagStats = getUidTagComplete()
.getSummary(template, start, end, mAccessLevel, mCallingUid); .getSummary(template, start, end, mAccessLevel, mCallingUid);
@@ -822,6 +850,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public NetworkStatsHistory getHistoryForUid( public NetworkStatsHistory getHistoryForUid(
NetworkTemplate template, int uid, int set, int tag, int fields) { NetworkTemplate template, int uid, int set, int tag, int fields) {
enforceTemplatePermissions(template, callingPackage);
// NOTE: We don't augment UID-level statistics // NOTE: We don't augment UID-level statistics
if (tag == TAG_NONE) { if (tag == TAG_NONE) {
return getUidComplete().getHistory(template, null, uid, set, tag, fields, return getUidComplete().getHistory(template, null, uid, set, tag, fields,
@@ -836,6 +865,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public NetworkStatsHistory getHistoryIntervalForUid( public NetworkStatsHistory getHistoryIntervalForUid(
NetworkTemplate template, int uid, int set, int tag, int fields, NetworkTemplate template, int uid, int set, int tag, int fields,
long start, long end) { 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 // NOTE: We don't augment UID-level statistics
if (tag == TAG_NONE) { if (tag == TAG_NONE) {
return getUidComplete().getHistory(template, null, uid, set, tag, fields, 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) { private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
return NetworkStatsAccess.checkAccessLevel( return NetworkStatsAccess.checkAccessLevel(
mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage); 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 // We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together. // splice DEV and XT together.
final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL, final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL,
accessLevel, callingUid); accessLevel, callingUid, start, end);
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
@@ -910,14 +962,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* appropriate. * appropriate.
*/ */
private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, 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 // We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together. // splice DEV and XT together.
final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags); final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags);
synchronized (mStatsLock) { synchronized (mStatsLock) {
return mXtStatsCached.getHistory(template, augmentPlan, return mXtStatsCached.getHistory(template, augmentPlan,
UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE, UID_ALL, SET_ALL, TAG_NONE, fields, start, end, accessLevel, callingUid);
accessLevel, callingUid);
} }
} }