1 /* 2 * Copyright (C) 2015 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 package com.android.settings.applications; 17 18 import android.app.AppGlobals; 19 import android.app.AppOpsManager; 20 import android.app.AppOpsManager.PackageOps; 21 import android.content.Context; 22 import android.content.pm.IPackageManager; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.os.RemoteException; 26 import android.os.UserHandle; 27 import android.os.UserManager; 28 import android.util.ArrayMap; 29 import android.util.Log; 30 import android.util.SparseArray; 31 32 import androidx.annotation.VisibleForTesting; 33 34 import com.android.settingslib.applications.ApplicationsState; 35 import com.android.settingslib.applications.ApplicationsState.AppEntry; 36 37 import java.util.Arrays; 38 import java.util.Collection; 39 import java.util.HashSet; 40 import java.util.List; 41 import java.util.Set; 42 43 /* 44 * Connects app ops info to the ApplicationsState. Makes use of AppOpsManager to 45 * determine further permission level. 46 */ 47 public abstract class AppStateAppOpsBridge extends AppStateBaseBridge { 48 49 private static final String TAG = "AppStateAppOpsBridge"; 50 51 private final IPackageManager mIPackageManager; 52 private final UserManager mUserManager; 53 private final List<UserHandle> mProfiles; 54 private final AppOpsManager mAppOpsManager; 55 private final Context mContext; 56 private final int[] mAppOpsOpCodes; 57 private final String[] mPermissions; 58 AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, int appOpsOpCode, String[] permissions)59 public AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, 60 int appOpsOpCode, String[] permissions) { 61 this(context, appState, callback, appOpsOpCode, permissions, 62 AppGlobals.getPackageManager()); 63 } 64 AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, int[] appOpsOpCodes, String[] permissions)65 AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, 66 int[] appOpsOpCodes, String[] permissions) { 67 this(context, appState, callback, appOpsOpCodes, permissions, 68 AppGlobals.getPackageManager()); 69 } 70 71 @VisibleForTesting AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, int appOpsOpCode, String[] permissions, IPackageManager packageManager)72 AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, 73 int appOpsOpCode, String[] permissions, IPackageManager packageManager) { 74 this(context, appState, callback, new int[]{appOpsOpCode}, permissions, 75 packageManager); 76 } 77 AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, int[] appOpsOpCodes, String[] permissions, IPackageManager packageManager)78 AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, 79 int[] appOpsOpCodes, String[] permissions, IPackageManager packageManager) { 80 super(appState, callback); 81 mContext = context; 82 mIPackageManager = packageManager; 83 mUserManager = UserManager.get(context); 84 mProfiles = mUserManager.getUserProfiles(); 85 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 86 mAppOpsOpCodes = appOpsOpCodes; 87 mPermissions = permissions; 88 } 89 isThisUserAProfileOfCurrentUser(final int userId)90 private boolean isThisUserAProfileOfCurrentUser(final int userId) { 91 final int profilesMax = mProfiles.size(); 92 for (int i = 0; i < profilesMax; i++) { 93 if (mProfiles.get(i).getIdentifier() == userId) { 94 return true; 95 } 96 } 97 return false; 98 } 99 updateExtraInfo(AppEntry app, String pkg, int uid)100 protected abstract void updateExtraInfo(AppEntry app, String pkg, int uid); 101 doesAnyPermissionMatch(String permissionToMatch, String[] permissions)102 private boolean doesAnyPermissionMatch(String permissionToMatch, String[] permissions) { 103 for (String permission : permissions) { 104 if (permissionToMatch.equals(permission)) { 105 return true; 106 } 107 } 108 return false; 109 } 110 getPermissionInfo(String pkg, int uid)111 public PermissionState getPermissionInfo(String pkg, int uid) { 112 PermissionState permissionState = new PermissionState(pkg, new UserHandle(UserHandle 113 .getUserId(uid))); 114 try { 115 permissionState.packageInfo = mIPackageManager.getPackageInfo(pkg, 116 PackageManager.GET_PERMISSIONS | PackageManager.MATCH_ANY_USER, 117 permissionState.userHandle.getIdentifier()); 118 if (permissionState.packageInfo != null) { 119 // Check static permission state (whatever that is declared in package manifest) 120 String[] requestedPermissions = permissionState.packageInfo.requestedPermissions; 121 int[] permissionFlags = permissionState.packageInfo.requestedPermissionsFlags; 122 if (requestedPermissions != null) { 123 for (int i = 0; i < requestedPermissions.length; i++) { 124 if (doesAnyPermissionMatch(requestedPermissions[i], mPermissions)) { 125 permissionState.permissionDeclared = true; 126 if ((permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) 127 != 0) { 128 permissionState.staticPermissionGranted = true; 129 break; 130 } 131 } 132 } 133 } 134 } 135 // Check app op state. 136 List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, mAppOpsOpCodes); 137 if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) { 138 permissionState.appOpMode = ops.get(0).getOps().get(0).getMode(); 139 } 140 } catch (RemoteException e) { 141 Log.w(TAG, "PackageManager is dead. Can't get package info " + pkg, e); 142 } 143 return permissionState; 144 } 145 146 @Override loadAllExtraInfo()147 protected void loadAllExtraInfo() { 148 SparseArray<ArrayMap<String, PermissionState>> entries = getEntries(); 149 150 // Load state info. 151 loadPermissionsStates(entries); 152 loadAppOpsStates(entries); 153 154 // Map states to application info. 155 List<AppEntry> apps = mAppSession.getAllApps(); 156 final int N = apps.size(); 157 for (int i = 0; i < N; i++) { 158 AppEntry app = apps.get(i); 159 int userId = UserHandle.getUserId(app.info.uid); 160 if (entries != null) { 161 ArrayMap<String, PermissionState> userMap = entries.get(userId); 162 app.extraInfo = userMap != null ? userMap.get(app.info.packageName) : null; 163 } else { 164 app.extraInfo = null; 165 } 166 } 167 } 168 169 /* 170 * Gets a sparse array that describes every user on the device and all the associated packages 171 * of each user, together with the packages available for that user. 172 */ getEntries()173 private SparseArray<ArrayMap<String, PermissionState>> getEntries() { 174 try { 175 Set<String> packagesSet = new HashSet<>(); 176 for (String permission : mPermissions) { 177 String[] pkgs = mIPackageManager.getAppOpPermissionPackages(permission); 178 if (pkgs != null) { 179 packagesSet.addAll(Arrays.asList(pkgs)); 180 } 181 } 182 183 if (packagesSet.isEmpty()) { 184 // No packages are requesting permission as specified by mPermissions. 185 return null; 186 } 187 188 // Create a sparse array that maps profileIds to an ArrayMap that maps package names to 189 // an associated PermissionState object 190 SparseArray<ArrayMap<String, PermissionState>> entries = new SparseArray<>(); 191 for (final UserHandle profile : mProfiles) { 192 final ArrayMap<String, PermissionState> entriesForProfile = new ArrayMap<>(); 193 final int profileId = profile.getIdentifier(); 194 entries.put(profileId, entriesForProfile); 195 for (final String packageName : packagesSet) { 196 final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName, 197 profileId); 198 if (!shouldIgnorePackage(packageName) && isAvailable) { 199 final PermissionState newEntry = new PermissionState(packageName, profile); 200 entriesForProfile.put(packageName, newEntry); 201 } 202 } 203 } 204 205 return entries; 206 } catch (RemoteException e) { 207 Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting " 208 + mPermissions[0], e); 209 return null; 210 } 211 } 212 213 /* 214 * This method will set the packageInfo and staticPermissionGranted field of the associated 215 * PermissionState, which describes a particular package. 216 */ loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries)217 private void loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries) { 218 // Load the packages that have been granted the permission specified in mPermission. 219 if (entries == null) { 220 return; 221 } 222 223 try { 224 for (final UserHandle profile : mProfiles) { 225 final int profileId = profile.getIdentifier(); 226 final ArrayMap<String, PermissionState> entriesForProfile = entries.get(profileId); 227 if (entriesForProfile == null) { 228 continue; 229 } 230 @SuppressWarnings("unchecked") final List<PackageInfo> packageInfos = 231 mIPackageManager 232 .getPackagesHoldingPermissions(mPermissions, 0, 233 profileId).getList(); 234 final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0; 235 for (int i = 0; i < packageInfoCount; i++) { 236 final PackageInfo packageInfo = packageInfos.get(i); 237 final PermissionState pe = entriesForProfile.get(packageInfo.packageName); 238 if (pe != null) { 239 pe.packageInfo = packageInfo; 240 pe.staticPermissionGranted = true; 241 } 242 } 243 } 244 } catch (RemoteException e) { 245 Log.w(TAG, "PackageManager is dead. Can't get list of packages granted " 246 + mPermissions, e); 247 return; 248 } 249 } 250 251 /* 252 * This method will set the appOpMode field of the associated PermissionState, which describes 253 * a particular package. 254 */ loadAppOpsStates(SparseArray<ArrayMap<String, PermissionState>> entries)255 private void loadAppOpsStates(SparseArray<ArrayMap<String, PermissionState>> entries) { 256 if (entries == null) { 257 return; 258 } 259 260 // Find out which packages have been granted permission from AppOps. 261 final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps( 262 mAppOpsOpCodes); 263 final int packageOpsCount = packageOps != null ? packageOps.size() : 0; 264 for (int i = 0; i < packageOpsCount; i++) { 265 final AppOpsManager.PackageOps packageOp = packageOps.get(i); 266 final int userId = UserHandle.getUserId(packageOp.getUid()); 267 if (!isThisUserAProfileOfCurrentUser(userId)) { 268 // This AppOp does not belong to any of this user's profiles. 269 continue; 270 } 271 272 final ArrayMap<String, PermissionState> entriesForProfile = entries.get(userId); 273 if (entriesForProfile == null) { 274 continue; 275 } 276 final PermissionState pe = entriesForProfile.get(packageOp.getPackageName()); 277 if (pe == null) { 278 Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName() 279 + " of user " + userId + " but package doesn't exist or did not request " 280 + Arrays.toString(mPermissions) + " access"); 281 continue; 282 } 283 284 if (packageOp.getOps().size() < 1) { 285 Log.w(TAG, "No AppOps permission exists for package " + packageOp.getPackageName()); 286 continue; 287 } 288 pe.appOpMode = packageOp.getOps().get(0).getMode(); 289 } 290 } 291 292 /* 293 * Check for packages that should be ignored for further processing 294 */ shouldIgnorePackage(String packageName)295 private boolean shouldIgnorePackage(String packageName) { 296 return packageName.equals("android") || packageName.equals(mContext.getPackageName()); 297 } 298 getNumPackagesDeclaredPermission()299 public int getNumPackagesDeclaredPermission() { 300 SparseArray<ArrayMap<String, PermissionState>> entries = getEntries(); 301 if (entries == null) { 302 return 0; 303 } 304 final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager 305 .getUserHandle()); 306 if (entriesForProfile == null) { 307 return 0; 308 } 309 return entriesForProfile.size(); 310 } 311 getNumPackagesAllowedByAppOps()312 public int getNumPackagesAllowedByAppOps() { 313 SparseArray<ArrayMap<String, PermissionState>> entries = getEntries(); 314 if (entries == null) { 315 return 0; 316 } 317 loadPermissionsStates(entries); 318 loadAppOpsStates(entries); 319 final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager 320 .getUserHandle()); 321 if (entriesForProfile == null) { 322 return 0; 323 } 324 Collection<PermissionState> permStates = entriesForProfile.values(); 325 int result = 0; 326 for (PermissionState permState : permStates) { 327 if (permState.isPermissible()) { 328 result++; 329 } 330 } 331 return result; 332 } 333 334 public static class PermissionState { 335 public final String packageName; 336 public final UserHandle userHandle; 337 public PackageInfo packageInfo; 338 public boolean staticPermissionGranted; 339 public boolean permissionDeclared; 340 public int appOpMode; 341 PermissionState(String packageName, UserHandle userHandle)342 public PermissionState(String packageName, UserHandle userHandle) { 343 this.packageName = packageName; 344 this.appOpMode = AppOpsManager.MODE_DEFAULT; 345 this.userHandle = userHandle; 346 } 347 isPermissible()348 public boolean isPermissible() { 349 // defining the default behavior as permissible as long as the package requested this 350 // permission (this means pre-M gets approval during install time; M apps gets approval 351 // during runtime). 352 if (appOpMode == AppOpsManager.MODE_DEFAULT) { 353 return staticPermissionGranted; 354 } 355 return appOpMode == AppOpsManager.MODE_ALLOWED; 356 } 357 } 358 } 359