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.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.PermissionInfo; 25 import android.graphics.drawable.Drawable; 26 import android.os.AsyncTask; 27 import android.os.Process; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.util.ArrayMap; 31 import android.util.ArraySet; 32 import android.util.Log; 33 import android.util.SparseArray; 34 35 import com.android.packageinstaller.R; 36 import com.android.packageinstaller.permission.utils.Utils; 37 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.List; 41 42 public class PermissionApps { 43 private static final String LOG_TAG = "PermissionApps"; 44 45 private final Context mContext; 46 private final String mGroupName; 47 private final PackageManager mPm; 48 private final Callback mCallback; 49 50 private final PmCache mCache; 51 52 private CharSequence mLabel; 53 private Drawable mIcon; 54 private List<PermissionApp> mPermApps; 55 // Map (pkg|uid) -> AppPermission 56 private ArrayMap<String, PermissionApp> mAppLookup; 57 58 private boolean mSkipUi; 59 private boolean mRefreshing; 60 61 public PermissionApps(Context context, String groupName, Callback callback) { 62 this(context, groupName, callback, null); 63 } 64 65 public PermissionApps(Context context, String groupName, Callback callback, PmCache cache) { 66 mCache = cache; 67 mContext = context; 68 mPm = mContext.getPackageManager(); 69 mGroupName = groupName; 70 mCallback = callback; 71 loadGroupInfo(); 72 } 73 74 public String getGroupName() { 75 return mGroupName; 76 } 77 78 public void loadNowWithoutUi() { 79 mSkipUi = true; 80 createMap(loadPermissionApps()); 81 } 82 83 public void refresh(boolean getUiInfo) { 84 if (!mRefreshing) { 85 mRefreshing = true; 86 mSkipUi = !getUiInfo; 87 new PermissionAppsLoader().execute(); 88 } 89 } 90 91 public int getGrantedCount(ArraySet<String> launcherPkgs) { 92 int count = 0; 93 for (PermissionApp app : mPermApps) { 94 if (!Utils.shouldShowPermission(app)) { 95 continue; 96 } 97 if (Utils.isSystem(app, launcherPkgs)) { 98 // We default to not showing system apps, so hide them from count. 99 continue; 100 } 101 if (app.areRuntimePermissionsGranted()) { 102 count++; 103 } 104 } 105 return count; 106 } 107 108 public int getTotalCount(ArraySet<String> launcherPkgs) { 109 int count = 0; 110 for (PermissionApp app : mPermApps) { 111 if (!Utils.shouldShowPermission(app)) { 112 continue; 113 } 114 if (Utils.isSystem(app, launcherPkgs)) { 115 // We default to not showing system apps, so hide them from count. 116 continue; 117 } 118 count++; 119 } 120 return count; 121 } 122 123 public List<PermissionApp> getApps() { 124 return mPermApps; 125 } 126 127 public PermissionApp getApp(String key) { 128 return mAppLookup.get(key); 129 } 130 131 public CharSequence getLabel() { 132 return mLabel; 133 } 134 135 public Drawable getIcon() { 136 return mIcon; 137 } 138 139 private List<PermissionApp> loadPermissionApps() { 140 PackageItemInfo groupInfo = getGroupInfo(mGroupName); 141 if (groupInfo == null) { 142 return Collections.emptyList(); 143 } 144 145 List<PermissionInfo> groupPermInfos = getGroupPermissionInfos(mGroupName); 146 if (groupPermInfos == null) { 147 return Collections.emptyList(); 148 } 149 150 ArrayList<PermissionApp> permApps = new ArrayList<>(); 151 152 UserManager userManager = mContext.getSystemService(UserManager.class); 153 for (UserHandle user : userManager.getUserProfiles()) { 154 List<PackageInfo> apps = mCache != null ? mCache.getPackages(user.getIdentifier()) 155 : mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, 156 user.getIdentifier()); 157 158 final int N = apps.size(); 159 for (int i = 0; i < N; i++) { 160 PackageInfo app = apps.get(i); 161 if (app.requestedPermissions == null) { 162 continue; 163 } 164 165 for (int j = 0; j < app.requestedPermissions.length; j++) { 166 String requestedPerm = app.requestedPermissions[j]; 167 168 PermissionInfo requestedPermissionInfo = null; 169 170 for (PermissionInfo groupPermInfo : groupPermInfos) { 171 if (requestedPerm.equals(groupPermInfo.name)) { 172 requestedPermissionInfo = groupPermInfo; 173 break; 174 } 175 } 176 177 if (requestedPermissionInfo == null) { 178 continue; 179 } 180 181 if (requestedPermissionInfo.protectionLevel 182 != PermissionInfo.PROTECTION_DANGEROUS 183 || (requestedPermissionInfo.flags 184 & PermissionInfo.FLAG_INSTALLED) == 0 185 || (requestedPermissionInfo.flags 186 & PermissionInfo.FLAG_REMOVED) != 0) { 187 continue; 188 } 189 190 AppPermissionGroup group = AppPermissionGroup.create(mContext, 191 app, groupInfo, groupPermInfos, user); 192 193 if (group == null) { 194 continue; 195 } 196 197 String label = mSkipUi ? app.packageName 198 : app.applicationInfo.loadLabel(mPm).toString(); 199 200 Drawable icon = null; 201 if (!mSkipUi) { 202 UserHandle userHandle = new UserHandle( 203 UserHandle.getUserId(group.getApp().applicationInfo.uid)); 204 205 icon = mPm.getUserBadgedIcon( 206 mPm.loadUnbadgedItemIcon(app.applicationInfo, app.applicationInfo), 207 userHandle); 208 } 209 210 PermissionApp permApp = new PermissionApp(app.packageName, group, label, icon, 211 app.applicationInfo); 212 213 permApps.add(permApp); 214 break; // move to the next app. 215 } 216 } 217 } 218 219 Collections.sort(permApps); 220 221 return permApps; 222 } 223 224 private void createMap(List<PermissionApp> result) { 225 mAppLookup = new ArrayMap<>(); 226 for (PermissionApp app : result) { 227 mAppLookup.put(app.getKey(), app); 228 } 229 mPermApps = result; 230 } 231 232 private PackageItemInfo getGroupInfo(String groupName) { 233 try { 234 return mContext.getPackageManager().getPermissionGroupInfo(groupName, 0); 235 } catch (NameNotFoundException e) { 236 /* ignore */ 237 } 238 try { 239 return mContext.getPackageManager().getPermissionInfo(groupName, 0); 240 } catch (NameNotFoundException e2) { 241 /* ignore */ 242 } 243 return null; 244 } 245 246 private List<PermissionInfo> getGroupPermissionInfos(String groupName) { 247 try { 248 return mContext.getPackageManager().queryPermissionsByGroup(groupName, 0); 249 } catch (NameNotFoundException e) { 250 /* ignore */ 251 } 252 try { 253 PermissionInfo permissionInfo = mContext.getPackageManager() 254 .getPermissionInfo(groupName, 0); 255 List<PermissionInfo> permissions = new ArrayList<>(); 256 permissions.add(permissionInfo); 257 return permissions; 258 } catch (NameNotFoundException e2) { 259 /* ignore */ 260 } 261 return null; 262 } 263 264 private void loadGroupInfo() { 265 PackageItemInfo info; 266 try { 267 info = mPm.getPermissionGroupInfo(mGroupName, 0); 268 } catch (PackageManager.NameNotFoundException e) { 269 try { 270 PermissionInfo permInfo = mPm.getPermissionInfo(mGroupName, 0); 271 if (permInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) { 272 Log.w(LOG_TAG, mGroupName + " is not a runtime permission"); 273 return; 274 } 275 info = permInfo; 276 } catch (NameNotFoundException reallyNotFound) { 277 Log.w(LOG_TAG, "Can't find permission: " + mGroupName, reallyNotFound); 278 return; 279 } 280 } 281 mLabel = info.loadLabel(mPm); 282 if (info.icon != 0) { 283 mIcon = info.loadUnbadgedIcon(mPm); 284 } else { 285 mIcon = mContext.getDrawable(R.drawable.ic_perm_device_info); 286 } 287 mIcon = Utils.applyTint(mContext, mIcon, android.R.attr.colorControlNormal); 288 } 289 290 public static class PermissionApp implements Comparable<PermissionApp> { 291 private final String mPackageName; 292 private final AppPermissionGroup mAppPermissionGroup; 293 private final String mLabel; 294 private final Drawable mIcon; 295 private final ApplicationInfo mInfo; 296 297 public PermissionApp(String packageName, AppPermissionGroup appPermissionGroup, 298 String label, Drawable icon, ApplicationInfo info) { 299 mPackageName = packageName; 300 mAppPermissionGroup = appPermissionGroup; 301 mLabel = label; 302 mIcon = icon; 303 mInfo = info; 304 } 305 306 public ApplicationInfo getAppInfo() { 307 return mInfo; 308 } 309 310 public String getKey() { 311 return mPackageName + getUid(); 312 } 313 314 public String getLabel() { 315 return mLabel; 316 } 317 318 public Drawable getIcon() { 319 return mIcon; 320 } 321 322 public boolean areRuntimePermissionsGranted() { 323 return mAppPermissionGroup.areRuntimePermissionsGranted(); 324 } 325 326 public boolean isReviewRequired() { 327 return mAppPermissionGroup.isReviewRequired(); 328 } 329 330 public void grantRuntimePermissions() { 331 mAppPermissionGroup.grantRuntimePermissions(false); 332 } 333 334 public void revokeRuntimePermissions() { 335 mAppPermissionGroup.revokeRuntimePermissions(false); 336 } 337 338 public boolean isPolicyFixed() { 339 return mAppPermissionGroup.isPolicyFixed(); 340 } 341 342 public boolean isSystemFixed() { 343 return mAppPermissionGroup.isSystemFixed(); 344 } 345 346 public boolean hasGrantedByDefaultPermissions() { 347 return mAppPermissionGroup.hasGrantedByDefaultPermission(); 348 } 349 350 public boolean hasRuntimePermissions() { 351 return mAppPermissionGroup.hasRuntimePermission(); 352 } 353 354 public int getUserId() { 355 return mAppPermissionGroup.getUserId(); 356 } 357 358 public String getPackageName() { 359 return mPackageName; 360 } 361 362 public AppPermissionGroup getPermissionGroup() { 363 return mAppPermissionGroup; 364 } 365 366 @Override 367 public int compareTo(PermissionApp another) { 368 final int result = mLabel.compareTo(another.mLabel); 369 if (result == 0) { 370 // Unbadged before badged. 371 return getKey().compareTo(another.getKey()); 372 } 373 return result; 374 } 375 376 public int getUid() { 377 return mAppPermissionGroup.getApp().applicationInfo.uid; 378 } 379 } 380 381 private class PermissionAppsLoader extends AsyncTask<Void, Void, List<PermissionApp>> { 382 383 @Override 384 protected List<PermissionApp> doInBackground(Void... args) { 385 return loadPermissionApps(); 386 } 387 388 @Override 389 protected void onPostExecute(List<PermissionApp> result) { 390 mRefreshing = false; 391 createMap(result); 392 if (mCallback != null) { 393 mCallback.onPermissionsLoaded(PermissionApps.this); 394 } 395 } 396 } 397 398 /** 399 * Class used to reduce the number of calls to the package manager. 400 * This caches app information so it should only be used across parallel PermissionApps 401 * instances, and should not be retained across UI refresh. 402 */ 403 public static class PmCache { 404 private final SparseArray<List<PackageInfo>> mPackageInfoCache = new SparseArray<>(); 405 private final PackageManager mPm; 406 407 public PmCache(PackageManager pm) { 408 mPm = pm; 409 } 410 411 public synchronized List<PackageInfo> getPackages(int userId) { 412 List<PackageInfo> ret = mPackageInfoCache.get(userId); 413 if (ret == null) { 414 ret = mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, userId); 415 mPackageInfoCache.put(userId, ret); 416 } 417 return ret; 418 } 419 } 420 421 public interface Callback { 422 void onPermissionsLoaded(PermissionApps permissionApps); 423 } 424 } 425