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.BroadcastReceiver; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.ApplicationInfo; 22 import android.content.pm.PackageInfo; 23 import android.content.pm.PackageManager; 24 import android.content.pm.PackageManager.NameNotFoundException; 25 import android.util.ArrayMap; 26 import android.util.ArraySet; 27 28 import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp; 29 import com.android.packageinstaller.permission.utils.Utils; 30 31 import java.text.Collator; 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.List; 35 36 /** 37 * This class handles backwards compatibility for M. Don't remove 38 * until we decide to drop M support altogether. 39 */ 40 public class PermissionStatusReceiver extends BroadcastReceiver { 41 42 /** 43 * Broadcast action that requests current permission granted information. It will respond 44 * to the request by sending a broadcast with action defined by 45 * {@link #EXTRA_GET_PERMISSIONS_RESPONSE_INTENT}. The response will contain 46 * {@link #EXTRA_GET_PERMISSIONS_COUNT_RESULT}, as well as 47 * {@link #EXTRA_GET_PERMISSIONS_GROUP_LIST_RESULT}, with contents described below or 48 * a null upon failure. 49 * 50 * <p>If {@link Intent#EXTRA_PACKAGE_NAME} is included then the number of permissions granted, the 51 * number of permissions requested and the number of granted additional permissions 52 * by that package will be calculated and included as the first 53 * and second elements respectively of an int[] in the response as 54 * {@link #EXTRA_GET_PERMISSIONS_COUNT_RESULT}. The response will also deliver the list 55 * of localized permission group names that are granted in 56 * {@link #EXTRA_GET_PERMISSIONS_GROUP_LIST_RESULT}. 57 * 58 * <p>If {@link #EXTRA_PACKAGE_NAME} is not included then the number of apps granted any runtime 59 * permissions and the total number of apps requesting runtime permissions will be the first 60 * and second elements respectively of an int[] in the response as 61 * {@link #EXTRA_GET_PERMISSIONS_COUNT_RESULT}. 62 * 63 * @hide 64 */ 65 public static final String ACTION_GET_PERMISSIONS_COUNT 66 = "android.intent.action.GET_PERMISSIONS_COUNT"; 67 68 /** 69 * Broadcast action that requests list of all apps that have runtime permissions. It will 70 * respond to the request by sending a broadcast with action defined by 71 * {@link #EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT}. The response will contain 72 * {@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT}, as well as 73 * {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}, with contents described below or 74 * a null upon failure. 75 * 76 * <p>{@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT} will contain a list of package names of 77 * apps that have runtime permissions. {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT} 78 * will contain the list of app labels corresponding ot the apps in the first list. 79 * 80 * @hide 81 */ 82 public static final String ACTION_GET_PERMISSIONS_PACKAGES 83 = "android.intent.action.GET_PERMISSIONS_PACKAGES"; 84 85 /** 86 * Extra included in response to {@link #ACTION_GET_PERMISSIONS_COUNT}. 87 * @hide 88 */ 89 public static final String EXTRA_GET_PERMISSIONS_COUNT_RESULT 90 = "android.intent.extra.GET_PERMISSIONS_COUNT_RESULT"; 91 92 /** 93 * List of CharSequence of localized permission group labels. 94 * @hide 95 */ 96 public static final String EXTRA_GET_PERMISSIONS_GROUP_LIST_RESULT 97 = "android.intent.extra.GET_PERMISSIONS_GROUP_LIST_RESULT"; 98 99 /** 100 * String list of apps that have one or more runtime permissions. 101 * @hide 102 */ 103 public static final String EXTRA_GET_PERMISSIONS_APP_LIST_RESULT 104 = "android.intent.extra.GET_PERMISSIONS_APP_LIST_RESULT"; 105 106 /** 107 * String list of app labels for apps that have one or more runtime permissions. 108 * @hide 109 */ 110 public static final String EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT 111 = "android.intent.extra.GET_PERMISSIONS_APP_LABEL_LIST_RESULT"; 112 113 /** 114 * Boolean list describing if the app is a system app for apps that have one or more runtime 115 * permissions. 116 * @hide 117 */ 118 public static final String EXTRA_GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT 119 = "android.intent.extra.GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT"; 120 121 /** 122 * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_COUNT} broadcasts. 123 * @hide 124 */ 125 public static final String EXTRA_GET_PERMISSIONS_RESPONSE_INTENT 126 = "android.intent.extra.GET_PERMISSIONS_RESONSE_INTENT"; 127 128 /** 129 * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_PACKAGES} broadcasts. 130 * @hide 131 */ 132 public static final String EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT 133 = "android.intent.extra.GET_PERMISSIONS_PACKAGES_RESONSE_INTENT"; 134 135 @Override onReceive(Context context, Intent intent)136 public void onReceive(Context context, Intent intent) { 137 if (ACTION_GET_PERMISSIONS_COUNT.equals(intent.getAction())) { 138 Intent responseIntent = new Intent(intent.getStringExtra( 139 EXTRA_GET_PERMISSIONS_RESPONSE_INTENT)); 140 responseIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 141 142 int[] counts = new int[3]; 143 ArrayList<CharSequence> grantedGroups = new ArrayList<>(); 144 boolean succeeded = false; 145 146 boolean isForPackage = intent.hasExtra(Intent.EXTRA_PACKAGE_NAME); 147 148 if (isForPackage) { 149 String pkg = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); 150 succeeded = getPermissionsCount(context, pkg, counts, grantedGroups); 151 } else { 152 succeeded = getAppsWithPermissionsCount(context, counts); 153 } 154 if (succeeded) { 155 responseIntent.putExtra(EXTRA_GET_PERMISSIONS_COUNT_RESULT, counts); 156 157 if (isForPackage) { 158 responseIntent.putExtra(EXTRA_GET_PERMISSIONS_GROUP_LIST_RESULT, 159 grantedGroups.toArray(new CharSequence[grantedGroups.size()])); 160 } 161 } 162 context.sendBroadcast(responseIntent); 163 } else if (ACTION_GET_PERMISSIONS_PACKAGES.equals(intent.getAction())) { 164 Intent responseIntent = new Intent(intent.getStringExtra( 165 EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT)); 166 responseIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 167 168 List<String> appsList = new ArrayList<>(); 169 List<CharSequence> appLabelsList = new ArrayList<>(); 170 List<Boolean> isSystemAppList = new ArrayList<>(); 171 if (getAppsWithRuntimePermissions(context, appsList, appLabelsList, isSystemAppList)) { 172 responseIntent.putExtra(EXTRA_GET_PERMISSIONS_APP_LIST_RESULT, 173 appsList.toArray(new String[appsList.size()])); 174 responseIntent.putExtra(EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT, 175 appLabelsList.toArray(new String[appLabelsList.size()])); 176 responseIntent.putExtra(EXTRA_GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT, 177 toPrimitiveBoolArray(isSystemAppList)); 178 } 179 context.sendBroadcast(responseIntent); 180 } 181 } 182 getPermissionsCount(Context context, String pkg, int[] counts, ArrayList<CharSequence> grantedGroups)183 public boolean getPermissionsCount(Context context, String pkg, int[] counts, 184 ArrayList<CharSequence> grantedGroups) { 185 try { 186 PackageInfo packageInfo = 187 context.getPackageManager().getPackageInfo(pkg, PackageManager.GET_PERMISSIONS); 188 AppPermissions appPermissions = 189 new AppPermissions(context, packageInfo, null, false, null); 190 int grantedCount = 0; 191 int totalCount = 0; 192 int additionalCount = 0; 193 194 for (AppPermissionGroup group : appPermissions.getPermissionGroups()) { 195 if (Utils.shouldShowPermission(group, pkg)) { 196 totalCount++; 197 if (group.areRuntimePermissionsGranted()) { 198 grantedCount++; 199 200 if (Utils.OS_PKG.equals(group.getDeclaringPackage())) { 201 grantedGroups.add(group.getLabel()); 202 } else { 203 additionalCount++; 204 } 205 } 206 } 207 } 208 209 // Sort 210 Collator coll = Collator.getInstance(); 211 coll.setStrength(Collator.PRIMARY); 212 Collections.sort(grantedGroups, coll); 213 214 // Set results 215 counts[0] = grantedCount; 216 counts[1] = totalCount; 217 counts[2] = additionalCount; 218 219 return true; 220 } catch (NameNotFoundException e) { 221 return false; 222 } 223 } 224 getAppsWithRuntimePermissions(Context context, List<String> appsList, List<CharSequence> appLabelsList, List<Boolean> isSystemAppList)225 public boolean getAppsWithRuntimePermissions(Context context, List<String> appsList, 226 List<CharSequence> appLabelsList, List<Boolean> isSystemAppList) { 227 final List<ApplicationInfo> appInfos = Utils.getAllInstalledApplications(context); 228 if (appInfos == null) { 229 return false; 230 } 231 final int appInfosSize = appInfos.size(); 232 try { 233 ArraySet<String> launcherPackages = Utils.getLauncherPackages(context); 234 for (int i = 0; i < appInfosSize; ++i) { 235 final String packageName = appInfos.get(i).packageName; 236 PackageInfo packageInfo = context.getPackageManager().getPackageInfo( 237 packageName, PackageManager.GET_PERMISSIONS); 238 AppPermissions appPermissions = 239 new AppPermissions(context, packageInfo, null, false, null); 240 241 boolean shouldShow = false; 242 for (AppPermissionGroup group : appPermissions.getPermissionGroups()) { 243 if (Utils.shouldShowPermission(group, packageName)) { 244 shouldShow = true; 245 break; 246 } 247 } 248 if (shouldShow) { 249 appsList.add(packageName); 250 appLabelsList.add(appPermissions.getAppLabel()); 251 isSystemAppList.add(Utils.isSystem(appPermissions, launcherPackages)); 252 } 253 } 254 } catch (NameNotFoundException e) { 255 return false; 256 } 257 258 return true; 259 } 260 getAppsWithPermissionsCount(Context context, int[] counts)261 public boolean getAppsWithPermissionsCount(Context context, int[] counts) { 262 ArraySet<String> launcherPkgs = Utils.getLauncherPackages(context); 263 // Indexed by uid. 264 ArrayMap<String, Boolean> grantedApps = new ArrayMap<>(); 265 ArrayMap<String, Boolean> allApps = new ArrayMap<>(); 266 for (String group : Utils.MODERN_PERMISSION_GROUPS) { 267 PermissionApps permissionApps = new PermissionApps(context, 268 group, null); 269 permissionApps.loadNowWithoutUi(); 270 for (PermissionApp app : permissionApps.getApps()) { 271 String key = app.getKey(); 272 if (Utils.isSystem(app, launcherPkgs)) { 273 // We default to not showing system apps, so hide them from count. 274 continue; 275 } 276 if (app.areRuntimePermissionsGranted()) { 277 grantedApps.put(key, true); 278 } 279 allApps.put(key, true); 280 } 281 } 282 counts[0] = grantedApps.size(); 283 counts[1] = allApps.size(); 284 return true; 285 } 286 toPrimitiveBoolArray(final List<Boolean> list)287 private boolean[] toPrimitiveBoolArray(final List<Boolean> list) { 288 final int count = list.size(); 289 final boolean[] result = new boolean[count]; 290 for (int i = 0; i < count; ++i) { 291 result[i] = list.get(i); 292 } 293 294 return result; 295 } 296 } 297