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 
     17 package com.android.packageinstaller.permission.model;
     18 
     19 import android.app.LoaderManager;
     20 import android.app.LoaderManager.LoaderCallbacks;
     21 import android.content.AsyncTaskLoader;
     22 import android.content.Context;
     23 import android.content.Loader;
     24 import android.content.pm.PackageInfo;
     25 import android.content.pm.PackageItemInfo;
     26 import android.content.pm.PackageManager;
     27 import android.content.pm.PermissionGroupInfo;
     28 import android.content.pm.PermissionInfo;
     29 import android.graphics.drawable.Drawable;
     30 import android.os.Bundle;
     31 import android.util.ArraySet;
     32 
     33 import com.android.packageinstaller.R;
     34 import com.android.packageinstaller.permission.utils.Utils;
     35 
     36 import java.util.ArrayList;
     37 import java.util.Collections;
     38 import java.util.List;
     39 import java.util.Set;
     40 
     41 public final class PermissionGroups implements LoaderCallbacks<List<PermissionGroup>> {
     42     private final ArrayList<PermissionGroup> mGroups = new ArrayList<>();
     43     private final Context mContext;
     44     private final LoaderManager mLoaderManager;
     45     private final PermissionsGroupsChangeCallback mCallback;
     46 
     47     public interface PermissionsGroupsChangeCallback {
     48         public void onPermissionGroupsChanged();
     49     }
     50 
     51     public PermissionGroups(Context context, LoaderManager loaderManager,
     52             PermissionsGroupsChangeCallback callback) {
     53         mContext = context;
     54         mLoaderManager = loaderManager;
     55         mCallback = callback;
     56         mLoaderManager.initLoader(0, null, this);
     57     }
     58 
     59     @Override
     60     public Loader<List<PermissionGroup>> onCreateLoader(int id, Bundle args) {
     61         return new PermissionsLoader(mContext);
     62     }
     63 
     64     @Override
     65     public void onLoadFinished(Loader<List<PermissionGroup>> loader,
     66             List<PermissionGroup> groups) {
     67         if (mGroups.equals(groups)) {
     68             return;
     69         }
     70         mGroups.clear();
     71         mGroups.addAll(groups);
     72         mCallback.onPermissionGroupsChanged();
     73     }
     74 
     75     @Override
     76     public void onLoaderReset(Loader<List<PermissionGroup>> loader) {
     77         mGroups.clear();
     78         mCallback.onPermissionGroupsChanged();
     79     }
     80 
     81     public List<PermissionGroup> getGroups() {
     82         return mGroups;
     83     }
     84 
     85     public PermissionGroup getGroup(String name) {
     86         for (PermissionGroup group : mGroups) {
     87             if (group.getName().equals(name)) {
     88                 return group;
     89             }
     90         }
     91         return null;
     92     }
     93 
     94     private static final class PermissionsLoader extends AsyncTaskLoader<List<PermissionGroup>>
     95             implements PackageManager.OnPermissionsChangedListener {
     96 
     97         public PermissionsLoader(Context context) {
     98             super(context);
     99         }
    100 
    101         @Override
    102         protected void onStartLoading() {
    103             getContext().getPackageManager().addOnPermissionsChangeListener(this);
    104             forceLoad();
    105         }
    106 
    107         @Override
    108         protected void onStopLoading() {
    109             getContext().getPackageManager().removeOnPermissionsChangeListener(this);
    110         }
    111 
    112         @Override
    113         public List<PermissionGroup> loadInBackground() {
    114             ArraySet<String> launcherPkgs = Utils.getLauncherPackages(getContext());
    115             PermissionApps.PmCache pmCache = new PermissionApps.PmCache(
    116                     getContext().getPackageManager());
    117 
    118             List<PermissionGroup> groups = new ArrayList<>();
    119             Set<String> seenPermissions = new ArraySet<>();
    120 
    121             PackageManager packageManager = getContext().getPackageManager();
    122             List<PermissionGroupInfo> groupInfos = packageManager.getAllPermissionGroups(0);
    123 
    124             for (PermissionGroupInfo groupInfo : groupInfos) {
    125                 // Mare sure we respond to cancellation.
    126                 if (isLoadInBackgroundCanceled()) {
    127                     return Collections.emptyList();
    128                 }
    129 
    130                 // Get the permissions in this group.
    131                 final List<PermissionInfo> groupPermissions;
    132                 try {
    133                     groupPermissions = packageManager.queryPermissionsByGroup(groupInfo.name, 0);
    134                 } catch (PackageManager.NameNotFoundException e) {
    135                     continue;
    136                 }
    137 
    138                 boolean hasRuntimePermissions = false;
    139 
    140                 // Cache seen permissions and see if group has runtime permissions.
    141                 for (PermissionInfo groupPermission : groupPermissions) {
    142                     seenPermissions.add(groupPermission.name);
    143                     if ((groupPermission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
    144                             == PermissionInfo.PROTECTION_DANGEROUS
    145                             && (groupPermission.flags & PermissionInfo.FLAG_INSTALLED) != 0
    146                             && (groupPermission.flags & PermissionInfo.FLAG_REMOVED) == 0) {
    147                         hasRuntimePermissions = true;
    148                     }
    149                 }
    150 
    151                 // No runtime permissions - not interesting for us.
    152                 if (!hasRuntimePermissions) {
    153                     continue;
    154                 }
    155 
    156                 CharSequence label = loadItemInfoLabel(groupInfo);
    157                 Drawable icon = loadItemInfoIcon(groupInfo);
    158 
    159                 PermissionApps permApps = new PermissionApps(getContext(), groupInfo.name, null,
    160                         pmCache);
    161                 permApps.refreshSync();
    162 
    163                 // Create the group and add to the list.
    164                 PermissionGroup group = new PermissionGroup(groupInfo.name,
    165                         groupInfo.packageName, label, icon, permApps.getTotalCount(launcherPkgs),
    166                         permApps.getGrantedCount(launcherPkgs));
    167                 groups.add(group);
    168             }
    169 
    170 
    171             // Make sure we add groups for lone runtime permissions.
    172             List<PackageInfo> installedPackages = getContext().getPackageManager()
    173                     .getInstalledPackages(PackageManager.GET_PERMISSIONS);
    174 
    175 
    176             // We will filter out permissions that no package requests.
    177             Set<String> requestedPermissions = new ArraySet<>();
    178             for (PackageInfo installedPackage : installedPackages) {
    179                 if (installedPackage.requestedPermissions == null) {
    180                     continue;
    181                 }
    182                 for (String requestedPermission : installedPackage.requestedPermissions) {
    183                     requestedPermissions.add(requestedPermission);
    184                 }
    185             }
    186 
    187             for (PackageInfo installedPackage : installedPackages) {
    188                 if (installedPackage.permissions == null) {
    189                     continue;
    190                 }
    191 
    192                 for (PermissionInfo permissionInfo : installedPackage.permissions) {
    193                     // If we have handled this permission, no more work to do.
    194                     if (!seenPermissions.add(permissionInfo.name)) {
    195                         continue;
    196                     }
    197 
    198                     // We care only about installed runtime permissions.
    199                     if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
    200                             != PermissionInfo.PROTECTION_DANGEROUS
    201                             || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0) {
    202                         continue;
    203                     }
    204 
    205                     // If no app uses this permission,
    206                     if (!requestedPermissions.contains(permissionInfo.name)) {
    207                         continue;
    208                     }
    209 
    210                     CharSequence label = loadItemInfoLabel(permissionInfo);
    211                     Drawable icon = loadItemInfoIcon(permissionInfo);
    212 
    213                     PermissionApps permApps = new PermissionApps(getContext(), permissionInfo.name,
    214                             null, pmCache);
    215                     permApps.refreshSync();
    216 
    217                     // Create the group and add to the list.
    218                     PermissionGroup group = new PermissionGroup(permissionInfo.name,
    219                             permissionInfo.packageName, label, icon,
    220                             permApps.getTotalCount(launcherPkgs),
    221                             permApps.getGrantedCount(launcherPkgs));
    222                     groups.add(group);
    223                 }
    224             }
    225 
    226             Collections.sort(groups);
    227             return groups;
    228         }
    229 
    230         private CharSequence loadItemInfoLabel(PackageItemInfo itemInfo) {
    231             CharSequence label = itemInfo.loadLabel(getContext().getPackageManager());
    232             if (label == null) {
    233                 label = itemInfo.name;
    234             }
    235             return label;
    236         }
    237 
    238         private Drawable loadItemInfoIcon(PackageItemInfo itemInfo) {
    239             Drawable icon = null;
    240             if (itemInfo.icon > 0) {
    241                 icon = Utils.loadDrawable(getContext().getPackageManager(),
    242                         itemInfo.packageName, itemInfo.icon);
    243             }
    244             if (icon == null) {
    245                 icon = getContext().getDrawable(R.drawable.ic_perm_device_info);
    246             }
    247             return icon;
    248         }
    249 
    250         @Override
    251         public void onPermissionsChanged(int uid) {
    252             forceLoad();
    253         }
    254     }
    255 }
    256