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