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