/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.packageinstaller.permission.model; import android.app.LoaderManager; import android.app.LoaderManager.LoaderCallbacks; import android.content.AsyncTaskLoader; import android.content.Context; import android.content.Loader; import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.ArraySet; import com.android.packageinstaller.R; import com.android.packageinstaller.permission.utils.Utils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; public final class PermissionGroups implements LoaderCallbacks> { private final ArrayList mGroups = new ArrayList<>(); private final Context mContext; private final LoaderManager mLoaderManager; private final PermissionsGroupsChangeCallback mCallback; public interface PermissionsGroupsChangeCallback { public void onPermissionGroupsChanged(); } public PermissionGroups(Context context, LoaderManager loaderManager, PermissionsGroupsChangeCallback callback) { mContext = context; mLoaderManager = loaderManager; mCallback = callback; mLoaderManager.initLoader(0, null, this); } @Override public Loader> onCreateLoader(int id, Bundle args) { return new PermissionsLoader(mContext); } @Override public void onLoadFinished(Loader> loader, List groups) { if (mGroups.equals(groups)) { return; } mGroups.clear(); mGroups.addAll(groups); mCallback.onPermissionGroupsChanged(); } @Override public void onLoaderReset(Loader> loader) { mGroups.clear(); mCallback.onPermissionGroupsChanged(); } public List getGroups() { return mGroups; } public PermissionGroup getGroup(String name) { for (PermissionGroup group : mGroups) { if (group.getName().equals(name)) { return group; } } return null; } private static final class PermissionsLoader extends AsyncTaskLoader> implements PackageManager.OnPermissionsChangedListener { public PermissionsLoader(Context context) { super(context); } @Override protected void onStartLoading() { getContext().getPackageManager().addOnPermissionsChangeListener(this); forceLoad(); } @Override protected void onStopLoading() { getContext().getPackageManager().removeOnPermissionsChangeListener(this); } @Override public List loadInBackground() { ArraySet launcherPkgs = Utils.getLauncherPackages(getContext()); PermissionApps.PmCache pmCache = new PermissionApps.PmCache( getContext().getPackageManager()); List groups = new ArrayList<>(); Set seenPermissions = new ArraySet<>(); PackageManager packageManager = getContext().getPackageManager(); List groupInfos = packageManager.getAllPermissionGroups(0); for (PermissionGroupInfo groupInfo : groupInfos) { // Mare sure we respond to cancellation. if (isLoadInBackgroundCanceled()) { return Collections.emptyList(); } // Get the permissions in this group. final List groupPermissions; try { groupPermissions = packageManager.queryPermissionsByGroup(groupInfo.name, 0); } catch (PackageManager.NameNotFoundException e) { continue; } boolean hasRuntimePermissions = false; // Cache seen permissions and see if group has runtime permissions. for (PermissionInfo groupPermission : groupPermissions) { seenPermissions.add(groupPermission.name); if ((groupPermission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == PermissionInfo.PROTECTION_DANGEROUS && (groupPermission.flags & PermissionInfo.FLAG_INSTALLED) != 0 && (groupPermission.flags & PermissionInfo.FLAG_REMOVED) == 0) { hasRuntimePermissions = true; } } // No runtime permissions - not interesting for us. if (!hasRuntimePermissions) { continue; } CharSequence label = loadItemInfoLabel(groupInfo); Drawable icon = loadItemInfoIcon(groupInfo); PermissionApps permApps = new PermissionApps(getContext(), groupInfo.name, null, pmCache); permApps.refreshSync(); // Create the group and add to the list. PermissionGroup group = new PermissionGroup(groupInfo.name, groupInfo.packageName, label, icon, permApps.getTotalCount(launcherPkgs), permApps.getGrantedCount(launcherPkgs)); groups.add(group); } // Make sure we add groups for lone runtime permissions. List installedPackages = getContext().getPackageManager() .getInstalledPackages(PackageManager.GET_PERMISSIONS); // We will filter out permissions that no package requests. Set requestedPermissions = new ArraySet<>(); for (PackageInfo installedPackage : installedPackages) { if (installedPackage.requestedPermissions == null) { continue; } for (String requestedPermission : installedPackage.requestedPermissions) { requestedPermissions.add(requestedPermission); } } for (PackageInfo installedPackage : installedPackages) { if (installedPackage.permissions == null) { continue; } for (PermissionInfo permissionInfo : installedPackage.permissions) { // If we have handled this permission, no more work to do. if (!seenPermissions.add(permissionInfo.name)) { continue; } // We care only about installed runtime permissions. if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) != PermissionInfo.PROTECTION_DANGEROUS || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0) { continue; } // If no app uses this permission, if (!requestedPermissions.contains(permissionInfo.name)) { continue; } CharSequence label = loadItemInfoLabel(permissionInfo); Drawable icon = loadItemInfoIcon(permissionInfo); PermissionApps permApps = new PermissionApps(getContext(), permissionInfo.name, null, pmCache); permApps.refreshSync(); // Create the group and add to the list. PermissionGroup group = new PermissionGroup(permissionInfo.name, permissionInfo.packageName, label, icon, permApps.getTotalCount(launcherPkgs), permApps.getGrantedCount(launcherPkgs)); groups.add(group); } } Collections.sort(groups); return groups; } private CharSequence loadItemInfoLabel(PackageItemInfo itemInfo) { CharSequence label = itemInfo.loadLabel(getContext().getPackageManager()); if (label == null) { label = itemInfo.name; } return label; } private Drawable loadItemInfoIcon(PackageItemInfo itemInfo) { Drawable icon = null; if (itemInfo.icon > 0) { icon = Utils.loadDrawable(getContext().getPackageManager(), itemInfo.packageName, itemInfo.icon); } if (icon == null) { icon = getContext().getDrawable(R.drawable.ic_perm_device_info); } return icon; } @Override public void onPermissionsChanged(int uid) { forceLoad(); } } }