1 /* 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 17 package com.android.server.connectivity; 18 19 import static android.Manifest.permission.CHANGE_NETWORK_STATE; 20 import static android.Manifest.permission.CONNECTIVITY_INTERNAL; 21 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; 22 import static android.Manifest.permission.NETWORK_STACK; 23 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; 24 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 25 import static android.content.pm.PackageManager.GET_PERMISSIONS; 26 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.PackageInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PackageManager.NameNotFoundException; 35 import android.content.pm.UserInfo; 36 import android.net.Uri; 37 import android.os.INetworkManagementService; 38 import android.os.RemoteException; 39 import android.os.UserHandle; 40 import android.os.UserManager; 41 import android.text.TextUtils; 42 import android.util.Log; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 46 import java.util.ArrayList; 47 import java.util.HashMap; 48 import java.util.HashSet; 49 import java.util.List; 50 import java.util.Map.Entry; 51 import java.util.Map; 52 import 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 */ 60 public class PermissionMonitor { 61 private static final String TAG = "PermissionMonitor"; 62 private static final boolean DBG = true; 63 private static final Boolean SYSTEM = Boolean.TRUE; 64 private static final Boolean NETWORK = Boolean.FALSE; 65 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. 73 private final Set<Integer> mUsers = new HashSet<>(); 74 75 // Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission. 76 private final Map<Integer, Boolean> mApps = new HashMap<>(); 77 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); 134 boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app); 135 136 if (isNetwork || hasRestrictedPermission) { 137 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) { 141 mApps.put(uid, hasRestrictedPermission); 142 } 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 157 @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) { 165 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 179 private boolean hasRestrictedNetworkPermission(PackageInfo app) { 180 if (isPreinstalledSystemApp(app)) return true; 181 return hasPermission(app, CONNECTIVITY_INTERNAL) 182 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS); 183 } 184 185 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. 205 int userId = UserHandle.getUserId(uid); 206 final PackageInfo app = mPackageManager.getPackageInfoAsUser( 207 names[0], GET_PERMISSIONS, userId); 208 return hasUseBackgroundNetworksPermission(app); 209 } catch (NameNotFoundException e) { 210 // App not found. 211 loge("NameNotFoundException " + names[0], e); 212 return false; 213 } 214 } 215 216 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) { 225 List<Integer> network = new ArrayList<>(); 226 List<Integer> system = new ArrayList<>(); 227 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) { 235 mNetd.setPermission("NETWORK", toIntArray(network)); 236 mNetd.setPermission("SYSTEM", toIntArray(system)); 237 } 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 253 Set<Integer> users = new HashSet<>(); 254 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 265 Set<Integer> users = new HashSet<>(); 266 users.add(user); 267 update(users, mApps, false); 268 } 269 270 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); 278 final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app); 279 if (isNetwork || hasRestrictedPermission) { 280 currentPermission = hasRestrictedPermission; 281 } 282 } catch (NameNotFoundException e) { 283 // App not found. 284 loge("NameNotFoundException " + name); 285 } 286 return currentPermission; 287 } 288 289 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 295 // 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); 300 301 Map<Integer, Boolean> apps = new HashMap<>(); 302 apps.put(appUid, permission); 303 update(mUsers, apps, true); 304 } 305 } 306 307 private synchronized void onAppRemoved(int appUid) { 308 if (appUid < 0) { 309 loge("Invalid app in onAppRemoved: " + appUid); 310 return; 311 } 312 Map<Integer, Boolean> apps = new HashMap<>(); 313 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 } 339 } 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 } 350 351 private static void loge(String s, Throwable e) { 352 Log.e(TAG, s, e); 353 } 354 } 355