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.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 
     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 
     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 
     79     protected abstract void updateExtraInfo(AppEntry app, String pkg, int uid);
     80 
     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 
     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
    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      */
    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      */
    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      */
    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      */
    258     private boolean shouldIgnorePackage(String packageName) {
    259         return packageName.equals("android") || packageName.equals(mContext.getPackageName());
    260     }
    261 
    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 
    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 
    305         public PermissionState(String packageName, UserHandle userHandle) {
    306             this.packageName = packageName;
    307             this.appOpMode = AppOpsManager.MODE_DEFAULT;
    308             this.userHandle = userHandle;
    309         }
    310 
    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