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 } 57 58 @Override 59 public Loader<List<PermissionGroup>> onCreateLoader(int id, Bundle args) { 60 return new PermissionsLoader(mContext); 61 } 62 63 @Override 64 public void onLoadFinished(Loader<List<PermissionGroup>> loader, 65 List<PermissionGroup> groups) { 66 if (mGroups.equals(groups)) { 67 return; 68 } 69 mGroups.clear(); 70 mGroups.addAll(groups); 71 mCallback.onPermissionGroupsChanged(); 72 } 73 74 @Override 75 public void onLoaderReset(Loader<List<PermissionGroup>> loader) { 76 mGroups.clear(); 77 mCallback.onPermissionGroupsChanged(); 78 } 79 80 public void refresh() { 81 mLoaderManager.restartLoader(0, null, this); 82 mLoaderManager.getLoader(0).forceLoad(); 83 } 84 85 public List<PermissionGroup> getGroups() { 86 return mGroups; 87 } 88 89 public PermissionGroup getGroup(String name) { 90 for (PermissionGroup group : mGroups) { 91 if (group.getName().equals(name)) { 92 return group; 93 } 94 } 95 return null; 96 } 97 98 private static final class PermissionsLoader extends AsyncTaskLoader<List<PermissionGroup>> { 99 100 public PermissionsLoader(Context context) { 101 super(context); 102 } 103 104 @Override 105 public List<PermissionGroup> loadInBackground() { 106 List<PermissionGroup> groups = new ArrayList<>(); 107 Set<String> seenPermissions = new ArraySet<>(); 108 109 110 PackageManager packageManager = getContext().getPackageManager(); 111 List<PermissionGroupInfo> groupInfos = packageManager.getAllPermissionGroups(0); 112 113 for (PermissionGroupInfo groupInfo : groupInfos) { 114 // Mare sure we respond to cancellation. 115 if (isLoadInBackgroundCanceled()) { 116 return Collections.emptyList(); 117 } 118 119 // Get the permissions in this group. 120 final List<PermissionInfo> groupPermissions; 121 try { 122 groupPermissions = packageManager.queryPermissionsByGroup(groupInfo.name, 0); 123 } catch (PackageManager.NameNotFoundException e) { 124 continue; 125 } 126 127 boolean hasRuntimePermissions = false; 128 129 // Cache seen permissions and see if group has runtime permissions. 130 for (PermissionInfo groupPermission : groupPermissions) { 131 seenPermissions.add(groupPermission.name); 132 if (groupPermission.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS 133 && (groupPermission.flags & PermissionInfo.FLAG_INSTALLED) != 0 134 && (groupPermission.flags & PermissionInfo.FLAG_REMOVED) == 0) { 135 hasRuntimePermissions = true; 136 } 137 } 138 139 // No runtime permissions - not interesting for us. 140 if (!hasRuntimePermissions) { 141 continue; 142 } 143 144 CharSequence label = loadItemInfoLabel(groupInfo); 145 Drawable icon = loadItemInfoIcon(groupInfo); 146 147 // Create the group and add to the list. 148 PermissionGroup group = new PermissionGroup(groupInfo.name, 149 groupInfo.packageName, label, icon); 150 groups.add(group); 151 } 152 153 154 // Make sure we add groups for lone runtime permissions. 155 List<PackageInfo> installedPackages = getContext().getPackageManager() 156 .getInstalledPackages(PackageManager.GET_PERMISSIONS); 157 158 159 // We will filter out permissions that no package requests. 160 Set<String> requestedPermissions = new ArraySet<>(); 161 for (PackageInfo installedPackage : installedPackages) { 162 if (installedPackage.requestedPermissions == null) { 163 continue; 164 } 165 for (String requestedPermission : installedPackage.requestedPermissions) { 166 requestedPermissions.add(requestedPermission); 167 } 168 } 169 170 for (PackageInfo installedPackage : installedPackages) { 171 if (installedPackage.permissions == null) { 172 continue; 173 } 174 175 for (PermissionInfo permissionInfo : installedPackage.permissions) { 176 // If we have handled this permission, no more work to do. 177 if (!seenPermissions.add(permissionInfo.name)) { 178 continue; 179 } 180 181 // We care only about installed runtime permissions. 182 if (permissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS 183 || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0) { 184 continue; 185 } 186 187 // If no app uses this permission, 188 if (!requestedPermissions.contains(permissionInfo.name)) { 189 continue; 190 } 191 192 CharSequence label = loadItemInfoLabel(permissionInfo); 193 Drawable icon = loadItemInfoIcon(permissionInfo); 194 195 // Create the group and add to the list. 196 PermissionGroup group = new PermissionGroup(permissionInfo.name, 197 permissionInfo.packageName, label, icon); 198 groups.add(group); 199 } 200 } 201 202 Collections.sort(groups); 203 return groups; 204 } 205 206 private CharSequence loadItemInfoLabel(PackageItemInfo itemInfo) { 207 CharSequence label = itemInfo.loadLabel(getContext().getPackageManager()); 208 if (label == null) { 209 label = itemInfo.name; 210 } 211 return label; 212 } 213 214 private Drawable loadItemInfoIcon(PackageItemInfo itemInfo) { 215 Drawable icon = null; 216 if (itemInfo.icon > 0) { 217 icon = Utils.loadDrawable(getContext().getPackageManager(), 218 itemInfo.packageName, itemInfo.icon); 219 } 220 if (icon == null) { 221 icon = getContext().getDrawable(R.drawable.ic_perm_device_info); 222 } 223 return icon; 224 } 225 } 226 } 227