[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.
*/
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

View File

@@ -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.
* <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.
* 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}.
* <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 {
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.
}
/**

View File

@@ -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.

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.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);
}
}