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