From 2f41aa7955e27a050155765803d3a5ae98782f26 Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Thu, 26 Mar 2015 08:30:50 -0400 Subject: [PATCH] Add permissions screen to advanced apps Add a screen that shows a list of permissions and how many apps have been granted them, link to PackageInstaller for control of the permissions. Depends on I68cdbe53177f742daf396f4eb53761fd5cda2636 Change-Id: I183848ea89ea41a17eaf663441dc4ef963be99bf --- res/values/strings.xml | 9 + res/xml/advanced_apps.xml | 6 + .../applications/AdvancedAppSettings.java | 18 +- .../applications/ManagePermissions.java | 117 +++++++++++ .../applications/PermissionsInfo.java | 188 ++++++++++++++++++ 5 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/applications/ManagePermissions.java create mode 100644 src/com/android/settings/applications/PermissionsInfo.java diff --git a/res/values/strings.xml b/res/values/strings.xml index d87d98fa73e..033b56863b1 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5638,6 +5638,7 @@ text correction correct sound vibrate auto language gesture suggest suggestion theme offensive word type emoji international reset preferences default apps download applications system + apps permissions security slide password pattern pin @@ -6136,4 +6137,12 @@ Choose Profile + + App permissions + + %d of %d apps allowed additional access + + %d of %d apps allowed + diff --git a/res/xml/advanced_apps.xml b/res/xml/advanced_apps.xml index a6cec1375e2..e1759aaa753 100644 --- a/res/xml/advanced_apps.xml +++ b/res/xml/advanced_apps.xml @@ -19,6 +19,12 @@ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" android:key="applications_settings"> + + groups = mPermissionsInfo.getGroups(); + for (PermissionGroup group : groups) { + if (group.possibleApps.size() == 0) continue; + PermissionPreference pref = new PermissionPreference(getActivity(), group); + pref.refreshUi(); + screen.addPreference(pref); + } + } else { + for (int i = 0; i < count; i++) { + ((PermissionPreference) screen.getPreference(i)).refreshUi(); + } + } + } + + @Override + public void onPermissionLoadComplete() { + refreshUi(); + } + + @Override + public boolean onPreferenceClick(Preference preference) { + return true; + } + + @Override + protected int getMetricsCategory() { + return MetricsLogger.MANAGE_PERMISSIONS; + } + + private class PermissionPreference extends Preference implements OnPreferenceClickListener { + private final PermissionGroup mGroup; + + public PermissionPreference(Context context, PermissionGroup group) { + super(context); + setOnPreferenceClickListener(this); + mGroup = group; + } + + public void refreshUi() { + setTitle(mGroup.label); + setIcon(mGroup.icon); + setSummary(getContext().getString(R.string.app_permissions_group_summary, + mGroup.grantedApps.size(), mGroup.possibleApps.size())); + } + + @Override + public boolean onPreferenceClick(Preference preference) { + Intent i = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS) + .putExtra(Intent.EXTRA_PERMISSION_NAME, mGroup.name); + try { + getActivity().startActivity(i); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "No app to handle " + i.getAction()); + } + return true; + } + } + +} diff --git a/src/com/android/settings/applications/PermissionsInfo.java b/src/com/android/settings/applications/PermissionsInfo.java new file mode 100644 index 00000000000..8656d1a8d79 --- /dev/null +++ b/src/com/android/settings/applications/PermissionsInfo.java @@ -0,0 +1,188 @@ +/* + * 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.settings.applications; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.os.AsyncTask; +import android.os.Build; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.ArrayMap; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class PermissionsInfo { + + private static final String TAG = "PermissionsInfo"; + + private final PackageManager mPm; + private final ArrayList mGroups = new ArrayList<>(); + private final Map mGroupLookup = new ArrayMap<>(); + private final Callback mCallback; + private final Context mContext; + // Count of apps that request runtime permissions. + private int mRuntimePermAppsCt; + // Count of apps that are granted runtime permissions. + private int mRuntimePermAppsGrantedCt; + + public PermissionsInfo(Context context, Callback callback) { + mContext = context; + mPm = context.getPackageManager(); + mCallback = callback; + new PermissionsLoader().execute(); + } + + public List getGroups() { + synchronized (mGroups) { + return new ArrayList<>(mGroups); + } + } + + public int getRuntimePermAppsCount() { + return mRuntimePermAppsCt; + } + + public int getRuntimePermAppsGrantedCount() { + return mRuntimePermAppsGrantedCt; + } + + private PermissionGroup getOrCreateGroup(String permission) { + PermissionGroup group = mGroupLookup.get(permission); + if (group == null) { + // Some permissions don't have a group, in that case treat them like a group + // and create their own PermissionGroup (only if they are runtime). + try { + PermissionInfo info = mPm.getPermissionInfo(permission, 0); + if (info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) { + group = new PermissionGroup(); + // TODO: Add default permission icon. + group.icon = info.icon != 0 ? info.loadIcon(mPm) : new ShapeDrawable(); + group.name = info.name; + group.label = info.loadLabel(mPm).toString(); + mGroups.add(group); + mGroupLookup.put(permission, group); + } + } catch (NameNotFoundException e) { + Log.w(TAG, "Unknown permission " + permission, e); + } + } + return group; + } + + private class PermissionsLoader extends AsyncTask { + + @Override + protected Void doInBackground(Void... params) { + List groups = + mPm.getAllPermissionGroups(PackageManager.GET_META_DATA); + // Get the groups. + for (PermissionGroupInfo groupInfo : groups) { + PermissionGroup group = new PermissionGroup(); + // TODO: Add default permission icon. + group.icon = groupInfo.icon != 0 ? groupInfo.loadIcon(mPm) : new ShapeDrawable(); + group.name = groupInfo.name; + group.label = groupInfo.loadLabel(mPm).toString(); + synchronized (mGroups) { + mGroups.add(group); + } + } + // Load permissions and which are runtime. + for (PermissionGroup group : mGroups) { + try { + List permissions = + mPm.queryPermissionsByGroup(group.name, 0); + for (PermissionInfo info : permissions) { + if (info.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) continue; + mGroupLookup.put(info.name, group); + } + } catch (NameNotFoundException e) { + Log.w(TAG, "Problem getting permissions", e); + } + } + // Load granted info. + for (UserHandle user : UserManager.get(mContext).getUserProfiles()) { + List allApps = mPm.getInstalledPackages( + PackageManager.GET_PERMISSIONS, user.getIdentifier()); + for (PackageInfo info : allApps) { + if (info.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1 + || info.requestedPermissions == null) { + continue; + } + final int N = info.requestedPermissionsFlags.length; + boolean appHasRuntimePerms = false; + boolean appGrantedRuntimePerms = false; + for (int i = 0; i < N; i++) { + boolean granted = (info.requestedPermissionsFlags[i] + & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0; + PermissionGroup group = getOrCreateGroup(info.requestedPermissions[i]); + String key = Integer.toString(info.applicationInfo.uid); + if (group != null && !group.possibleApps.contains(key)) { + appHasRuntimePerms = true; + group.possibleApps.add(key); + if (granted) { + appGrantedRuntimePerms = true; + group.grantedApps.add(key); + } + } + } + if (appHasRuntimePerms) { + mRuntimePermAppsCt++; + if (appGrantedRuntimePerms) { + mRuntimePermAppsGrantedCt++; + } + } + } + } + Collections.sort(mGroups); + + return null; + } + + @Override + protected void onPostExecute(Void result) { + mCallback.onPermissionLoadComplete(); + } + } + + public static class PermissionGroup implements Comparable { + public final List possibleApps = new ArrayList<>(); + public final List grantedApps = new ArrayList<>(); + public String name; + public String label; + public Drawable icon; + + @Override + public int compareTo(PermissionGroup another) { + return label.compareTo(another.label); + } + } + + public interface Callback { + void onPermissionLoadComplete(); + } + +}