Loading core/java/android/app/usage/NetworkStatsManager.java +14 −7 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkIdentity; import android.net.NetworkTemplate; import android.os.Build; import android.os.RemoteException; import android.util.Log; Loading @@ -29,10 +30,9 @@ import android.util.Log; * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details. * <p /> * Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and * Long.MAX_VALUE can be used to simulate open ended intervals). All queries (except * {@link #querySummaryForDevice}) collect only network usage of apps belonging to the same user * as the client. In addition tethering usage, usage by removed users and apps, and usage by system * is also included in the results. * Long.MAX_VALUE can be used to simulate open ended intervals). By default, apps can only obtain * data about themselves. See the below note for special cases in which apps can obtain data about * other applications. * <h3> * Summary queries * </h3> Loading @@ -51,13 +51,20 @@ import android.util.Log; * multiple buckets for a particular key but all Bucket's state is going to be * {@link NetworkStats.Bucket#STATE_ALL}. * <p /> * <b>NOTE:</b> This API requires the permission * <b>NOTE:</b> Accessing stats for apps other than the calling app requires the permission * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and * will not be granted to third-party apps. However, declaring the permission implies intention to * use the API and the user of the device can grant permission through the Settings application. * Profile owner apps are automatically granted permission to query data on the profile they manage * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps likewise get * access to usage data of the primary user. * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps and carrier- * privileged apps likewise get access to usage data for all users on the device. * <p /> * 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. * <p /> * <b>NOTE:</b> Prior to API level {@value 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. */ public class NetworkStatsManager { private final static String TAG = "NetworkStatsManager"; Loading services/core/java/com/android/server/net/NetworkStatsAccess.java 0 → 100644 +166 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.server.net; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_TETHERING; import android.Manifest; import android.annotation.IntDef; import android.app.AppOpsManager; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.content.Context; import android.content.pm.PackageManager; import android.os.UserHandle; import android.telephony.TelephonyManager; import com.android.server.LocalServices; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** Utility methods for controlling access to network stats APIs. */ public final class NetworkStatsAccess { private NetworkStatsAccess() {} /** * Represents an access level for the network usage history and statistics APIs. * * <p>Access levels are in increasing order; that is, it is reasonable to check access by * verifying that the caller's access level is at least the minimum required level. */ @IntDef({ Level.DEFAULT, Level.USER, Level.DEVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface Level { /** * Default, unprivileged access level. * * <p>Can only access usage for one's own UID. * * <p>Every app will have at least this access level. */ int DEFAULT = 0; /** * Access level for apps which can access usage for any app running in the same user. * * <p>Granted to: * <ul> * <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit * so it is not necessarily sufficient to declare this in the manifest. * <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission. * <li>Profile owners. * </ul> */ int USER = 1; /** * Access level for apps which can access usage for any app on the device, including apps * running on other users/profiles. * * <p>Granted to: * <ul> * <li>Device owners. * <li>Carrier-privileged applications. * <li>The system UID. * </ul> */ int DEVICE = 2; } /** Returns the {@link NetworkStatsAccess.Level} for the given caller. */ public static @NetworkStatsAccess.Level int checkAccessLevel( Context context, int callingUid, String callingPackage) { final DevicePolicyManagerInternal dpmi = LocalServices.getService( DevicePolicyManagerInternal.class); final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); boolean hasCarrierPrivileges = tm != null && tm.checkCarrierPrivilegesForPackage(callingPackage) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; boolean isDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); if (hasCarrierPrivileges || isDeviceOwner || UserHandle.getAppId(callingUid) == android.os.Process.SYSTEM_UID) { // Carrier-privileged apps and device owners, and the system can access data usage for // all apps on the device. return NetworkStatsAccess.Level.DEVICE; } boolean isProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (hasAppOpsPermission(context, callingUid, callingPackage) || isProfileOwner || context.checkCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY) == PackageManager.PERMISSION_GRANTED) { // Apps with the AppOps permission, profile owners, and apps with the privileged // permission can access data usage for all apps in this user/profile. return NetworkStatsAccess.Level.USER; } // Everyone else gets default access (only to their own UID). return NetworkStatsAccess.Level.DEFAULT; } /** * Returns whether the given caller should be able to access the given UID when the caller has * the given {@link NetworkStatsAccess.Level}. */ public static boolean isAccessibleToUser(int uid, int callerUid, @NetworkStatsAccess.Level int accessLevel) { switch (accessLevel) { case NetworkStatsAccess.Level.DEVICE: // Device-level access - can access usage for any uid. return true; case NetworkStatsAccess.Level.USER: // User-level access - can access usage for any app running in the same user, along // with some special uids (system, removed, or tethering). return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED || uid == UID_TETHERING || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid); case NetworkStatsAccess.Level.DEFAULT: default: // Default access level - can only access one's own usage. return uid == callerUid; } } private static boolean hasAppOpsPermission( Context context, int callingUid, String callingPackage) { if (callingPackage != null) { AppOpsManager appOps = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE); final int mode = appOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage); if (mode == AppOpsManager.MODE_DEFAULT) { // The default behavior here is to check if PackageManager has given the app // permission. final int permissionCheck = context.checkCallingPermission( Manifest.permission.PACKAGE_USAGE_STATS); return permissionCheck == PackageManager.PERMISSION_GRANTED; } return (mode == AppOpsManager.MODE_ALLOWED); } return false; } } services/core/java/com/android/server/net/NetworkStatsCollection.java +15 −20 Original line number Diff line number Diff line Loading @@ -22,24 +22,18 @@ import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_TETHERING; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; import android.net.ConnectivityManager; import android.net.NetworkIdentity; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; import android.os.Binder; import android.os.UserHandle; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.IntArray; import libcore.io.IoUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; Loading @@ -47,6 +41,8 @@ import com.android.internal.util.IndentingPrintWriter; 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; Loading Loading @@ -136,12 +132,12 @@ public class NetworkStatsCollection implements FileRotator.Reader { return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE; } public int[] getRelevantUids() { public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) { final int callerUid = Binder.getCallingUid(); IntArray uids = new IntArray(); for (int i = 0; i < mStats.size(); i++) { final Key key = mStats.keyAt(i); if (isAccessibleToUser(key.uid, callerUid)) { if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) { int j = uids.binarySearch(key.uid); if (j < 0) { Loading @@ -158,8 +154,10 @@ public class NetworkStatsCollection implements FileRotator.Reader { * the requested parameters. */ public NetworkStatsHistory getHistory( NetworkTemplate template, int uid, int set, int tag, int fields) { return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE); 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); } /** Loading @@ -167,9 +165,10 @@ public class NetworkStatsCollection implements FileRotator.Reader { * the requested parameters. */ public NetworkStatsHistory getHistory( NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) { NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end, @NetworkStatsAccess.Level int accessLevel) { final int callerUid = Binder.getCallingUid(); if (!isAccessibleToUser(uid, callerUid)) { if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) { throw new SecurityException("Network stats history of uid " + uid + " is forbidden for caller " + callerUid); } Loading @@ -195,7 +194,8 @@ public class NetworkStatsCollection implements FileRotator.Reader { * Summarize all {@link NetworkStatsHistory} in this collection which match * the requested parameters. */ public NetworkStats getSummary(NetworkTemplate template, long start, long end) { public NetworkStats getSummary(NetworkTemplate template, long start, long end, @NetworkStatsAccess.Level int accessLevel) { final long now = System.currentTimeMillis(); final NetworkStats stats = new NetworkStats(end - start, 24); Loading @@ -208,7 +208,8 @@ public class NetworkStatsCollection implements FileRotator.Reader { final int callerUid = Binder.getCallingUid(); for (int i = 0; i < mStats.size(); i++) { final Key key = mStats.keyAt(i); if (templateMatches(template, key.ident) && isAccessibleToUser(key.uid, callerUid) if (templateMatches(template, key.ident) && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel) && key.set < NetworkStats.SET_DEBUG_START) { final NetworkStatsHistory value = mStats.valueAt(i); historyEntry = value.getValues(start, end, now, historyEntry); Loading Loading @@ -570,12 +571,6 @@ public class NetworkStatsCollection implements FileRotator.Reader { } } private static boolean isAccessibleToUser(int uid, int callerUid) { return UserHandle.getAppId(callerUid) == android.os.Process.SYSTEM_UID || uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED || uid == UID_TETHERING || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid); } /** * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity} * in the given {@link NetworkIdentitySet}. Loading services/core/java/com/android/server/net/NetworkStatsRecorder.java +2 −1 Original line number Diff line number Diff line Loading @@ -116,7 +116,8 @@ public class NetworkStatsRecorder { } public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) { return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null); return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE).getTotal(null); } /** Loading services/core/java/com/android/server/net/NetworkStatsService.java +38 −74 Original line number Diff line number Diff line Loading @@ -62,13 +62,9 @@ import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats; import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet; import android.Manifest; import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.IAlarmManager; import android.app.PendingIntent; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; Loading Loading @@ -97,9 +93,7 @@ import android.os.HandlerThread; import android.os.INetworkManagementService; import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; Loading @@ -122,7 +116,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.connectivity.Tethering; import java.io.File; Loading Loading @@ -484,18 +477,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public int[] getRelevantUids() { enforcePermissionForManagedAdmin(mCallingPackage); return getUidComplete().getRelevantUids(); return getUidComplete().getRelevantUids(checkAccessLevel(mCallingPackage)); } @Override public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start, long end) { enforcePermission(mCallingPackage); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); if (accessLevel < NetworkStatsAccess.Level.DEVICE) { throw new SecurityException("Calling package " + mCallingPackage + " cannot access device-level network stats"); } NetworkStats result = new NetworkStats(end - start, 1); final long ident = Binder.clearCallingIdentity(); try { result.combineAllValues(internalGetSummaryForNetwork(template, start, end)); result.combineAllValues( internalGetSummaryForNetwork(template, start, end, accessLevel)); } finally { Binder.restoreCallingIdentity(ident); } Loading @@ -505,23 +502,25 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getSummaryForNetwork( NetworkTemplate template, long start, long end) { enforcePermission(mCallingPackage); return internalGetSummaryForNetwork(template, start, end); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); return internalGetSummaryForNetwork(template, start, end, accessLevel); } @Override public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) { return internalGetHistoryForNetwork(template, fields); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); return internalGetHistoryForNetwork(template, fields, accessLevel); } @Override public NetworkStats getSummaryForAllUid( NetworkTemplate template, long start, long end, boolean includeTags) { enforcePermissionForManagedAdmin(mCallingPackage); final NetworkStats stats = getUidComplete().getSummary(template, start, end); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); final NetworkStats stats = getUidComplete().getSummary(template, start, end, accessLevel); if (includeTags) { final NetworkStats tagStats = getUidTagComplete() .getSummary(template, start, end); .getSummary(template, start, end, accessLevel); stats.combineAllValues(tagStats); } return stats; Loading @@ -530,11 +529,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStatsHistory getHistoryForUid( NetworkTemplate template, int uid, int set, int tag, int fields) { enforcePermissionForManagedAdmin(mCallingPackage); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); if (tag == TAG_NONE) { return getUidComplete().getHistory(template, uid, set, tag, fields); return getUidComplete().getHistory(template, uid, set, tag, fields, accessLevel); } else { return getUidTagComplete().getHistory(template, uid, set, tag, fields); return getUidTagComplete().getHistory(template, uid, set, tag, fields, accessLevel); } } Loading @@ -542,12 +543,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsHistory getHistoryIntervalForUid( NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) { enforcePermissionForManagedAdmin(mCallingPackage); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); if (tag == TAG_NONE) { return getUidComplete().getHistory(template, uid, set, tag, fields, start, end); return getUidComplete().getHistory(template, uid, set, tag, fields, start, end, accessLevel); } else { return getUidTagComplete().getHistory(template, uid, set, tag, fields, start, end); start, end, accessLevel); } } Loading @@ -559,80 +561,42 @@ public class NetworkStatsService extends INetworkStatsService.Stub { }; } private boolean hasAppOpsPermission(String callingPackage) { final int callingUid = Binder.getCallingUid(); boolean appOpsAllow = false; if (callingPackage != null) { AppOpsManager appOps = (AppOpsManager) mContext.getSystemService( Context.APP_OPS_SERVICE); final int mode = appOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage); if (mode == AppOpsManager.MODE_DEFAULT) { // The default behavior here is to check if PackageManager has given the app // permission. final int permissionCheck = mContext.checkCallingPermission( Manifest.permission.PACKAGE_USAGE_STATS); appOpsAllow = permissionCheck == PackageManager.PERMISSION_GRANTED; } appOpsAllow = (mode == AppOpsManager.MODE_ALLOWED); } return appOpsAllow; private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) { return NetworkStatsAccess.checkAccessLevel( mContext, Binder.getCallingUid(), callingPackage); } private void enforcePermissionForManagedAdmin(String callingPackage) { boolean hasPermission = hasAppOpsPermission(callingPackage); if (!hasPermission) { // Profile and device owners are exempt from permission checking. final int callingUid = Binder.getCallingUid(); final DevicePolicyManagerInternal dpmi = LocalServices.getService( DevicePolicyManagerInternal.class); // Device owners are also profile owners so it is enough to check for that. if (dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) { return; } } if (!hasPermission) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); } } private void enforcePermission(String callingPackage) { boolean appOpsAllow = hasAppOpsPermission(callingPackage); if (!appOpsAllow) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); } } /** * Return network summary, splicing between DEV and XT stats when * appropriate. */ private NetworkStats internalGetSummaryForNetwork( NetworkTemplate template, long start, long end) { NetworkTemplate template, long start, long end, @NetworkStatsAccess.Level int accessLevel) { // 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); return mXtStatsCached.getSummary(template, start, end, accessLevel); } /** * Return network history, splicing between DEV and XT stats when * appropriate. */ private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields) { private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields, @NetworkStatsAccess.Level int accessLevel) { // 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); return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields, accessLevel); } @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. mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); assertBandwidthControlEnabled(); return internalGetSummaryForNetwork(template, start, end).getTotalBytes(); return internalGetSummaryForNetwork(template, start, end, NetworkStatsAccess.Level.DEVICE) .getTotalBytes(); } @Override Loading Loading
core/java/android/app/usage/NetworkStatsManager.java +14 −7 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkIdentity; import android.net.NetworkTemplate; import android.os.Build; import android.os.RemoteException; import android.util.Log; Loading @@ -29,10 +30,9 @@ import android.util.Log; * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details. * <p /> * Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and * Long.MAX_VALUE can be used to simulate open ended intervals). All queries (except * {@link #querySummaryForDevice}) collect only network usage of apps belonging to the same user * as the client. In addition tethering usage, usage by removed users and apps, and usage by system * is also included in the results. * Long.MAX_VALUE can be used to simulate open ended intervals). By default, apps can only obtain * data about themselves. See the below note for special cases in which apps can obtain data about * other applications. * <h3> * Summary queries * </h3> Loading @@ -51,13 +51,20 @@ import android.util.Log; * multiple buckets for a particular key but all Bucket's state is going to be * {@link NetworkStats.Bucket#STATE_ALL}. * <p /> * <b>NOTE:</b> This API requires the permission * <b>NOTE:</b> Accessing stats for apps other than the calling app requires the permission * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and * will not be granted to third-party apps. However, declaring the permission implies intention to * use the API and the user of the device can grant permission through the Settings application. * Profile owner apps are automatically granted permission to query data on the profile they manage * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps likewise get * access to usage data of the primary user. * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps and carrier- * privileged apps likewise get access to usage data for all users on the device. * <p /> * 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. * <p /> * <b>NOTE:</b> Prior to API level {@value 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. */ public class NetworkStatsManager { private final static String TAG = "NetworkStatsManager"; Loading
services/core/java/com/android/server/net/NetworkStatsAccess.java 0 → 100644 +166 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.server.net; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_TETHERING; import android.Manifest; import android.annotation.IntDef; import android.app.AppOpsManager; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.content.Context; import android.content.pm.PackageManager; import android.os.UserHandle; import android.telephony.TelephonyManager; import com.android.server.LocalServices; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** Utility methods for controlling access to network stats APIs. */ public final class NetworkStatsAccess { private NetworkStatsAccess() {} /** * Represents an access level for the network usage history and statistics APIs. * * <p>Access levels are in increasing order; that is, it is reasonable to check access by * verifying that the caller's access level is at least the minimum required level. */ @IntDef({ Level.DEFAULT, Level.USER, Level.DEVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface Level { /** * Default, unprivileged access level. * * <p>Can only access usage for one's own UID. * * <p>Every app will have at least this access level. */ int DEFAULT = 0; /** * Access level for apps which can access usage for any app running in the same user. * * <p>Granted to: * <ul> * <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit * so it is not necessarily sufficient to declare this in the manifest. * <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission. * <li>Profile owners. * </ul> */ int USER = 1; /** * Access level for apps which can access usage for any app on the device, including apps * running on other users/profiles. * * <p>Granted to: * <ul> * <li>Device owners. * <li>Carrier-privileged applications. * <li>The system UID. * </ul> */ int DEVICE = 2; } /** Returns the {@link NetworkStatsAccess.Level} for the given caller. */ public static @NetworkStatsAccess.Level int checkAccessLevel( Context context, int callingUid, String callingPackage) { final DevicePolicyManagerInternal dpmi = LocalServices.getService( DevicePolicyManagerInternal.class); final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); boolean hasCarrierPrivileges = tm != null && tm.checkCarrierPrivilegesForPackage(callingPackage) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; boolean isDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); if (hasCarrierPrivileges || isDeviceOwner || UserHandle.getAppId(callingUid) == android.os.Process.SYSTEM_UID) { // Carrier-privileged apps and device owners, and the system can access data usage for // all apps on the device. return NetworkStatsAccess.Level.DEVICE; } boolean isProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (hasAppOpsPermission(context, callingUid, callingPackage) || isProfileOwner || context.checkCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY) == PackageManager.PERMISSION_GRANTED) { // Apps with the AppOps permission, profile owners, and apps with the privileged // permission can access data usage for all apps in this user/profile. return NetworkStatsAccess.Level.USER; } // Everyone else gets default access (only to their own UID). return NetworkStatsAccess.Level.DEFAULT; } /** * Returns whether the given caller should be able to access the given UID when the caller has * the given {@link NetworkStatsAccess.Level}. */ public static boolean isAccessibleToUser(int uid, int callerUid, @NetworkStatsAccess.Level int accessLevel) { switch (accessLevel) { case NetworkStatsAccess.Level.DEVICE: // Device-level access - can access usage for any uid. return true; case NetworkStatsAccess.Level.USER: // User-level access - can access usage for any app running in the same user, along // with some special uids (system, removed, or tethering). return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED || uid == UID_TETHERING || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid); case NetworkStatsAccess.Level.DEFAULT: default: // Default access level - can only access one's own usage. return uid == callerUid; } } private static boolean hasAppOpsPermission( Context context, int callingUid, String callingPackage) { if (callingPackage != null) { AppOpsManager appOps = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE); final int mode = appOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage); if (mode == AppOpsManager.MODE_DEFAULT) { // The default behavior here is to check if PackageManager has given the app // permission. final int permissionCheck = context.checkCallingPermission( Manifest.permission.PACKAGE_USAGE_STATS); return permissionCheck == PackageManager.PERMISSION_GRANTED; } return (mode == AppOpsManager.MODE_ALLOWED); } return false; } }
services/core/java/com/android/server/net/NetworkStatsCollection.java +15 −20 Original line number Diff line number Diff line Loading @@ -22,24 +22,18 @@ import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_TETHERING; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; import android.net.ConnectivityManager; import android.net.NetworkIdentity; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; import android.os.Binder; import android.os.UserHandle; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.IntArray; import libcore.io.IoUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; Loading @@ -47,6 +41,8 @@ import com.android.internal.util.IndentingPrintWriter; 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; Loading Loading @@ -136,12 +132,12 @@ public class NetworkStatsCollection implements FileRotator.Reader { return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE; } public int[] getRelevantUids() { public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) { final int callerUid = Binder.getCallingUid(); IntArray uids = new IntArray(); for (int i = 0; i < mStats.size(); i++) { final Key key = mStats.keyAt(i); if (isAccessibleToUser(key.uid, callerUid)) { if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) { int j = uids.binarySearch(key.uid); if (j < 0) { Loading @@ -158,8 +154,10 @@ public class NetworkStatsCollection implements FileRotator.Reader { * the requested parameters. */ public NetworkStatsHistory getHistory( NetworkTemplate template, int uid, int set, int tag, int fields) { return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE); 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); } /** Loading @@ -167,9 +165,10 @@ public class NetworkStatsCollection implements FileRotator.Reader { * the requested parameters. */ public NetworkStatsHistory getHistory( NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) { NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end, @NetworkStatsAccess.Level int accessLevel) { final int callerUid = Binder.getCallingUid(); if (!isAccessibleToUser(uid, callerUid)) { if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) { throw new SecurityException("Network stats history of uid " + uid + " is forbidden for caller " + callerUid); } Loading @@ -195,7 +194,8 @@ public class NetworkStatsCollection implements FileRotator.Reader { * Summarize all {@link NetworkStatsHistory} in this collection which match * the requested parameters. */ public NetworkStats getSummary(NetworkTemplate template, long start, long end) { public NetworkStats getSummary(NetworkTemplate template, long start, long end, @NetworkStatsAccess.Level int accessLevel) { final long now = System.currentTimeMillis(); final NetworkStats stats = new NetworkStats(end - start, 24); Loading @@ -208,7 +208,8 @@ public class NetworkStatsCollection implements FileRotator.Reader { final int callerUid = Binder.getCallingUid(); for (int i = 0; i < mStats.size(); i++) { final Key key = mStats.keyAt(i); if (templateMatches(template, key.ident) && isAccessibleToUser(key.uid, callerUid) if (templateMatches(template, key.ident) && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel) && key.set < NetworkStats.SET_DEBUG_START) { final NetworkStatsHistory value = mStats.valueAt(i); historyEntry = value.getValues(start, end, now, historyEntry); Loading Loading @@ -570,12 +571,6 @@ public class NetworkStatsCollection implements FileRotator.Reader { } } private static boolean isAccessibleToUser(int uid, int callerUid) { return UserHandle.getAppId(callerUid) == android.os.Process.SYSTEM_UID || uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED || uid == UID_TETHERING || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid); } /** * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity} * in the given {@link NetworkIdentitySet}. Loading
services/core/java/com/android/server/net/NetworkStatsRecorder.java +2 −1 Original line number Diff line number Diff line Loading @@ -116,7 +116,8 @@ public class NetworkStatsRecorder { } public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) { return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null); return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE).getTotal(null); } /** Loading
services/core/java/com/android/server/net/NetworkStatsService.java +38 −74 Original line number Diff line number Diff line Loading @@ -62,13 +62,9 @@ import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats; import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet; import android.Manifest; import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.IAlarmManager; import android.app.PendingIntent; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; Loading Loading @@ -97,9 +93,7 @@ import android.os.HandlerThread; import android.os.INetworkManagementService; import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; Loading @@ -122,7 +116,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.connectivity.Tethering; import java.io.File; Loading Loading @@ -484,18 +477,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public int[] getRelevantUids() { enforcePermissionForManagedAdmin(mCallingPackage); return getUidComplete().getRelevantUids(); return getUidComplete().getRelevantUids(checkAccessLevel(mCallingPackage)); } @Override public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start, long end) { enforcePermission(mCallingPackage); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); if (accessLevel < NetworkStatsAccess.Level.DEVICE) { throw new SecurityException("Calling package " + mCallingPackage + " cannot access device-level network stats"); } NetworkStats result = new NetworkStats(end - start, 1); final long ident = Binder.clearCallingIdentity(); try { result.combineAllValues(internalGetSummaryForNetwork(template, start, end)); result.combineAllValues( internalGetSummaryForNetwork(template, start, end, accessLevel)); } finally { Binder.restoreCallingIdentity(ident); } Loading @@ -505,23 +502,25 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getSummaryForNetwork( NetworkTemplate template, long start, long end) { enforcePermission(mCallingPackage); return internalGetSummaryForNetwork(template, start, end); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); return internalGetSummaryForNetwork(template, start, end, accessLevel); } @Override public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) { return internalGetHistoryForNetwork(template, fields); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); return internalGetHistoryForNetwork(template, fields, accessLevel); } @Override public NetworkStats getSummaryForAllUid( NetworkTemplate template, long start, long end, boolean includeTags) { enforcePermissionForManagedAdmin(mCallingPackage); final NetworkStats stats = getUidComplete().getSummary(template, start, end); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); final NetworkStats stats = getUidComplete().getSummary(template, start, end, accessLevel); if (includeTags) { final NetworkStats tagStats = getUidTagComplete() .getSummary(template, start, end); .getSummary(template, start, end, accessLevel); stats.combineAllValues(tagStats); } return stats; Loading @@ -530,11 +529,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStatsHistory getHistoryForUid( NetworkTemplate template, int uid, int set, int tag, int fields) { enforcePermissionForManagedAdmin(mCallingPackage); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); if (tag == TAG_NONE) { return getUidComplete().getHistory(template, uid, set, tag, fields); return getUidComplete().getHistory(template, uid, set, tag, fields, accessLevel); } else { return getUidTagComplete().getHistory(template, uid, set, tag, fields); return getUidTagComplete().getHistory(template, uid, set, tag, fields, accessLevel); } } Loading @@ -542,12 +543,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsHistory getHistoryIntervalForUid( NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) { enforcePermissionForManagedAdmin(mCallingPackage); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); if (tag == TAG_NONE) { return getUidComplete().getHistory(template, uid, set, tag, fields, start, end); return getUidComplete().getHistory(template, uid, set, tag, fields, start, end, accessLevel); } else { return getUidTagComplete().getHistory(template, uid, set, tag, fields, start, end); start, end, accessLevel); } } Loading @@ -559,80 +561,42 @@ public class NetworkStatsService extends INetworkStatsService.Stub { }; } private boolean hasAppOpsPermission(String callingPackage) { final int callingUid = Binder.getCallingUid(); boolean appOpsAllow = false; if (callingPackage != null) { AppOpsManager appOps = (AppOpsManager) mContext.getSystemService( Context.APP_OPS_SERVICE); final int mode = appOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage); if (mode == AppOpsManager.MODE_DEFAULT) { // The default behavior here is to check if PackageManager has given the app // permission. final int permissionCheck = mContext.checkCallingPermission( Manifest.permission.PACKAGE_USAGE_STATS); appOpsAllow = permissionCheck == PackageManager.PERMISSION_GRANTED; } appOpsAllow = (mode == AppOpsManager.MODE_ALLOWED); } return appOpsAllow; private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) { return NetworkStatsAccess.checkAccessLevel( mContext, Binder.getCallingUid(), callingPackage); } private void enforcePermissionForManagedAdmin(String callingPackage) { boolean hasPermission = hasAppOpsPermission(callingPackage); if (!hasPermission) { // Profile and device owners are exempt from permission checking. final int callingUid = Binder.getCallingUid(); final DevicePolicyManagerInternal dpmi = LocalServices.getService( DevicePolicyManagerInternal.class); // Device owners are also profile owners so it is enough to check for that. if (dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) { return; } } if (!hasPermission) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); } } private void enforcePermission(String callingPackage) { boolean appOpsAllow = hasAppOpsPermission(callingPackage); if (!appOpsAllow) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); } } /** * Return network summary, splicing between DEV and XT stats when * appropriate. */ private NetworkStats internalGetSummaryForNetwork( NetworkTemplate template, long start, long end) { NetworkTemplate template, long start, long end, @NetworkStatsAccess.Level int accessLevel) { // 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); return mXtStatsCached.getSummary(template, start, end, accessLevel); } /** * Return network history, splicing between DEV and XT stats when * appropriate. */ private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields) { private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields, @NetworkStatsAccess.Level int accessLevel) { // 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); return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields, accessLevel); } @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. mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); assertBandwidthControlEnabled(); return internalGetSummaryForNetwork(template, start, end).getTotalBytes(); return internalGetSummaryForNetwork(template, start, end, NetworkStatsAccess.Level.DEVICE) .getTotalBytes(); } @Override Loading