/* * 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; } @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 void refresh() { mLoaderManager.restartLoader(0, null, this); mLoaderManager.getLoader(0).forceLoad(); } 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> { public PermissionsLoader(Context context) { super(context); } @Override public List loadInBackground() { 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_DANGEROUS && (groupPermission.flags & PermissionInfo.FLAG_INSTALLED) != 0 && (groupPermission.flags & PermissionInfo.FLAG_HIDDEN) == 0) { hasRuntimePermissions = true; } } // No runtime permissions - not interesting for us. if (!hasRuntimePermissions) { continue; } CharSequence label = loadItemInfoLabel(groupInfo); Drawable icon = loadItemInfoIcon(groupInfo); // Create the group and add to the list. PermissionGroup group = new PermissionGroup(groupInfo.name, groupInfo.packageName, label, icon); 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_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); // Create the group and add to the list. PermissionGroup group = new PermissionGroup(permissionInfo.name, permissionInfo.packageName, label, icon); 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) { final Drawable icon; if (itemInfo.icon > 0) { icon = Utils.loadDrawable(getContext().getPackageManager(), itemInfo.packageName, itemInfo.icon); } else { icon = getContext().getDrawable(R.drawable.ic_perm_device_info); } return icon; } } }