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.permissioncontroller.permission.model.legacy; 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.PermissionGroupInfo; 25 import android.content.pm.PermissionInfo; 26 import android.graphics.drawable.Drawable; 27 import android.os.AsyncTask; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.text.TextUtils; 31 import android.util.ArrayMap; 32 import android.util.Log; 33 import android.util.Pair; 34 import android.util.SparseArray; 35 36 import androidx.annotation.NonNull; 37 import androidx.annotation.Nullable; 38 39 import com.android.permissioncontroller.R; 40 import com.android.permissioncontroller.permission.model.AppPermissionGroup; 41 import com.android.permissioncontroller.permission.utils.Utils; 42 43 import java.util.ArrayList; 44 import java.util.Collections; 45 import java.util.List; 46 47 /** 48 * @deprecated Use classes from permission.ui.model instead 49 */ 50 @Deprecated 51 public class PermissionApps { 52 private static final String LOG_TAG = "PermissionApps"; 53 54 private final Context mContext; 55 private final String mGroupName; 56 private final String mPackageName; 57 private final PackageManager mPm; 58 private final Callback mCallback; 59 60 private final @Nullable PmCache mPmCache; 61 private final @Nullable AppDataCache mAppDataCache; 62 63 private CharSequence mLabel; 64 private CharSequence mFullLabel; 65 private Drawable mIcon; 66 private @Nullable CharSequence mDescription; 67 private List<PermissionApp> mPermApps; 68 // Map (pkg|uid) -> AppPermission 69 private ArrayMap<String, PermissionApp> mAppLookup; 70 71 private boolean mSkipUi; 72 private boolean mRefreshing; 73 PermissionApps(Context context, String groupName, Callback callback)74 public PermissionApps(Context context, String groupName, Callback callback) { 75 this(context, groupName, null, callback, null, null); 76 } 77 PermissionApps(Context context, String groupName, String packageName, Callback callback, @Nullable PmCache pmCache, @Nullable AppDataCache appDataCache)78 public PermissionApps(Context context, String groupName, String packageName, 79 Callback callback, @Nullable PmCache pmCache, @Nullable AppDataCache appDataCache) { 80 mPmCache = pmCache; 81 mAppDataCache = appDataCache; 82 mContext = context; 83 mPm = mContext.getPackageManager(); 84 mGroupName = groupName; 85 mCallback = callback; 86 mPackageName = packageName; 87 loadGroupInfo(); 88 } 89 getGroupName()90 public String getGroupName() { 91 return mGroupName; 92 } 93 94 /** 95 * Start an async refresh and call back the registered call back once done. 96 * 97 * @param getUiInfo If the UI info should be updated 98 */ refresh(boolean getUiInfo)99 public void refresh(boolean getUiInfo) { 100 if (!mRefreshing) { 101 mRefreshing = true; 102 mSkipUi = !getUiInfo; 103 new PermissionAppsLoader().execute(); 104 } 105 } 106 107 /** 108 * Refresh the state and do not return until it finishes. Should not be called while an {@link 109 * #refresh async referesh} is in progress. 110 */ refreshSync(boolean getUiInfo)111 public void refreshSync(boolean getUiInfo) { 112 mSkipUi = !getUiInfo; 113 createMap(loadPermissionApps()); 114 } 115 getGrantedCount()116 public int getGrantedCount() { 117 int count = 0; 118 for (PermissionApp app : mPermApps) { 119 if (!app.getAppInfo().enabled) { 120 continue; 121 } 122 if (!Utils.shouldShowPermission(mContext, app.getPermissionGroup())) { 123 continue; 124 } 125 if (!Utils.isGroupOrBgGroupUserSensitive(app.mAppPermissionGroup)) { 126 // We default to not showing system apps, so hide them from count. 127 continue; 128 } 129 if (app.areRuntimePermissionsGranted()) { 130 count++; 131 } 132 } 133 return count; 134 } 135 getTotalCount()136 public int getTotalCount() { 137 int count = 0; 138 for (PermissionApp app : mPermApps) { 139 if (!app.getAppInfo().enabled) { 140 continue; 141 } 142 if (!Utils.shouldShowPermission(mContext, app.getPermissionGroup())) { 143 continue; 144 } 145 if (!Utils.isGroupOrBgGroupUserSensitive(app.mAppPermissionGroup)) { 146 // We default to not showing system apps, so hide them from count. 147 continue; 148 } 149 count++; 150 } 151 return count; 152 } 153 getApps()154 public List<PermissionApp> getApps() { 155 return mPermApps; 156 } 157 getApp(String key)158 public PermissionApp getApp(String key) { 159 return mAppLookup.get(key); 160 } 161 getLabel()162 public CharSequence getLabel() { 163 return mLabel; 164 } 165 getFullLabel()166 public CharSequence getFullLabel() { 167 return mFullLabel; 168 } 169 getIcon()170 public Drawable getIcon() { 171 return mIcon; 172 } 173 getDescription()174 public CharSequence getDescription() { 175 return mDescription; 176 } 177 getPackageInfos(@onNull UserHandle user)178 private @NonNull List<PackageInfo> getPackageInfos(@NonNull UserHandle user) { 179 List<PackageInfo> apps = (mPmCache != null) ? mPmCache.getPackages( 180 user.getIdentifier()) : null; 181 if (apps != null) { 182 if (mPackageName != null) { 183 final int appCount = apps.size(); 184 for (int i = 0; i < appCount; i++) { 185 final PackageInfo app = apps.get(i); 186 if (mPackageName.equals(app.packageName)) { 187 apps = new ArrayList<>(1); 188 apps.add(app); 189 return apps; 190 } 191 } 192 } 193 return apps; 194 } 195 if (mPackageName == null) { 196 return mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, 197 user.getIdentifier()); 198 } else { 199 try { 200 final PackageInfo packageInfo = mPm.getPackageInfo(mPackageName, 201 PackageManager.GET_PERMISSIONS); 202 apps = new ArrayList<>(1); 203 apps.add(packageInfo); 204 return apps; 205 } catch (NameNotFoundException e) { 206 return Collections.emptyList(); 207 } 208 } 209 } 210 loadPermissionApps()211 private List<PermissionApp> loadPermissionApps() { 212 PackageItemInfo groupInfo = Utils.getGroupInfo(mGroupName, mContext); 213 if (groupInfo == null) { 214 return Collections.emptyList(); 215 } 216 217 List<PermissionInfo> groupPermInfos = Utils.getGroupPermissionInfos(mGroupName, mContext); 218 if (groupPermInfos == null) { 219 return Collections.emptyList(); 220 } 221 List<PermissionInfo> targetPermInfos = new ArrayList<PermissionInfo>(groupPermInfos.size()); 222 for (int i = 0; i < groupPermInfos.size(); i++) { 223 PermissionInfo permInfo = groupPermInfos.get(i); 224 if ((permInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 225 == PermissionInfo.PROTECTION_DANGEROUS 226 && (permInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0 227 && (permInfo.flags & PermissionInfo.FLAG_REMOVED) == 0) { 228 targetPermInfos.add(permInfo); 229 } 230 } 231 232 PackageManager packageManager = mContext.getPackageManager(); 233 CharSequence groupLabel = groupInfo.loadLabel(packageManager); 234 CharSequence fullGroupLabel = groupInfo.loadSafeLabel(packageManager, 0, 235 TextUtils.SAFE_STRING_FLAG_TRIM | TextUtils.SAFE_STRING_FLAG_FIRST_LINE); 236 237 ArrayList<PermissionApp> permApps = new ArrayList<>(); 238 239 UserManager userManager = mContext.getSystemService(UserManager.class); 240 for (UserHandle user : userManager.getUserProfiles()) { 241 List<PackageInfo> apps = getPackageInfos(user); 242 final int N = apps.size(); 243 for (int i = 0; i < N; i++) { 244 PackageInfo app = apps.get(i); 245 if (app.requestedPermissions == null) { 246 continue; 247 } 248 249 for (int j = 0; j < app.requestedPermissions.length; j++) { 250 String requestedPerm = app.requestedPermissions[j]; 251 252 PermissionInfo requestedPermissionInfo = null; 253 254 for (PermissionInfo groupPermInfo : targetPermInfos) { 255 if (requestedPerm.equals(groupPermInfo.name)) { 256 requestedPermissionInfo = groupPermInfo; 257 break; 258 } 259 } 260 261 if (requestedPermissionInfo == null) { 262 continue; 263 } 264 265 AppPermissionGroup group = AppPermissionGroup.create(mContext, 266 app, groupInfo, groupPermInfos, groupLabel, fullGroupLabel, false); 267 268 if (group == null) { 269 continue; 270 } 271 272 Pair<String, Drawable> appData = null; 273 if (mAppDataCache != null && !mSkipUi) { 274 appData = mAppDataCache.getAppData(user.getIdentifier(), 275 app.applicationInfo); 276 } 277 278 String label; 279 if (mSkipUi) { 280 label = app.packageName; 281 } else if (appData != null) { 282 label = appData.first; 283 } else { 284 label = app.applicationInfo.loadLabel(mPm).toString(); 285 } 286 287 Drawable icon = null; 288 if (!mSkipUi) { 289 if (appData != null) { 290 icon = appData.second; 291 } else { 292 icon = Utils.getBadgedIcon(mContext, app.applicationInfo); 293 } 294 } 295 296 PermissionApp permApp = new PermissionApp(app.packageName, group, label, icon, 297 app.applicationInfo); 298 299 permApps.add(permApp); 300 break; // move to the next app. 301 } 302 } 303 } 304 305 Collections.sort(permApps); 306 307 return permApps; 308 } 309 createMap(List<PermissionApp> result)310 private void createMap(List<PermissionApp> result) { 311 mAppLookup = new ArrayMap<>(); 312 for (PermissionApp app : result) { 313 mAppLookup.put(app.getKey(), app); 314 } 315 mPermApps = result; 316 } 317 loadGroupInfo()318 private void loadGroupInfo() { 319 PackageItemInfo info; 320 try { 321 info = mPm.getPermissionGroupInfo(mGroupName, 0); 322 } catch (PackageManager.NameNotFoundException e) { 323 try { 324 PermissionInfo permInfo = mPm.getPermissionInfo(mGroupName, 0); 325 if ((permInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) 326 != PermissionInfo.PROTECTION_DANGEROUS) { 327 Log.w(LOG_TAG, mGroupName + " is not a runtime permission"); 328 return; 329 } 330 info = permInfo; 331 } catch (NameNotFoundException reallyNotFound) { 332 Log.w(LOG_TAG, "Can't find permission: " + mGroupName, reallyNotFound); 333 return; 334 } 335 } 336 mLabel = info.loadLabel(mPm); 337 mFullLabel = info.loadSafeLabel(mPm, 0, 338 TextUtils.SAFE_STRING_FLAG_TRIM | TextUtils.SAFE_STRING_FLAG_FIRST_LINE); 339 if (info.icon != 0) { 340 mIcon = info.loadUnbadgedIcon(mPm); 341 } else { 342 mIcon = mContext.getDrawable(R.drawable.ic_perm_device_info); 343 } 344 mIcon = Utils.applyTint(mContext, mIcon, android.R.attr.colorControlNormal); 345 if (info instanceof PermissionGroupInfo) { 346 mDescription = ((PermissionGroupInfo) info).loadDescription(mPm); 347 } else if (info instanceof PermissionInfo) { 348 mDescription = ((PermissionInfo) info).loadDescription(mPm); 349 } 350 } 351 352 public static class PermissionApp implements Comparable<PermissionApp> { 353 private final String mPackageName; 354 private final AppPermissionGroup mAppPermissionGroup; 355 private String mLabel; 356 private Drawable mIcon; 357 private final ApplicationInfo mInfo; 358 PermissionApp(String packageName, AppPermissionGroup appPermissionGroup, String label, Drawable icon, ApplicationInfo info)359 public PermissionApp(String packageName, AppPermissionGroup appPermissionGroup, 360 String label, Drawable icon, ApplicationInfo info) { 361 mPackageName = packageName; 362 mAppPermissionGroup = appPermissionGroup; 363 mLabel = label; 364 mIcon = icon; 365 mInfo = info; 366 } 367 getAppInfo()368 public ApplicationInfo getAppInfo() { 369 return mInfo; 370 } 371 getKey()372 public String getKey() { 373 return mPackageName + getUid(); 374 } 375 getLabel()376 public String getLabel() { 377 return mLabel; 378 } 379 getIcon()380 public Drawable getIcon() { 381 return mIcon; 382 } 383 areRuntimePermissionsGranted()384 public boolean areRuntimePermissionsGranted() { 385 return mAppPermissionGroup.areRuntimePermissionsGranted(); 386 } 387 isReviewRequired()388 public boolean isReviewRequired() { 389 return mAppPermissionGroup.isReviewRequired(); 390 } 391 grantRuntimePermissions()392 public void grantRuntimePermissions() { 393 mAppPermissionGroup.grantRuntimePermissions(true, false); 394 } 395 revokeRuntimePermissions()396 public void revokeRuntimePermissions() { 397 mAppPermissionGroup.revokeRuntimePermissions(false); 398 } 399 isPolicyFixed()400 public boolean isPolicyFixed() { 401 return mAppPermissionGroup.isPolicyFixed(); 402 } 403 isSystemFixed()404 public boolean isSystemFixed() { 405 return mAppPermissionGroup.isSystemFixed(); 406 } 407 hasGrantedByDefaultPermissions()408 public boolean hasGrantedByDefaultPermissions() { 409 return mAppPermissionGroup.hasGrantedByDefaultPermission(); 410 } 411 doesSupportRuntimePermissions()412 public boolean doesSupportRuntimePermissions() { 413 return mAppPermissionGroup.doesSupportRuntimePermissions(); 414 } 415 getPackageName()416 public String getPackageName() { 417 return mPackageName; 418 } 419 getPermissionGroup()420 public AppPermissionGroup getPermissionGroup() { 421 return mAppPermissionGroup; 422 } 423 424 @Override compareTo(PermissionApp another)425 public int compareTo(PermissionApp another) { 426 final int result = mLabel.compareTo(another.mLabel); 427 if (result == 0) { 428 // Unbadged before badged. 429 return getKey().compareTo(another.getKey()); 430 } 431 return result; 432 } 433 getUid()434 public int getUid() { 435 return mAppPermissionGroup.getApp().applicationInfo.uid; 436 } 437 } 438 439 private class PermissionAppsLoader extends AsyncTask<Void, Void, List<PermissionApp>> { 440 441 @Override doInBackground(Void... args)442 protected List<PermissionApp> doInBackground(Void... args) { 443 return loadPermissionApps(); 444 } 445 446 @Override onPostExecute(List<PermissionApp> result)447 protected void onPostExecute(List<PermissionApp> result) { 448 mRefreshing = false; 449 createMap(result); 450 if (mCallback != null) { 451 mCallback.onPermissionsLoaded(PermissionApps.this); 452 } 453 } 454 } 455 456 /** 457 * Class used to reduce the number of calls to the package manager. 458 * This caches app information so it should only be used across parallel PermissionApps 459 * instances, and should not be retained across UI refresh. 460 */ 461 public static class PmCache { 462 private final SparseArray<List<PackageInfo>> mPackageInfoCache = new SparseArray<>(); 463 private final PackageManager mPm; 464 PmCache(PackageManager pm)465 public PmCache(PackageManager pm) { 466 mPm = pm; 467 } 468 getPackages(int userId)469 public synchronized List<PackageInfo> getPackages(int userId) { 470 List<PackageInfo> ret = mPackageInfoCache.get(userId); 471 if (ret == null) { 472 ret = mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, userId); 473 mPackageInfoCache.put(userId, ret); 474 } 475 return ret; 476 } 477 } 478 479 /** 480 * Class used to reduce the number of calls to loading labels and icons. 481 * This caches app information so it should only be used across parallel PermissionApps 482 * instances, and should not be retained across UI refresh. 483 */ 484 public static class AppDataCache { 485 private final @NonNull SparseArray<ArrayMap<String, Pair<String, Drawable>>> mCache = 486 new SparseArray<>(); 487 private final @NonNull PackageManager mPm; 488 private final @NonNull Context mContext; 489 AppDataCache(@onNull PackageManager pm, @NonNull Context context)490 public AppDataCache(@NonNull PackageManager pm, @NonNull Context context) { 491 mPm = pm; 492 mContext = context; 493 } 494 495 /** 496 * Get the label and icon for the given app. 497 * 498 * @param userId the user id. 499 * @param app The app 500 * 501 * @return a pair of the label and icon. 502 */ getAppData(int userId, @NonNull ApplicationInfo app)503 public @NonNull Pair<String, Drawable> getAppData(int userId, 504 @NonNull ApplicationInfo app) { 505 ArrayMap<String, Pair<String, Drawable>> dataForUser = mCache.get(userId); 506 if (dataForUser == null) { 507 dataForUser = new ArrayMap<>(); 508 mCache.put(userId, dataForUser); 509 } 510 Pair<String, Drawable> data = dataForUser.get(app.packageName); 511 if (data == null) { 512 data = Pair.create(app.loadLabel(mPm).toString(), 513 Utils.getBadgedIcon(mContext, app)); 514 dataForUser.put(app.packageName, data); 515 } 516 return data; 517 } 518 } 519 520 public interface Callback { onPermissionsLoaded(PermissionApps permissionApps)521 void onPermissionsLoaded(PermissionApps permissionApps); 522 } 523 } 524