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.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