Home | History | Annotate | Download | only in model
      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.packageinstaller.permission.model;
     17 
     18 import android.content.Context;
     19 import android.content.pm.ApplicationInfo;
     20 import android.content.pm.PackageInfo;
     21 import android.content.pm.PackageItemInfo;
     22 import android.content.pm.PackageManager;
     23 import android.content.pm.PackageManager.NameNotFoundException;
     24 import android.content.pm.PermissionInfo;
     25 import android.graphics.drawable.Drawable;
     26 import android.os.AsyncTask;
     27 import android.os.Process;
     28 import android.os.UserHandle;
     29 import android.os.UserManager;
     30 import android.util.ArrayMap;
     31 import android.util.ArraySet;
     32 import android.util.Log;
     33 import android.util.SparseArray;
     34 
     35 import com.android.packageinstaller.R;
     36 import com.android.packageinstaller.permission.utils.Utils;
     37 
     38 import java.util.ArrayList;
     39 import java.util.Collections;
     40 import java.util.List;
     41 
     42 public class PermissionApps {
     43     private static final String LOG_TAG = "PermissionApps";
     44 
     45     private final Context mContext;
     46     private final String mGroupName;
     47     private final PackageManager mPm;
     48     private final Callback mCallback;
     49 
     50     private final PmCache mCache;
     51 
     52     private CharSequence mLabel;
     53     private Drawable mIcon;
     54     private List<PermissionApp> mPermApps;
     55     // Map (pkg|uid) -> AppPermission
     56     private ArrayMap<String, PermissionApp> mAppLookup;
     57 
     58     private boolean mSkipUi;
     59     private boolean mRefreshing;
     60 
     61     public PermissionApps(Context context, String groupName, Callback callback) {
     62         this(context, groupName, callback, null);
     63     }
     64 
     65     public PermissionApps(Context context, String groupName, Callback callback, PmCache cache) {
     66         mCache = cache;
     67         mContext = context;
     68         mPm = mContext.getPackageManager();
     69         mGroupName = groupName;
     70         mCallback = callback;
     71         loadGroupInfo();
     72     }
     73 
     74     public String getGroupName() {
     75         return mGroupName;
     76     }
     77 
     78     public void loadNowWithoutUi() {
     79         mSkipUi = true;
     80         createMap(loadPermissionApps());
     81     }
     82 
     83     public void refresh(boolean getUiInfo) {
     84         if (!mRefreshing) {
     85             mRefreshing = true;
     86             mSkipUi = !getUiInfo;
     87             new PermissionAppsLoader().execute();
     88         }
     89     }
     90 
     91     public int getGrantedCount(ArraySet<String> launcherPkgs) {
     92         int count = 0;
     93         for (PermissionApp app : mPermApps) {
     94             if (!Utils.shouldShowPermission(app)) {
     95                 continue;
     96             }
     97             if (Utils.isSystem(app, launcherPkgs)) {
     98                 // We default to not showing system apps, so hide them from count.
     99                 continue;
    100             }
    101             if (app.areRuntimePermissionsGranted()) {
    102                 count++;
    103             }
    104         }
    105         return count;
    106     }
    107 
    108     public int getTotalCount(ArraySet<String> launcherPkgs) {
    109         int count = 0;
    110         for (PermissionApp app : mPermApps) {
    111             if (!Utils.shouldShowPermission(app)) {
    112                 continue;
    113             }
    114             if (Utils.isSystem(app, launcherPkgs)) {
    115                 // We default to not showing system apps, so hide them from count.
    116                 continue;
    117             }
    118             count++;
    119         }
    120         return count;
    121     }
    122 
    123     public List<PermissionApp> getApps() {
    124         return mPermApps;
    125     }
    126 
    127     public PermissionApp getApp(String key) {
    128         return mAppLookup.get(key);
    129     }
    130 
    131     public CharSequence getLabel() {
    132         return mLabel;
    133     }
    134 
    135     public Drawable getIcon() {
    136         return mIcon;
    137     }
    138 
    139     private List<PermissionApp> loadPermissionApps() {
    140         PackageItemInfo groupInfo = getGroupInfo(mGroupName);
    141         if (groupInfo == null) {
    142             return Collections.emptyList();
    143         }
    144 
    145         List<PermissionInfo> groupPermInfos = getGroupPermissionInfos(mGroupName);
    146         if (groupPermInfos == null) {
    147             return Collections.emptyList();
    148         }
    149 
    150         ArrayList<PermissionApp> permApps = new ArrayList<>();
    151 
    152         UserManager userManager = mContext.getSystemService(UserManager.class);
    153         for (UserHandle user : userManager.getUserProfiles()) {
    154             List<PackageInfo> apps = mCache != null ? mCache.getPackages(user.getIdentifier())
    155                     : mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS,
    156                             user.getIdentifier());
    157 
    158             final int N = apps.size();
    159             for (int i = 0; i < N; i++) {
    160                 PackageInfo app = apps.get(i);
    161                 if (app.requestedPermissions == null) {
    162                     continue;
    163                 }
    164 
    165                 for (int j = 0; j < app.requestedPermissions.length; j++) {
    166                     String requestedPerm = app.requestedPermissions[j];
    167 
    168                     PermissionInfo requestedPermissionInfo = null;
    169 
    170                     for (PermissionInfo groupPermInfo : groupPermInfos) {
    171                         if (requestedPerm.equals(groupPermInfo.name)) {
    172                             requestedPermissionInfo = groupPermInfo;
    173                             break;
    174                         }
    175                     }
    176 
    177                     if (requestedPermissionInfo == null) {
    178                         continue;
    179                     }
    180 
    181                     if (requestedPermissionInfo.protectionLevel
    182                                 != PermissionInfo.PROTECTION_DANGEROUS
    183                             || (requestedPermissionInfo.flags
    184                                 & PermissionInfo.FLAG_INSTALLED) == 0
    185                             || (requestedPermissionInfo.flags
    186                                 & PermissionInfo.FLAG_REMOVED) != 0) {
    187                         continue;
    188                     }
    189 
    190                     AppPermissionGroup group = AppPermissionGroup.create(mContext,
    191                             app, groupInfo, groupPermInfos, user);
    192 
    193                     if (group == null) {
    194                         continue;
    195                     }
    196 
    197                     String label = mSkipUi ? app.packageName
    198                             : app.applicationInfo.loadLabel(mPm).toString();
    199 
    200                     Drawable icon = null;
    201                     if (!mSkipUi) {
    202                         UserHandle userHandle = new UserHandle(
    203                                 UserHandle.getUserId(group.getApp().applicationInfo.uid));
    204 
    205                         icon = mPm.getUserBadgedIcon(
    206                                 mPm.loadUnbadgedItemIcon(app.applicationInfo, app.applicationInfo),
    207                                 userHandle);
    208                     }
    209 
    210                     PermissionApp permApp = new PermissionApp(app.packageName, group, label, icon,
    211                             app.applicationInfo);
    212 
    213                     permApps.add(permApp);
    214                     break; // move to the next app.
    215                 }
    216             }
    217         }
    218 
    219         Collections.sort(permApps);
    220 
    221         return permApps;
    222     }
    223 
    224     private void createMap(List<PermissionApp> result) {
    225         mAppLookup = new ArrayMap<>();
    226         for (PermissionApp app : result) {
    227             mAppLookup.put(app.getKey(), app);
    228         }
    229         mPermApps = result;
    230     }
    231 
    232     private PackageItemInfo getGroupInfo(String groupName) {
    233         try {
    234             return mContext.getPackageManager().getPermissionGroupInfo(groupName, 0);
    235         } catch (NameNotFoundException e) {
    236             /* ignore */
    237         }
    238         try {
    239             return mContext.getPackageManager().getPermissionInfo(groupName, 0);
    240         } catch (NameNotFoundException e2) {
    241             /* ignore */
    242         }
    243         return null;
    244     }
    245 
    246     private List<PermissionInfo> getGroupPermissionInfos(String groupName) {
    247         try {
    248             return mContext.getPackageManager().queryPermissionsByGroup(groupName, 0);
    249         } catch (NameNotFoundException e) {
    250             /* ignore */
    251         }
    252         try {
    253             PermissionInfo permissionInfo = mContext.getPackageManager()
    254                     .getPermissionInfo(groupName, 0);
    255             List<PermissionInfo> permissions = new ArrayList<>();
    256             permissions.add(permissionInfo);
    257             return permissions;
    258         } catch (NameNotFoundException e2) {
    259             /* ignore */
    260         }
    261         return null;
    262     }
    263 
    264     private void loadGroupInfo() {
    265         PackageItemInfo info;
    266         try {
    267             info = mPm.getPermissionGroupInfo(mGroupName, 0);
    268         } catch (PackageManager.NameNotFoundException e) {
    269             try {
    270                 PermissionInfo permInfo = mPm.getPermissionInfo(mGroupName, 0);
    271                 if (permInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) {
    272                     Log.w(LOG_TAG, mGroupName + " is not a runtime permission");
    273                     return;
    274                 }
    275                 info = permInfo;
    276             } catch (NameNotFoundException reallyNotFound) {
    277                 Log.w(LOG_TAG, "Can't find permission: " + mGroupName, reallyNotFound);
    278                 return;
    279             }
    280         }
    281         mLabel = info.loadLabel(mPm);
    282         if (info.icon != 0) {
    283             mIcon = info.loadUnbadgedIcon(mPm);
    284         } else {
    285             mIcon = mContext.getDrawable(R.drawable.ic_perm_device_info);
    286         }
    287         mIcon = Utils.applyTint(mContext, mIcon, android.R.attr.colorControlNormal);
    288     }
    289 
    290     public static class PermissionApp implements Comparable<PermissionApp> {
    291         private final String mPackageName;
    292         private final AppPermissionGroup mAppPermissionGroup;
    293         private final String mLabel;
    294         private final Drawable mIcon;
    295         private final ApplicationInfo mInfo;
    296 
    297         public PermissionApp(String packageName, AppPermissionGroup appPermissionGroup,
    298                 String label, Drawable icon, ApplicationInfo info) {
    299             mPackageName = packageName;
    300             mAppPermissionGroup = appPermissionGroup;
    301             mLabel = label;
    302             mIcon = icon;
    303             mInfo = info;
    304         }
    305 
    306         public ApplicationInfo getAppInfo() {
    307             return mInfo;
    308         }
    309 
    310         public String getKey() {
    311             return mPackageName + getUid();
    312         }
    313 
    314         public String getLabel() {
    315             return mLabel;
    316         }
    317 
    318         public Drawable getIcon() {
    319             return mIcon;
    320         }
    321 
    322         public boolean areRuntimePermissionsGranted() {
    323             return mAppPermissionGroup.areRuntimePermissionsGranted();
    324         }
    325 
    326         public boolean isReviewRequired() {
    327             return mAppPermissionGroup.isReviewRequired();
    328         }
    329 
    330         public void grantRuntimePermissions() {
    331             mAppPermissionGroup.grantRuntimePermissions(false);
    332         }
    333 
    334         public void revokeRuntimePermissions() {
    335             mAppPermissionGroup.revokeRuntimePermissions(false);
    336         }
    337 
    338         public boolean isPolicyFixed() {
    339             return mAppPermissionGroup.isPolicyFixed();
    340         }
    341 
    342         public boolean isSystemFixed() {
    343             return mAppPermissionGroup.isSystemFixed();
    344         }
    345 
    346         public boolean hasGrantedByDefaultPermissions() {
    347             return mAppPermissionGroup.hasGrantedByDefaultPermission();
    348         }
    349 
    350         public boolean hasRuntimePermissions() {
    351             return mAppPermissionGroup.hasRuntimePermission();
    352         }
    353 
    354         public int getUserId() {
    355             return mAppPermissionGroup.getUserId();
    356         }
    357 
    358         public String getPackageName() {
    359             return mPackageName;
    360         }
    361 
    362         public AppPermissionGroup getPermissionGroup() {
    363             return mAppPermissionGroup;
    364         }
    365 
    366         @Override
    367         public int compareTo(PermissionApp another) {
    368             final int result = mLabel.compareTo(another.mLabel);
    369             if (result == 0) {
    370                 // Unbadged before badged.
    371                 return getKey().compareTo(another.getKey());
    372             }
    373             return result;
    374         }
    375 
    376         public int getUid() {
    377             return mAppPermissionGroup.getApp().applicationInfo.uid;
    378         }
    379     }
    380 
    381     private class PermissionAppsLoader extends AsyncTask<Void, Void, List<PermissionApp>> {
    382 
    383         @Override
    384         protected List<PermissionApp> doInBackground(Void... args) {
    385             return loadPermissionApps();
    386         }
    387 
    388         @Override
    389         protected void onPostExecute(List<PermissionApp> result) {
    390             mRefreshing = false;
    391             createMap(result);
    392             if (mCallback != null) {
    393                 mCallback.onPermissionsLoaded(PermissionApps.this);
    394             }
    395         }
    396     }
    397 
    398     /**
    399      * Class used to reduce the number of calls to the package manager.
    400      * This caches app information so it should only be used across parallel PermissionApps
    401      * instances, and should not be retained across UI refresh.
    402      */
    403     public static class PmCache {
    404         private final SparseArray<List<PackageInfo>> mPackageInfoCache = new SparseArray<>();
    405         private final PackageManager mPm;
    406 
    407         public PmCache(PackageManager pm) {
    408             mPm = pm;
    409         }
    410 
    411         public synchronized List<PackageInfo> getPackages(int userId) {
    412             List<PackageInfo> ret = mPackageInfoCache.get(userId);
    413             if (ret == null) {
    414                 ret = mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, userId);
    415                 mPackageInfoCache.put(userId, ret);
    416             }
    417             return ret;
    418         }
    419     }
    420 
    421     public interface Callback {
    422         void onPermissionsLoaded(PermissionApps permissionApps);
    423     }
    424 }
    425