Relax permissions around NetworkStatsManager APIs.
Currently, access to network usage history and statistics requires a signature|privileged permission, an AppOps bit (associated with the PACKAGE_USAGE_STATS permission), or device/profile ownership. Once access is granted via one of these mechanisms, it generally applies to any UID running in the same user as the caller. This CL expands access as follows: -Any app can access its own usage history with no extra requirements. -Carrier-privileged applications can access usage history for the entire device. -Device owners can access per-UID breakdowns for usage. Previously they could access the summary for the whole device, but not the individual breakdowns. We simplify the permission model by defining three access levels - DEFAULT (own app only), USER (all apps in the same user), and DEVICE (all apps on the device), and propagate these levels throughout. Finally, this CL fixes an apparent bug in NetworkStatsSerice#hasAppOpsPermissions - if the AppOp bit was in MODE_DEFAULT, hasAppOpsPermission would always return false instead of falling back to the PackageManager permission check. Bug: 25812859 Bug: 25813856 Change-Id: Ic96e0776e2a4215a400163872acea1ededfaced9
This commit is contained in:
@@ -21,6 +21,7 @@ import android.content.Context;
|
|||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkIdentity;
|
import android.net.NetworkIdentity;
|
||||||
import android.net.NetworkTemplate;
|
import android.net.NetworkTemplate;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -29,10 +30,9 @@ import android.util.Log;
|
|||||||
* discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
|
* discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
|
||||||
* <p />
|
* <p />
|
||||||
* Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and
|
* 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
|
* Long.MAX_VALUE can be used to simulate open ended intervals). By default, apps can only obtain
|
||||||
* {@link #querySummaryForDevice}) collect only network usage of apps belonging to the same user
|
* data about themselves. See the below note for special cases in which apps can obtain data about
|
||||||
* as the client. In addition tethering usage, usage by removed users and apps, and usage by system
|
* other applications.
|
||||||
* is also included in the results.
|
|
||||||
* <h3>
|
* <h3>
|
||||||
* Summary queries
|
* Summary queries
|
||||||
* </h3>
|
* </h3>
|
||||||
@@ -51,13 +51,20 @@ import android.util.Log;
|
|||||||
* multiple buckets for a particular key but all Bucket's state is going to be
|
* multiple buckets for a particular key but all Bucket's state is going to be
|
||||||
* {@link NetworkStats.Bucket#STATE_ALL}.
|
* {@link NetworkStats.Bucket#STATE_ALL}.
|
||||||
* <p />
|
* <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
|
* {@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
|
* 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.
|
* 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
|
* 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
|
* (that is, for any query except {@link #querySummaryForDevice}). Device owner apps and carrier-
|
||||||
* access to usage data of the primary user.
|
* 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 {
|
public class NetworkStatsManager {
|
||||||
private final static String TAG = "NetworkStatsManager";
|
private final static String TAG = "NetworkStatsManager";
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,24 +22,18 @@ import static android.net.NetworkStats.SET_DEFAULT;
|
|||||||
import static android.net.NetworkStats.TAG_NONE;
|
import static android.net.NetworkStats.TAG_NONE;
|
||||||
import static android.net.NetworkStats.UID_ALL;
|
import static android.net.NetworkStats.UID_ALL;
|
||||||
import static android.net.TrafficStats.UID_REMOVED;
|
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 static android.text.format.DateUtils.WEEK_IN_MILLIS;
|
||||||
|
|
||||||
import android.net.ConnectivityManager;
|
|
||||||
import android.net.NetworkIdentity;
|
import android.net.NetworkIdentity;
|
||||||
import android.net.NetworkStats;
|
import android.net.NetworkStats;
|
||||||
import android.net.NetworkStatsHistory;
|
import android.net.NetworkStatsHistory;
|
||||||
import android.net.NetworkTemplate;
|
import android.net.NetworkTemplate;
|
||||||
import android.net.TrafficStats;
|
import android.net.TrafficStats;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.AtomicFile;
|
import android.util.AtomicFile;
|
||||||
import android.util.IntArray;
|
import android.util.IntArray;
|
||||||
|
|
||||||
import libcore.io.IoUtils;
|
|
||||||
|
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.util.FileRotator;
|
import com.android.internal.util.FileRotator;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
@@ -47,6 +41,8 @@ import com.android.internal.util.IndentingPrintWriter;
|
|||||||
import com.google.android.collect.Lists;
|
import com.google.android.collect.Lists;
|
||||||
import com.google.android.collect.Maps;
|
import com.google.android.collect.Maps;
|
||||||
|
|
||||||
|
import libcore.io.IoUtils;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
@@ -136,12 +132,12 @@ public class NetworkStatsCollection implements FileRotator.Reader {
|
|||||||
return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
|
return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getRelevantUids() {
|
public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
|
||||||
final int callerUid = Binder.getCallingUid();
|
final int callerUid = Binder.getCallingUid();
|
||||||
IntArray uids = new IntArray();
|
IntArray uids = new IntArray();
|
||||||
for (int i = 0; i < mStats.size(); i++) {
|
for (int i = 0; i < mStats.size(); i++) {
|
||||||
final Key key = mStats.keyAt(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);
|
int j = uids.binarySearch(key.uid);
|
||||||
|
|
||||||
if (j < 0) {
|
if (j < 0) {
|
||||||
@@ -158,8 +154,10 @@ public class NetworkStatsCollection implements FileRotator.Reader {
|
|||||||
* the requested parameters.
|
* the requested parameters.
|
||||||
*/
|
*/
|
||||||
public NetworkStatsHistory getHistory(
|
public NetworkStatsHistory getHistory(
|
||||||
NetworkTemplate template, int uid, int set, int tag, int fields) {
|
NetworkTemplate template, int uid, int set, int tag, int fields,
|
||||||
return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE);
|
@NetworkStatsAccess.Level int accessLevel) {
|
||||||
|
return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE,
|
||||||
|
accessLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,9 +165,10 @@ public class NetworkStatsCollection implements FileRotator.Reader {
|
|||||||
* the requested parameters.
|
* the requested parameters.
|
||||||
*/
|
*/
|
||||||
public NetworkStatsHistory getHistory(
|
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();
|
final int callerUid = Binder.getCallingUid();
|
||||||
if (!isAccessibleToUser(uid, callerUid)) {
|
if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
|
||||||
throw new SecurityException("Network stats history of uid " + uid
|
throw new SecurityException("Network stats history of uid " + uid
|
||||||
+ " is forbidden for caller " + callerUid);
|
+ " is forbidden for caller " + callerUid);
|
||||||
}
|
}
|
||||||
@@ -195,7 +194,8 @@ public class NetworkStatsCollection implements FileRotator.Reader {
|
|||||||
* Summarize all {@link NetworkStatsHistory} in this collection which match
|
* Summarize all {@link NetworkStatsHistory} in this collection which match
|
||||||
* the requested parameters.
|
* 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 long now = System.currentTimeMillis();
|
||||||
|
|
||||||
final NetworkStats stats = new NetworkStats(end - start, 24);
|
final NetworkStats stats = new NetworkStats(end - start, 24);
|
||||||
@@ -208,7 +208,8 @@ public class NetworkStatsCollection implements FileRotator.Reader {
|
|||||||
final int callerUid = Binder.getCallingUid();
|
final int callerUid = Binder.getCallingUid();
|
||||||
for (int i = 0; i < mStats.size(); i++) {
|
for (int i = 0; i < mStats.size(); i++) {
|
||||||
final Key key = mStats.keyAt(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) {
|
&& key.set < NetworkStats.SET_DEBUG_START) {
|
||||||
final NetworkStatsHistory value = mStats.valueAt(i);
|
final NetworkStatsHistory value = mStats.valueAt(i);
|
||||||
historyEntry = value.getValues(start, end, now, historyEntry);
|
historyEntry = value.getValues(start, end, now, historyEntry);
|
||||||
@@ -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}
|
* Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
|
||||||
* in the given {@link NetworkIdentitySet}.
|
* in the given {@link NetworkIdentitySet}.
|
||||||
|
|||||||
@@ -116,7 +116,8 @@ public class NetworkStatsRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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.resetKernelUidStats;
|
||||||
import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
|
import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.AppOpsManager;
|
|
||||||
import android.app.IAlarmManager;
|
import android.app.IAlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.admin.DeviceAdminInfo;
|
|
||||||
import android.app.admin.DevicePolicyManagerInternal;
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -97,9 +93,7 @@ import android.os.HandlerThread;
|
|||||||
import android.os.INetworkManagementService;
|
import android.os.INetworkManagementService;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.Process;
|
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
@@ -122,7 +116,6 @@ import com.android.internal.util.ArrayUtils;
|
|||||||
import com.android.internal.util.FileRotator;
|
import com.android.internal.util.FileRotator;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
import com.android.server.EventLogTags;
|
import com.android.server.EventLogTags;
|
||||||
import com.android.server.LocalServices;
|
|
||||||
import com.android.server.connectivity.Tethering;
|
import com.android.server.connectivity.Tethering;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -484,18 +477,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getRelevantUids() {
|
public int[] getRelevantUids() {
|
||||||
enforcePermissionForManagedAdmin(mCallingPackage);
|
return getUidComplete().getRelevantUids(checkAccessLevel(mCallingPackage));
|
||||||
return getUidComplete().getRelevantUids();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start,
|
public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start,
|
||||||
long end) {
|
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);
|
NetworkStats result = new NetworkStats(end - start, 1);
|
||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
result.combineAllValues(internalGetSummaryForNetwork(template, start, end));
|
result.combineAllValues(
|
||||||
|
internalGetSummaryForNetwork(template, start, end, accessLevel));
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
Binder.restoreCallingIdentity(ident);
|
||||||
}
|
}
|
||||||
@@ -505,23 +502,25 @@ 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) {
|
||||||
enforcePermission(mCallingPackage);
|
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
|
||||||
return internalGetSummaryForNetwork(template, start, end);
|
return internalGetSummaryForNetwork(template, start, end, accessLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
|
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
|
||||||
return internalGetHistoryForNetwork(template, fields);
|
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
|
||||||
|
return internalGetHistoryForNetwork(template, fields, accessLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkStats getSummaryForAllUid(
|
public NetworkStats getSummaryForAllUid(
|
||||||
NetworkTemplate template, long start, long end, boolean includeTags) {
|
NetworkTemplate template, long start, long end, boolean includeTags) {
|
||||||
enforcePermissionForManagedAdmin(mCallingPackage);
|
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
|
||||||
final NetworkStats stats = getUidComplete().getSummary(template, start, end);
|
final NetworkStats stats =
|
||||||
|
getUidComplete().getSummary(template, start, end, accessLevel);
|
||||||
if (includeTags) {
|
if (includeTags) {
|
||||||
final NetworkStats tagStats = getUidTagComplete()
|
final NetworkStats tagStats = getUidTagComplete()
|
||||||
.getSummary(template, start, end);
|
.getSummary(template, start, end, accessLevel);
|
||||||
stats.combineAllValues(tagStats);
|
stats.combineAllValues(tagStats);
|
||||||
}
|
}
|
||||||
return stats;
|
return stats;
|
||||||
@@ -530,11 +529,13 @@ 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) {
|
||||||
enforcePermissionForManagedAdmin(mCallingPackage);
|
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
|
||||||
if (tag == TAG_NONE) {
|
if (tag == TAG_NONE) {
|
||||||
return getUidComplete().getHistory(template, uid, set, tag, fields);
|
return getUidComplete().getHistory(template, uid, set, tag, fields,
|
||||||
|
accessLevel);
|
||||||
} else {
|
} else {
|
||||||
return getUidTagComplete().getHistory(template, uid, set, tag, fields);
|
return getUidTagComplete().getHistory(template, uid, set, tag, fields,
|
||||||
|
accessLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,12 +543,13 @@ 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) {
|
||||||
enforcePermissionForManagedAdmin(mCallingPackage);
|
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
|
||||||
if (tag == TAG_NONE) {
|
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 {
|
} else {
|
||||||
return getUidTagComplete().getHistory(template, uid, set, tag, fields,
|
return getUidTagComplete().getHistory(template, uid, set, tag, fields,
|
||||||
start, end);
|
start, end, accessLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,80 +561,42 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasAppOpsPermission(String callingPackage) {
|
private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
|
||||||
final int callingUid = Binder.getCallingUid();
|
return NetworkStatsAccess.checkAccessLevel(
|
||||||
boolean appOpsAllow = false;
|
mContext, Binder.getCallingUid(), callingPackage);
|
||||||
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 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
|
* Return network summary, splicing between DEV and XT stats when
|
||||||
* appropriate.
|
* appropriate.
|
||||||
*/
|
*/
|
||||||
private NetworkStats internalGetSummaryForNetwork(
|
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
|
// 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.
|
||||||
return mXtStatsCached.getSummary(template, start, end);
|
return mXtStatsCached.getSummary(template, start, end, accessLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return network history, splicing between DEV and XT stats when
|
* Return network history, splicing between DEV and XT stats when
|
||||||
* appropriate.
|
* 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
|
// 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.
|
||||||
return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields);
|
return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields, accessLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
|
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);
|
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
|
||||||
assertBandwidthControlEnabled();
|
assertBandwidthControlEnabled();
|
||||||
return internalGetSummaryForNetwork(template, start, end).getTotalBytes();
|
return internalGetSummaryForNetwork(template, start, end, NetworkStatsAccess.Level.DEVICE)
|
||||||
|
.getTotalBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user