blob: 94c94a514decaf7df05842c7d09ce1014b2f8b50 [file] [log] [blame]
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.connectivity;
18
19import static android.Manifest.permission.CHANGE_NETWORK_STATE;
20import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
Hugo Benichi514da602016-07-19 15:59:27 +090021import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
Chalard Jean26aa91a2018-03-20 19:13:57 +090022import static android.Manifest.permission.NETWORK_STACK;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070023import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
24import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
25import static android.content.pm.PackageManager.GET_PERMISSIONS;
paulhu3b0f5ea2018-11-01 10:38:11 +080026import static android.os.Process.INVALID_UID;
27import static android.os.Process.SYSTEM_UID;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070028
paulhub6733802018-08-20 11:01:21 +080029import android.annotation.NonNull;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070030import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
Chalard Jean26aa91a2018-03-20 19:13:57 +090034import android.content.pm.ApplicationInfo;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070035import android.content.pm.PackageInfo;
36import android.content.pm.PackageManager;
37import android.content.pm.PackageManager.NameNotFoundException;
38import android.content.pm.UserInfo;
39import android.net.Uri;
paulhub6733802018-08-20 11:01:21 +080040import android.os.Build;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070041import android.os.INetworkManagementService;
42import android.os.RemoteException;
43import android.os.UserHandle;
44import android.os.UserManager;
45import android.text.TextUtils;
46import android.util.Log;
47
Chalard Jean26aa91a2018-03-20 19:13:57 +090048import com.android.internal.annotations.VisibleForTesting;
49
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070050import java.util.ArrayList;
51import java.util.HashMap;
52import java.util.HashSet;
53import java.util.List;
54import java.util.Map.Entry;
55import java.util.Map;
56import java.util.Set;
57
58/**
59 * A utility class to inform Netd of UID permisisons.
60 * Does a mass update at boot and then monitors for app install/remove.
61 *
62 * @hide
63 */
64public class PermissionMonitor {
65 private static final String TAG = "PermissionMonitor";
66 private static final boolean DBG = true;
zhangshuxiaob48db5b2016-02-03 21:28:25 +080067 private static final Boolean SYSTEM = Boolean.TRUE;
68 private static final Boolean NETWORK = Boolean.FALSE;
paulhu3b0f5ea2018-11-01 10:38:11 +080069 private static final int VERSION_Q = Build.VERSION_CODES.Q;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070070
71 private final Context mContext;
72 private final PackageManager mPackageManager;
73 private final UserManager mUserManager;
74 private final INetworkManagementService mNetd;
75 private final BroadcastReceiver mIntentReceiver;
76
77 // Values are User IDs.
Hugo Benichi514da602016-07-19 15:59:27 +090078 private final Set<Integer> mUsers = new HashSet<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070079
80 // Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
Hugo Benichi514da602016-07-19 15:59:27 +090081 private final Map<Integer, Boolean> mApps = new HashMap<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070082
83 public PermissionMonitor(Context context, INetworkManagementService netd) {
84 mContext = context;
85 mPackageManager = context.getPackageManager();
86 mUserManager = UserManager.get(context);
87 mNetd = netd;
88 mIntentReceiver = new BroadcastReceiver() {
89 @Override
90 public void onReceive(Context context, Intent intent) {
91 String action = intent.getAction();
92 int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
paulhu3b0f5ea2018-11-01 10:38:11 +080093 int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070094 Uri appData = intent.getData();
95 String appName = appData != null ? appData.getSchemeSpecificPart() : null;
96
97 if (Intent.ACTION_USER_ADDED.equals(action)) {
98 onUserAdded(user);
99 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
100 onUserRemoved(user);
101 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
102 onAppAdded(appName, appUid);
103 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
104 onAppRemoved(appUid);
105 }
106 }
107 };
108 }
109
110 // Intended to be called only once at startup, after the system is ready. Installs a broadcast
111 // receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
112 public synchronized void startMonitoring() {
113 log("Monitoring");
114
115 IntentFilter intentFilter = new IntentFilter();
116 intentFilter.addAction(Intent.ACTION_USER_ADDED);
117 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
118 mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
119
120 intentFilter = new IntentFilter();
121 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
122 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
123 intentFilter.addDataScheme("package");
124 mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
125
126 List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
127 if (apps == null) {
128 loge("No apps");
129 return;
130 }
131
132 for (PackageInfo app : apps) {
paulhu3b0f5ea2018-11-01 10:38:11 +0800133 int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700134 if (uid < 0) {
135 continue;
136 }
137
138 boolean isNetwork = hasNetworkPermission(app);
Hugo Benichi514da602016-07-19 15:59:27 +0900139 boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700140
Hugo Benichi514da602016-07-19 15:59:27 +0900141 if (isNetwork || hasRestrictedPermission) {
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700142 Boolean permission = mApps.get(uid);
143 // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
144 // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
145 if (permission == null || permission == NETWORK) {
Hugo Benichi514da602016-07-19 15:59:27 +0900146 mApps.put(uid, hasRestrictedPermission);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700147 }
148 }
149 }
150
151 List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users
152 if (users != null) {
153 for (UserInfo user : users) {
154 mUsers.add(user.id);
155 }
156 }
157
158 log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
159 update(mUsers, mApps, true);
160 }
161
Chalard Jean26aa91a2018-03-20 19:13:57 +0900162 @VisibleForTesting
paulhub6733802018-08-20 11:01:21 +0800163 static boolean isVendorApp(@NonNull ApplicationInfo appInfo) {
164 return appInfo.isVendor() || appInfo.isOem() || appInfo.isProduct();
Chalard Jean26aa91a2018-03-20 19:13:57 +0900165 }
166
167 @VisibleForTesting
junyulai345155e2018-11-09 12:37:16 +0800168 protected int getDeviceFirstSdkInt() {
paulhu3b0f5ea2018-11-01 10:38:11 +0800169 return Build.VERSION.FIRST_SDK_INT;
170 }
171
172 @VisibleForTesting
Chalard Jean26aa91a2018-03-20 19:13:57 +0900173 boolean hasPermission(PackageInfo app, String permission) {
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700174 if (app.requestedPermissions != null) {
175 for (String p : app.requestedPermissions) {
176 if (permission.equals(p)) {
177 return true;
178 }
179 }
180 }
181 return false;
182 }
183
184 private boolean hasNetworkPermission(PackageInfo app) {
185 return hasPermission(app, CHANGE_NETWORK_STATE);
186 }
187
Hugo Benichi514da602016-07-19 15:59:27 +0900188 private boolean hasRestrictedNetworkPermission(PackageInfo app) {
paulhub6733802018-08-20 11:01:21 +0800189 // TODO : remove this check in the future(b/31479477). All apps should just
190 // request the appropriate permission for their use case since android Q.
paulhu3b0f5ea2018-11-01 10:38:11 +0800191 if (app.applicationInfo != null) {
192 // Backward compatibility for b/114245686, on devices that launched before Q daemons
193 // and apps running as the system UID are exempted from this check.
194 if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
195 return true;
196 }
197
198 if (app.applicationInfo.targetSdkVersion < VERSION_Q
199 && isVendorApp(app.applicationInfo)) {
200 return true;
201 }
paulhub6733802018-08-20 11:01:21 +0800202 }
Hugo Benichi514da602016-07-19 15:59:27 +0900203 return hasPermission(app, CONNECTIVITY_INTERNAL)
204 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700205 }
206
Chalard Jean26aa91a2018-03-20 19:13:57 +0900207 private boolean hasUseBackgroundNetworksPermission(PackageInfo app) {
208 // This function defines what it means to hold the permission to use
209 // background networks.
210 return hasPermission(app, CHANGE_NETWORK_STATE)
Chalard Jean26aa91a2018-03-20 19:13:57 +0900211 || hasPermission(app, NETWORK_STACK)
paulhub6733802018-08-20 11:01:21 +0800212 || hasRestrictedNetworkPermission(app);
Chalard Jean26aa91a2018-03-20 19:13:57 +0900213 }
214
215 public boolean hasUseBackgroundNetworksPermission(int uid) {
216 final String[] names = mPackageManager.getPackagesForUid(uid);
217 if (null == names || names.length == 0) return false;
218 try {
219 // Only using the first package name. There may be multiple names if multiple
220 // apps share the same UID, but in that case they also share permissions so
221 // querying with any of the names will return the same results.
Tony Mak352dc0b2018-03-26 12:38:04 +0100222 int userId = UserHandle.getUserId(uid);
223 final PackageInfo app = mPackageManager.getPackageInfoAsUser(
224 names[0], GET_PERMISSIONS, userId);
Chalard Jean26aa91a2018-03-20 19:13:57 +0900225 return hasUseBackgroundNetworksPermission(app);
226 } catch (NameNotFoundException e) {
227 // App not found.
228 loge("NameNotFoundException " + names[0], e);
229 return false;
230 }
231 }
232
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700233 private int[] toIntArray(List<Integer> list) {
234 int[] array = new int[list.size()];
235 for (int i = 0; i < list.size(); i++) {
236 array[i] = list.get(i);
237 }
238 return array;
239 }
240
241 private void update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add) {
Hugo Benichi514da602016-07-19 15:59:27 +0900242 List<Integer> network = new ArrayList<>();
243 List<Integer> system = new ArrayList<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700244 for (Entry<Integer, Boolean> app : apps.entrySet()) {
245 List<Integer> list = app.getValue() ? system : network;
246 for (int user : users) {
247 list.add(UserHandle.getUid(user, app.getKey()));
248 }
249 }
250 try {
251 if (add) {
Sreeram Ramachandran0f8f1202014-11-04 10:15:03 -0800252 mNetd.setPermission("NETWORK", toIntArray(network));
253 mNetd.setPermission("SYSTEM", toIntArray(system));
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700254 } else {
255 mNetd.clearPermission(toIntArray(network));
256 mNetd.clearPermission(toIntArray(system));
257 }
258 } catch (RemoteException e) {
259 loge("Exception when updating permissions: " + e);
260 }
261 }
262
263 private synchronized void onUserAdded(int user) {
264 if (user < 0) {
265 loge("Invalid user in onUserAdded: " + user);
266 return;
267 }
268 mUsers.add(user);
269
Hugo Benichi514da602016-07-19 15:59:27 +0900270 Set<Integer> users = new HashSet<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700271 users.add(user);
272 update(users, mApps, true);
273 }
274
275 private synchronized void onUserRemoved(int user) {
276 if (user < 0) {
277 loge("Invalid user in onUserRemoved: " + user);
278 return;
279 }
280 mUsers.remove(user);
281
Hugo Benichi514da602016-07-19 15:59:27 +0900282 Set<Integer> users = new HashSet<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700283 users.add(user);
284 update(users, mApps, false);
285 }
286
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800287
288 private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
289 if (currentPermission == SYSTEM) {
290 return currentPermission;
291 }
292 try {
293 final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
294 final boolean isNetwork = hasNetworkPermission(app);
Hugo Benichi524b2e42016-07-26 13:08:14 +0900295 final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
296 if (isNetwork || hasRestrictedPermission) {
297 currentPermission = hasRestrictedPermission;
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800298 }
299 } catch (NameNotFoundException e) {
300 // App not found.
301 loge("NameNotFoundException " + name);
302 }
303 return currentPermission;
304 }
305
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700306 private synchronized void onAppAdded(String appName, int appUid) {
307 if (TextUtils.isEmpty(appName) || appUid < 0) {
308 loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
309 return;
310 }
311
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800312 // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
313 // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
314 final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
315 if (permission != mApps.get(appUid)) {
316 mApps.put(appUid, permission);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700317
Hugo Benichi524b2e42016-07-26 13:08:14 +0900318 Map<Integer, Boolean> apps = new HashMap<>();
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800319 apps.put(appUid, permission);
320 update(mUsers, apps, true);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700321 }
322 }
323
324 private synchronized void onAppRemoved(int appUid) {
325 if (appUid < 0) {
326 loge("Invalid app in onAppRemoved: " + appUid);
327 return;
328 }
Hugo Benichi514da602016-07-19 15:59:27 +0900329 Map<Integer, Boolean> apps = new HashMap<>();
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800330
331 Boolean permission = null;
332 String[] packages = mPackageManager.getPackagesForUid(appUid);
333 if (packages != null && packages.length > 0) {
334 for (String name : packages) {
335 permission = highestPermissionForUid(permission, name);
336 if (permission == SYSTEM) {
337 // An app with this UID still has the SYSTEM permission.
338 // Therefore, this UID must already have the SYSTEM permission.
339 // Nothing to do.
340 return;
341 }
342 }
343 }
344 if (permission == mApps.get(appUid)) {
345 // The permissions of this UID have not changed. Nothing to do.
346 return;
347 } else if (permission != null) {
348 mApps.put(appUid, permission);
349 apps.put(appUid, permission);
350 update(mUsers, apps, true);
351 } else {
352 mApps.remove(appUid);
353 apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
354 update(mUsers, apps, false);
355 }
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700356 }
357
358 private static void log(String s) {
359 if (DBG) {
360 Log.d(TAG, s);
361 }
362 }
363
364 private static void loge(String s) {
365 Log.e(TAG, s);
366 }
Chalard Jean26aa91a2018-03-20 19:13:57 +0900367
368 private static void loge(String s, Throwable e) {
369 Log.e(TAG, s, e);
370 }
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700371}