blob: 09d469bc2aa91bef8135890cdf5e8e8eec19805d [file] [log] [blame]
/*
* 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.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import com.android.packageinstaller.permission.utils.Utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class PermissionApps {
private static final String LOG_TAG = "PermissionApps";
private final Context mContext;
private final String mGroupName;
private final PackageManager mPm;
private final Callback mCallback;
private final PmCache mCache;
private CharSequence mLabel;
private Drawable mIcon;
private List<PermissionApp> mPermApps;
// Map (pkg|uid) -> AppPermission
private ArrayMap<String, PermissionApp> mAppLookup;
private boolean mSkipUi;
private boolean mRefreshing;
public PermissionApps(Context context, String groupName, Callback callback) {
this(context, groupName, callback, null);
}
public PermissionApps(Context context, String groupName, Callback callback, PmCache cache) {
mCache = cache;
mContext = context;
mPm = mContext.getPackageManager();
mGroupName = groupName;
mCallback = callback;
loadGroupInfo();
}
public String getGroupName() {
return mGroupName;
}
public void loadNowWithoutUi() {
mSkipUi = true;
createMap(loadPermissionApps());
}
public void refresh(boolean getUiInfo) {
if (!mRefreshing) {
mRefreshing = true;
mSkipUi = !getUiInfo;
new PermissionAppsLoader().execute();
}
}
public int getGrantedCount(ArraySet<String> launcherPkgs) {
int count = 0;
for (PermissionApp app : mPermApps) {
if (!Utils.shouldShowPermission(app)) {
continue;
}
if (Utils.isSystem(app, launcherPkgs)) {
// We default to not showing system apps, so hide them from count.
continue;
}
if (app.areRuntimePermissionsGranted()) {
count++;
}
}
return count;
}
public int getTotalCount(ArraySet<String> launcherPkgs) {
int count = 0;
for (PermissionApp app : mPermApps) {
if (!Utils.shouldShowPermission(app)) {
continue;
}
if (Utils.isSystem(app, launcherPkgs)) {
// We default to not showing system apps, so hide them from count.
continue;
}
count++;
}
return count;
}
public Collection<PermissionApp> getApps() {
return mPermApps;
}
public PermissionApp getApp(String key) {
return mAppLookup.get(key);
}
public CharSequence getLabel() {
return mLabel;
}
public Drawable getIcon() {
return mIcon;
}
private List<PermissionApp> loadPermissionApps() {
PackageItemInfo groupInfo = getGroupInfo(mGroupName);
if (groupInfo == null) {
return Collections.emptyList();
}
List<PermissionInfo> groupPermInfos = getGroupPermissionInfos(mGroupName);
if (groupPermInfos == null) {
return Collections.emptyList();
}
ArrayList<PermissionApp> permApps = new ArrayList<>();
for (UserHandle user : UserManager.get(mContext).getUserProfiles()) {
List<PackageInfo> apps = mCache != null ? mCache.getPackages(user.getIdentifier())
: mPm.getInstalledPackages(PackageManager.GET_PERMISSIONS,
user.getIdentifier());
final int N = apps.size();
for (int i = 0; i < N; i++) {
PackageInfo app = apps.get(i);
if (app.requestedPermissions == null) {
continue;
}
for (int j = 0; j < app.requestedPermissions.length; j++) {
String requestedPerm = app.requestedPermissions[j];
PermissionInfo requestedPermissionInfo = null;
for (PermissionInfo groupPermInfo : groupPermInfos) {
if (requestedPerm.equals(groupPermInfo.name)) {
requestedPermissionInfo = groupPermInfo;
break;
}
}
if (requestedPermissionInfo == null) {
continue;
}
if (requestedPermissionInfo.protectionLevel
!= PermissionInfo.PROTECTION_DANGEROUS
|| (requestedPermissionInfo.flags
& PermissionInfo.FLAG_INSTALLED) == 0
|| (requestedPermissionInfo.flags
& PermissionInfo.FLAG_HIDDEN) != 0) {
continue;
}
AppPermissionGroup group = AppPermissionGroup.create(mContext,
app, groupInfo, groupPermInfos, user);
String label = mSkipUi ? app.packageName
: app.applicationInfo.loadLabel(mPm).toString();
PermissionApp permApp = new PermissionApp(app.packageName,
group, label, getBadgedIcon(app.applicationInfo),
app.applicationInfo);
permApps.add(permApp);
break; // move to the next app.
}
}
}
Collections.sort(permApps);
return permApps;
}
private void createMap(List<PermissionApp> result) {
mAppLookup = new ArrayMap<>();
for (PermissionApp app : result) {
mAppLookup.put(app.getKey(), app);
}
mPermApps = result;
}
private PackageItemInfo getGroupInfo(String groupName) {
try {
return mContext.getPackageManager().getPermissionGroupInfo(groupName, 0);
} catch (NameNotFoundException e) {
/* ignore */
}
try {
return mContext.getPackageManager().getPermissionInfo(groupName, 0);
} catch (NameNotFoundException e2) {
/* ignore */
}
return null;
}
private List<PermissionInfo> getGroupPermissionInfos(String groupName) {
try {
return mContext.getPackageManager().queryPermissionsByGroup(groupName, 0);
} catch (NameNotFoundException e) {
/* ignore */
}
try {
PermissionInfo permissionInfo = mContext.getPackageManager()
.getPermissionInfo(groupName, 0);
List<PermissionInfo> permissions = new ArrayList<>();
permissions.add(permissionInfo);
return permissions;
} catch (NameNotFoundException e2) {
/* ignore */
}
return null;
}
private Drawable getBadgedIcon(ApplicationInfo appInfo) {
if (mSkipUi) {
return null;
}
Drawable unbadged = appInfo.loadUnbadgedIcon(mPm);
return mPm.getUserBadgedIcon(unbadged,
new UserHandle(UserHandle.getUserId(appInfo.uid)));
}
private void loadGroupInfo() {
PackageItemInfo info;
try {
info = mPm.getPermissionGroupInfo(mGroupName, 0);
} catch (PackageManager.NameNotFoundException e) {
try {
PermissionInfo permInfo = mPm.getPermissionInfo(mGroupName, 0);
if (permInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) {
Log.w(LOG_TAG, mGroupName + " is not a runtime permission");
return;
}
info = permInfo;
} catch (NameNotFoundException reallyNotFound) {
Log.w(LOG_TAG, "Can't find permission: " + mGroupName, reallyNotFound);
return;
}
}
mLabel = info.loadLabel(mPm);
if (info.icon != 0) {
mIcon = info.loadUnbadgedIcon(mPm);
} else {
mIcon = mContext.getDrawable(com.android.internal.R.drawable.ic_perm_device_info);
}
mIcon = Utils.applyTint(mContext, mIcon, android.R.attr.colorControlNormal);
}
public static class PermissionApp implements Comparable<PermissionApp> {
private final String mPackageName;
private final AppPermissionGroup mAppPermissionGroup;
private final String mLabel;
private final Drawable mIcon;
private final ApplicationInfo mInfo;
public PermissionApp(String packageName, AppPermissionGroup appPermissionGroup,
String label, Drawable icon, ApplicationInfo info) {
mPackageName = packageName;
mAppPermissionGroup = appPermissionGroup;
mLabel = label;
mIcon = icon;
mInfo = info;
}
public ApplicationInfo getAppInfo() {
return mInfo;
}
public String getKey() {
return Integer.toString(getUid());
}
public String getLabel() {
return mLabel;
}
public Drawable getIcon() {
return mIcon;
}
public boolean areRuntimePermissionsGranted() {
return mAppPermissionGroup.areRuntimePermissionsGranted(null);
}
public void grantRuntimePermissions() {
mAppPermissionGroup.grantRuntimePermissions(false, null);
}
public void revokeRuntimePermissions() {
mAppPermissionGroup.revokeRuntimePermissions(false, null);
}
public boolean isPolicyFixed() {
return mAppPermissionGroup.isPolicyFixed();
}
public boolean isSystemFixed() {
return mAppPermissionGroup.isSystemFixed();
}
public boolean hasGrantedByDefaultPermissions() {
return mAppPermissionGroup.hasGrantedByDefaultPermission();
}
public boolean hasRuntimePermissions() {
return mAppPermissionGroup.hasRuntimePermission();
}
public boolean hasAppOpPermissions() {
return mAppPermissionGroup.hasAppOpPermission();
}
public String getPackageName() {
return mPackageName;
}
public AppPermissionGroup getPermissionGroup() {
return mAppPermissionGroup;
}
@Override
public int compareTo(PermissionApp another) {
final int result = mLabel.compareTo(another.mLabel);
if (result == 0) {
// Unbadged before badged.
return getUid() - another.getUid();
}
return result;
}
public int getUid() {
return mAppPermissionGroup.getApp().applicationInfo.uid;
}
}
private class PermissionAppsLoader extends AsyncTask<Void, Void, List<PermissionApp>> {
@Override
protected List<PermissionApp> doInBackground(Void... args) {
return loadPermissionApps();
}
@Override
protected void onPostExecute(List<PermissionApp> result) {
mRefreshing = false;
createMap(result);
if (mCallback != null) {
mCallback.onPermissionsLoaded(PermissionApps.this);
}
}
}
/**
* Class used to reduce the number of calls to the package manager.
* This caches app information so it should only be used across parallel PermissionApps
* instances, and should not be retained across UI refresh.
*/
public static class PmCache {
private final SparseArray<List<PackageInfo>> mPackageInfoCache = new SparseArray<>();
private final PackageManager mPm;
public PmCache(PackageManager pm) {
mPm = pm;
}
public synchronized List<PackageInfo> getPackages(int userId) {
List<PackageInfo> ret = mPackageInfoCache.get(userId);
if (ret == null) {
ret = mPm.getInstalledPackages(PackageManager.GET_PERMISSIONS, userId);
mPackageInfoCache.put(userId, ret);
}
return ret;
}
}
public interface Callback {
void onPermissionsLoaded(PermissionApps permissionApps);
}
}