1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
/*
* 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 android.net;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStats.UID_ALL;
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.DevicePolicyManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Utility methods for controlling access to network stats APIs.
*
* @hide
*/
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.DEVICESUMMARY,
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>Profile owners.
* </ul>
*/
int USER = 1;
/**
* Access level for apps which can access usage summary of device. Device summary includes
* usage by apps running in any profiles/users, however this access level does not
* allow querying usage of individual apps running in other profiles/users.
*
* <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.
* </ul>
*/
int DEVICESUMMARY = 2;
/**
* 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 = 3;
}
/** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
public static @NetworkStatsAccess.Level int checkAccessLevel(
Context context, int callingPid, int callingUid, String callingPackage) {
final DevicePolicyManager mDpm = context.getSystemService(DevicePolicyManager.class);
final TelephonyManager tm = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
boolean hasCarrierPrivileges;
final long token = Binder.clearCallingIdentity();
try {
hasCarrierPrivileges = tm != null
&& tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
} finally {
Binder.restoreCallingIdentity(token);
}
final boolean isDeviceOwner = mDpm != null && mDpm.isDeviceOwnerApp(callingPackage);
final int appId = UserHandle.getAppId(callingUid);
final boolean isNetworkStack = context.checkPermission(
android.Manifest.permission.NETWORK_STACK, callingPid, callingUid)
== PERMISSION_GRANTED;
if (hasCarrierPrivileges || isDeviceOwner
|| appId == Process.SYSTEM_UID || isNetworkStack) {
// Carrier-privileged apps and device owners, and the system (including the
// network stack) can access data usage for all apps on the device.
return NetworkStatsAccess.Level.DEVICE;
}
boolean hasAppOpsPermission = hasAppOpsPermission(context, callingUid, callingPackage);
if (hasAppOpsPermission || context.checkCallingOrSelfPermission(
READ_NETWORK_USAGE_HISTORY) == PackageManager.PERMISSION_GRANTED) {
return NetworkStatsAccess.Level.DEVICESUMMARY;
}
//TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
boolean isProfileOwner = mDpm != null && (mDpm.isProfileOwnerApp(callingPackage)
|| mDpm.isDeviceOwnerApp(callingPackage));
if (isProfileOwner) {
// 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) {
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
switch (accessLevel) {
case NetworkStatsAccess.Level.DEVICE:
// Device-level access - can access usage for any uid.
return true;
case NetworkStatsAccess.Level.DEVICESUMMARY:
// Can access usage for any app running in the same user, along
// with some special uids (system, removed, or tethering) and
// anonymized uids
return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
|| uid == UID_TETHERING || uid == UID_ALL
|| userId == callerUserId;
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
|| userId == callerUserId;
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.noteOp(AppOpsManager.OPSTR_GET_USAGE_STATS,
callingUid, callingPackage, null /* attributionTag */, null /* message */);
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;
}
}
|