blob: e471c7d84b561ece43abb3afa9bce8518f4067a5 [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;
26
27import android.content.BroadcastReceiver;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
Chalard Jean26aa91a2018-03-20 19:13:57 +090031import android.content.pm.ApplicationInfo;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070032import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.PackageManager.NameNotFoundException;
35import android.content.pm.UserInfo;
36import android.net.Uri;
37import android.os.INetworkManagementService;
38import android.os.RemoteException;
39import android.os.UserHandle;
40import android.os.UserManager;
41import android.text.TextUtils;
42import android.util.Log;
43
Chalard Jean26aa91a2018-03-20 19:13:57 +090044import com.android.internal.annotations.VisibleForTesting;
45
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070046import java.util.ArrayList;
47import java.util.HashMap;
48import java.util.HashSet;
49import java.util.List;
50import java.util.Map.Entry;
51import java.util.Map;
52import java.util.Set;
53
54/**
55 * A utility class to inform Netd of UID permisisons.
56 * Does a mass update at boot and then monitors for app install/remove.
57 *
58 * @hide
59 */
60public class PermissionMonitor {
61 private static final String TAG = "PermissionMonitor";
62 private static final boolean DBG = true;
zhangshuxiaob48db5b2016-02-03 21:28:25 +080063 private static final Boolean SYSTEM = Boolean.TRUE;
64 private static final Boolean NETWORK = Boolean.FALSE;
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070065
66 private final Context mContext;
67 private final PackageManager mPackageManager;
68 private final UserManager mUserManager;
69 private final INetworkManagementService mNetd;
70 private final BroadcastReceiver mIntentReceiver;
71
72 // Values are User IDs.
Hugo Benichi514da602016-07-19 15:59:27 +090073 private final Set<Integer> mUsers = new HashSet<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070074
75 // Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
Hugo Benichi514da602016-07-19 15:59:27 +090076 private final Map<Integer, Boolean> mApps = new HashMap<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -070077
78 public PermissionMonitor(Context context, INetworkManagementService netd) {
79 mContext = context;
80 mPackageManager = context.getPackageManager();
81 mUserManager = UserManager.get(context);
82 mNetd = netd;
83 mIntentReceiver = new BroadcastReceiver() {
84 @Override
85 public void onReceive(Context context, Intent intent) {
86 String action = intent.getAction();
87 int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
88 int appUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
89 Uri appData = intent.getData();
90 String appName = appData != null ? appData.getSchemeSpecificPart() : null;
91
92 if (Intent.ACTION_USER_ADDED.equals(action)) {
93 onUserAdded(user);
94 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
95 onUserRemoved(user);
96 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
97 onAppAdded(appName, appUid);
98 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
99 onAppRemoved(appUid);
100 }
101 }
102 };
103 }
104
105 // Intended to be called only once at startup, after the system is ready. Installs a broadcast
106 // receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
107 public synchronized void startMonitoring() {
108 log("Monitoring");
109
110 IntentFilter intentFilter = new IntentFilter();
111 intentFilter.addAction(Intent.ACTION_USER_ADDED);
112 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
113 mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
114
115 intentFilter = new IntentFilter();
116 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
117 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
118 intentFilter.addDataScheme("package");
119 mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
120
121 List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
122 if (apps == null) {
123 loge("No apps");
124 return;
125 }
126
127 for (PackageInfo app : apps) {
128 int uid = app.applicationInfo != null ? app.applicationInfo.uid : -1;
129 if (uid < 0) {
130 continue;
131 }
132
133 boolean isNetwork = hasNetworkPermission(app);
Hugo Benichi514da602016-07-19 15:59:27 +0900134 boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700135
Hugo Benichi514da602016-07-19 15:59:27 +0900136 if (isNetwork || hasRestrictedPermission) {
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700137 Boolean permission = mApps.get(uid);
138 // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
139 // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
140 if (permission == null || permission == NETWORK) {
Hugo Benichi514da602016-07-19 15:59:27 +0900141 mApps.put(uid, hasRestrictedPermission);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700142 }
143 }
144 }
145
146 List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users
147 if (users != null) {
148 for (UserInfo user : users) {
149 mUsers.add(user.id);
150 }
151 }
152
153 log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
154 update(mUsers, mApps, true);
155 }
156
Chalard Jean26aa91a2018-03-20 19:13:57 +0900157 @VisibleForTesting
158 boolean isPreinstalledSystemApp(PackageInfo app) {
159 int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
160 return (flags & (FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP)) != 0;
161 }
162
163 @VisibleForTesting
164 boolean hasPermission(PackageInfo app, String permission) {
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700165 if (app.requestedPermissions != null) {
166 for (String p : app.requestedPermissions) {
167 if (permission.equals(p)) {
168 return true;
169 }
170 }
171 }
172 return false;
173 }
174
175 private boolean hasNetworkPermission(PackageInfo app) {
176 return hasPermission(app, CHANGE_NETWORK_STATE);
177 }
178
Hugo Benichi514da602016-07-19 15:59:27 +0900179 private boolean hasRestrictedNetworkPermission(PackageInfo app) {
Chalard Jean26aa91a2018-03-20 19:13:57 +0900180 if (isPreinstalledSystemApp(app)) return true;
Hugo Benichi514da602016-07-19 15:59:27 +0900181 return hasPermission(app, CONNECTIVITY_INTERNAL)
182 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700183 }
184
Chalard Jean26aa91a2018-03-20 19:13:57 +0900185 private boolean hasUseBackgroundNetworksPermission(PackageInfo app) {
186 // This function defines what it means to hold the permission to use
187 // background networks.
188 return hasPermission(app, CHANGE_NETWORK_STATE)
189 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)
190 || hasPermission(app, CONNECTIVITY_INTERNAL)
191 || hasPermission(app, NETWORK_STACK)
192 // TODO : remove this check (b/31479477). Not all preinstalled apps should
193 // have access to background networks, they should just request the appropriate
194 // permission for their use case from the list above.
195 || isPreinstalledSystemApp(app);
196 }
197
198 public boolean hasUseBackgroundNetworksPermission(int uid) {
199 final String[] names = mPackageManager.getPackagesForUid(uid);
200 if (null == names || names.length == 0) return false;
201 try {
202 // Only using the first package name. There may be multiple names if multiple
203 // apps share the same UID, but in that case they also share permissions so
204 // querying with any of the names will return the same results.
Tony Mak352dc0b2018-03-26 12:38:04 +0100205 int userId = UserHandle.getUserId(uid);
206 final PackageInfo app = mPackageManager.getPackageInfoAsUser(
207 names[0], GET_PERMISSIONS, userId);
Chalard Jean26aa91a2018-03-20 19:13:57 +0900208 return hasUseBackgroundNetworksPermission(app);
209 } catch (NameNotFoundException e) {
210 // App not found.
211 loge("NameNotFoundException " + names[0], e);
212 return false;
213 }
214 }
215
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700216 private int[] toIntArray(List<Integer> list) {
217 int[] array = new int[list.size()];
218 for (int i = 0; i < list.size(); i++) {
219 array[i] = list.get(i);
220 }
221 return array;
222 }
223
224 private void update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add) {
Hugo Benichi514da602016-07-19 15:59:27 +0900225 List<Integer> network = new ArrayList<>();
226 List<Integer> system = new ArrayList<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700227 for (Entry<Integer, Boolean> app : apps.entrySet()) {
228 List<Integer> list = app.getValue() ? system : network;
229 for (int user : users) {
230 list.add(UserHandle.getUid(user, app.getKey()));
231 }
232 }
233 try {
234 if (add) {
Sreeram Ramachandran0f8f1202014-11-04 10:15:03 -0800235 mNetd.setPermission("NETWORK", toIntArray(network));
236 mNetd.setPermission("SYSTEM", toIntArray(system));
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700237 } else {
238 mNetd.clearPermission(toIntArray(network));
239 mNetd.clearPermission(toIntArray(system));
240 }
241 } catch (RemoteException e) {
242 loge("Exception when updating permissions: " + e);
243 }
244 }
245
246 private synchronized void onUserAdded(int user) {
247 if (user < 0) {
248 loge("Invalid user in onUserAdded: " + user);
249 return;
250 }
251 mUsers.add(user);
252
Hugo Benichi514da602016-07-19 15:59:27 +0900253 Set<Integer> users = new HashSet<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700254 users.add(user);
255 update(users, mApps, true);
256 }
257
258 private synchronized void onUserRemoved(int user) {
259 if (user < 0) {
260 loge("Invalid user in onUserRemoved: " + user);
261 return;
262 }
263 mUsers.remove(user);
264
Hugo Benichi514da602016-07-19 15:59:27 +0900265 Set<Integer> users = new HashSet<>();
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700266 users.add(user);
267 update(users, mApps, false);
268 }
269
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800270
271 private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
272 if (currentPermission == SYSTEM) {
273 return currentPermission;
274 }
275 try {
276 final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
277 final boolean isNetwork = hasNetworkPermission(app);
Hugo Benichi524b2e42016-07-26 13:08:14 +0900278 final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
279 if (isNetwork || hasRestrictedPermission) {
280 currentPermission = hasRestrictedPermission;
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800281 }
282 } catch (NameNotFoundException e) {
283 // App not found.
284 loge("NameNotFoundException " + name);
285 }
286 return currentPermission;
287 }
288
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700289 private synchronized void onAppAdded(String appName, int appUid) {
290 if (TextUtils.isEmpty(appName) || appUid < 0) {
291 loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
292 return;
293 }
294
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800295 // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
296 // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
297 final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
298 if (permission != mApps.get(appUid)) {
299 mApps.put(appUid, permission);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700300
Hugo Benichi524b2e42016-07-26 13:08:14 +0900301 Map<Integer, Boolean> apps = new HashMap<>();
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800302 apps.put(appUid, permission);
303 update(mUsers, apps, true);
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700304 }
305 }
306
307 private synchronized void onAppRemoved(int appUid) {
308 if (appUid < 0) {
309 loge("Invalid app in onAppRemoved: " + appUid);
310 return;
311 }
Hugo Benichi514da602016-07-19 15:59:27 +0900312 Map<Integer, Boolean> apps = new HashMap<>();
zhangshuxiaob48db5b2016-02-03 21:28:25 +0800313
314 Boolean permission = null;
315 String[] packages = mPackageManager.getPackagesForUid(appUid);
316 if (packages != null && packages.length > 0) {
317 for (String name : packages) {
318 permission = highestPermissionForUid(permission, name);
319 if (permission == SYSTEM) {
320 // An app with this UID still has the SYSTEM permission.
321 // Therefore, this UID must already have the SYSTEM permission.
322 // Nothing to do.
323 return;
324 }
325 }
326 }
327 if (permission == mApps.get(appUid)) {
328 // The permissions of this UID have not changed. Nothing to do.
329 return;
330 } else if (permission != null) {
331 mApps.put(appUid, permission);
332 apps.put(appUid, permission);
333 update(mUsers, apps, true);
334 } else {
335 mApps.remove(appUid);
336 apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
337 update(mUsers, apps, false);
338 }
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700339 }
340
341 private static void log(String s) {
342 if (DBG) {
343 Log.d(TAG, s);
344 }
345 }
346
347 private static void loge(String s) {
348 Log.e(TAG, s);
349 }
Chalard Jean26aa91a2018-03-20 19:13:57 +0900350
351 private static void loge(String s, Throwable e) {
352 Log.e(TAG, s, e);
353 }
Sreeram Ramachandrane4a05af2014-09-24 09:16:19 -0700354}