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