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