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 { onPermissionGroupsChanged()48 public void onPermissionGroupsChanged(); 49 } 50 PermissionGroups(Context context, LoaderManager loaderManager, PermissionsGroupsChangeCallback callback)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 onCreateLoader(int id, Bundle args)60 public Loader<List<PermissionGroup>> onCreateLoader(int id, Bundle args) { 61 return new PermissionsLoader(mContext); 62 } 63 64 @Override onLoadFinished(Loader<List<PermissionGroup>> loader, List<PermissionGroup> groups)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 onLoaderReset(Loader<List<PermissionGroup>> loader)76 public void onLoaderReset(Loader<List<PermissionGroup>> loader) { 77 mGroups.clear(); 78 mCallback.onPermissionGroupsChanged(); 79 } 80 getGroups()81 public List<PermissionGroup> getGroups() { 82 return mGroups; 83 } 84 getGroup(String name)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 PermissionsLoader(Context context)97 public PermissionsLoader(Context context) { 98 super(context); 99 } 100 101 @Override onStartLoading()102 protected void onStartLoading() { 103 getContext().getPackageManager().addOnPermissionsChangeListener(this); 104 forceLoad(); 105 } 106 107 @Override onStopLoading()108 protected void onStopLoading() { 109 getContext().getPackageManager().removeOnPermissionsChangeListener(this); 110 } 111 112 @Override loadInBackground()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 loadItemInfoLabel(PackageItemInfo itemInfo)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 loadItemInfoIcon(PackageItemInfo itemInfo)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 onPermissionsChanged(int uid)251 public void onPermissionsChanged(int uid) { 252 forceLoad(); 253 } 254 } 255 } 256