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