OSDN Git Service

Add permissions screen to advanced apps
authorJason Monk <jmonk@google.com>
Thu, 26 Mar 2015 12:30:50 +0000 (08:30 -0400)
committerJason Monk <jmonk@google.com>
Mon, 30 Mar 2015 14:39:29 +0000 (10:39 -0400)
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
res/xml/advanced_apps.xml
src/com/android/settings/applications/AdvancedAppSettings.java
src/com/android/settings/applications/ManagePermissions.java [new file with mode: 0644]
src/com/android/settings/applications/PermissionsInfo.java [new file with mode: 0644]

index d87d98f..033b568 100644 (file)
     <string name="keywords_keyboard_and_ime">text correction correct sound vibrate auto language gesture suggest suggestion theme offensive word type emoji international</string>
     <string name="keywords_reset_apps">reset preferences default</string>
     <string name="keywords_all_apps">apps download applications system</string>
+    <string name="keywords_app_permissions">apps permissions security</string>
     <!-- Search keywords for different screen unlock modes : slide to unlock, password, pattern and PIN [CHAR LIMIT=none] -->
     <string name="keywords_lockscreen">slide password pattern pin</string>
 
    <!-- Title for profile selection dialog [CHAR LIMIT=30] -->
    <string name="choose_profile">Choose Profile</string>
 
+   <!-- Label for list that shows all permissions -->
+   <string name="app_permissions">App permissions</string>
+   <!-- Summary of permissions currently granted to apps [CHAR LIMIT=45] -->
+   <string name="app_permissions_summary"><xliff:g id="count" example="10">%d</xliff:g> of <xliff:g id="count" example="10">%d</xliff:g> apps allowed additional access</string>
+   <!-- Summary of permissions currently granted to a single permission [CHAR
+        LIMIT=45] -->
+   <string name="app_permissions_group_summary"><xliff:g id="count" example="10">%d</xliff:g> of <xliff:g id="count" example="10">%d</xliff:g> apps allowed</string>
+
 </resources>
index a6cec13..e1759aa 100644 (file)
     android:key="applications_settings">
 
     <PreferenceScreen
+        android:key="manage_perms"
+        android:fragment="com.android.settings.applications.ManagePermissions"
+        android:title="@string/app_permissions"
+        settings:keywords="@string/keywords_app_permissions" />
+
+    <PreferenceScreen
         android:key="all_apps"
         android:fragment="com.android.settings.applications.ManageApplications"
         android:title="@string/filter_all_apps"
index 28ac1d0..d5ab8dc 100644 (file)
@@ -26,6 +26,7 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.net.NetworkPolicyManager;
 import android.os.AsyncTask;
@@ -34,9 +35,11 @@ import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceClickListener;
 import android.util.Log;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -47,22 +50,25 @@ import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.applications.ApplicationsState.AppEntry;
 import com.android.settings.applications.ApplicationsState.Callbacks;
 import com.android.settings.applications.ApplicationsState.Session;
+import com.android.settings.applications.PermissionsInfo.Callback;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class AdvancedAppSettings extends SettingsPreferenceFragment implements Callbacks,
-        DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+        DialogInterface.OnClickListener, DialogInterface.OnDismissListener, Callback {
 
     static final String TAG = "AdvancedAppSettings";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final String KEY_APP_PERM = "manage_perms";
     private static final String KEY_ALL_APPS = "all_apps";
     private static final String KEY_RESET_ALL = "reset_all";
     private static final String EXTRA_RESET_DIALOG = "resetDialog";
 
     private ApplicationsState mApplicationsState;
     private Session mSession;
+    private Preference mAppPerms;
     private Preference mAllApps;
     private Preference mResetAll;
 
@@ -75,6 +81,7 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C
     private NetworkPolicyManager mNpm;
     private AppOpsManager mAom;
     private Handler mHandler;
+    private PermissionsInfo mPermissionsInfo;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -84,6 +91,7 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C
         mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
         mSession = mApplicationsState.newSession(this);
 
+        mAppPerms = findPreference(KEY_APP_PERM);
         mAllApps = findPreference(KEY_ALL_APPS);
         mResetAll = findPreference(KEY_RESET_ALL);
         mResetAll.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@@ -136,6 +144,7 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C
     public void onResume() {
         super.onResume();
         mActivityResumed = true;
+        mPermissionsInfo = new PermissionsInfo(getActivity(), this);
     }
 
     @Override
@@ -265,4 +274,11 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C
         // No-op.
     }
 
+    @Override
+    public void onPermissionLoadComplete() {
+        mAppPerms.setSummary(getActivity().getString(R.string.app_permissions_summary,
+                mPermissionsInfo.getRuntimePermAppsGrantedCount(),
+                mPermissionsInfo.getRuntimePermAppsCount()));
+    }
+
 }
diff --git a/src/com/android/settings/applications/ManagePermissions.java b/src/com/android/settings/applications/ManagePermissions.java
new file mode 100644 (file)
index 0000000..8fb3e62
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.applications.PermissionsInfo.PermissionGroup;
+
+import java.util.List;
+
+public class ManagePermissions extends SettingsPreferenceFragment
+        implements PermissionsInfo.Callback, OnPreferenceClickListener {
+
+    private static final String TAG = "ManagePermissions";
+
+    private boolean mLoadComplete;
+    private PermissionsInfo mPermissionsInfo;
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        mPermissionsInfo = new PermissionsInfo(getActivity(), this);
+    }
+
+    private void refreshUi() {
+        PreferenceScreen screen = getPreferenceScreen();
+        if (screen == null) {
+            screen = getPreferenceManager().createPreferenceScreen(getActivity());
+            setPreferenceScreen(screen);
+        } else {
+            screen.removeAll();
+        }
+        final int count = screen.getPreferenceCount();
+        if (count == 0) {
+            List<PermissionGroup> 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 (file)
index 0000000..8656d1a
--- /dev/null
@@ -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<PermissionGroup> mGroups = new ArrayList<>();
+    private final Map<String, PermissionGroup> 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<PermissionGroup> 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<Void, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            List<PermissionGroupInfo> 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<PermissionInfo> 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<PackageInfo> 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<PermissionGroup> {
+        public final List<String> possibleApps = new ArrayList<>();
+        public final List<String> 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();
+    }
+
+}