summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/applications/AppStateAppOpsBridge.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/settings/applications/AppStateAppOpsBridge.java')
-rw-r--r--src/com/android/settings/applications/AppStateAppOpsBridge.java301
1 files changed, 301 insertions, 0 deletions
diff --git a/src/com/android/settings/applications/AppStateAppOpsBridge.java b/src/com/android/settings/applications/AppStateAppOpsBridge.java
new file mode 100644
index 0000000..20a00bd
--- /dev/null
+++ b/src/com/android/settings/applications/AppStateAppOpsBridge.java
@@ -0,0 +1,301 @@
+/*
+ * 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.Manifest;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.PackageOps;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.AppFilter;
+
+import java.util.Collection;
+import java.util.List;
+
+/*
+ * Connects app ops info to the ApplicationsState. Makes use of AppOpsManager to
+ * determine further permission level.
+ */
+public abstract class AppStateAppOpsBridge extends AppStateBaseBridge {
+
+ private static final String TAG = "AppStateAppOpsBridge";
+
+ private final IPackageManager mIPackageManager;
+ private final UserManager mUserManager;
+ private final List<UserHandle> mProfiles;
+ private final AppOpsManager mAppOpsManager;
+ private final Context mContext;
+ private final int[] mAppOpsOpCodes;
+ private final String[] mPermissions;
+
+ public AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback,
+ int appOpsOpCode, String permissionName) {
+ super(appState, callback);
+ mContext = context;
+ mIPackageManager = AppGlobals.getPackageManager();
+ mUserManager = UserManager.get(context);
+ mProfiles = mUserManager.getUserProfiles();
+ mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mAppOpsOpCodes = new int[] {appOpsOpCode};
+ mPermissions = new String[] {permissionName};
+ }
+
+ private boolean isThisUserAProfileOfCurrentUser(final int userId) {
+ final int profilesMax = mProfiles.size();
+ for (int i = 0; i < profilesMax; i++) {
+ if (mProfiles.get(i).getIdentifier() == userId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected abstract void updateExtraInfo(AppEntry app, String pkg, int uid);
+
+ public PermissionState getPermissionInfo(String pkg, int uid) {
+ PermissionState permissionState = new PermissionState(pkg, new UserHandle(UserHandle
+ .getUserId(uid)));
+ try {
+ permissionState.packageInfo = mIPackageManager.getPackageInfo(pkg,
+ PackageManager.GET_PERMISSIONS, permissionState.userHandle.getIdentifier());
+ // Check static permission state (whatever that is declared in package manifest)
+ String[] requestedPermissions = permissionState.packageInfo.requestedPermissions;
+ int[] permissionFlags = permissionState.packageInfo.requestedPermissionsFlags;
+ if (requestedPermissions != null) {
+ for (int i = 0; i < requestedPermissions.length; i++) {
+ if (mPermissions[0].equals(requestedPermissions[i]) &&
+ (permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
+ permissionState.permissionDeclared = true;
+ break;
+ }
+ }
+ }
+ // Check app op state.
+ List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, mAppOpsOpCodes);
+ if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) {
+ permissionState.appOpMode = ops.get(0).getOps().get(0).getMode();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "PackageManager is dead. Can't get package info " + pkg, e);
+ }
+ return permissionState;
+ }
+
+ @Override
+ protected void loadAllExtraInfo() {
+ SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
+
+ // Load state info.
+ loadPermissionsStates(entries);
+ loadAppOpsStates(entries);
+
+ // Map states to application info.
+ List<AppEntry> apps = mAppSession.getAllApps();
+ final int N = apps.size();
+ for (int i = 0; i < N; i++) {
+ AppEntry app = apps.get(i);
+ int userId = UserHandle.getUserId(app.info.uid);
+ ArrayMap<String, PermissionState> userMap = entries.get(userId);
+ app.extraInfo = userMap != null ? userMap.get(app.info.packageName) : null;
+ }
+ }
+
+ /*
+ * Gets a sparse array that describes every user on the device and all the associated packages
+ * of each user, together with the packages available for that user.
+ */
+ private SparseArray<ArrayMap<String, PermissionState>> getEntries() {
+ try {
+ final String[] packages = mIPackageManager.getAppOpPermissionPackages(mPermissions[0]);
+
+ if (packages == null) {
+ // No packages are requesting permission as specified by mPermissions.
+ return null;
+ }
+
+ // Create a sparse array that maps profileIds to an ArrayMap that maps package names to
+ // an associated PermissionState object
+ SparseArray<ArrayMap<String, PermissionState>> entries = new SparseArray<>();
+ for (final UserHandle profile : mProfiles) {
+ final ArrayMap<String, PermissionState> entriesForProfile = new ArrayMap<>();
+ final int profileId = profile.getIdentifier();
+ entries.put(profileId, entriesForProfile);
+ for (final String packageName : packages) {
+ final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName,
+ profileId);
+ if (!shouldIgnorePackage(packageName) && isAvailable) {
+ final PermissionState newEntry = new PermissionState(packageName, profile);
+ entriesForProfile.put(packageName, newEntry);
+ }
+ }
+ }
+
+ return entries;
+ } catch (RemoteException e) {
+ Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
+ + mPermissions[0], e);
+ return null;
+ }
+ }
+
+ /*
+ * This method will set the packageInfo and permissionDeclared field of the associated
+ * PermissionState, which describes a particular package.
+ */
+ private void loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
+ // Load the packages that have been granted the permission specified in mPermission.
+ try {
+ for (final UserHandle profile : mProfiles) {
+ final int profileId = profile.getIdentifier();
+ final ArrayMap<String, PermissionState> entriesForProfile = entries.get(profileId);
+ if (entriesForProfile == null) {
+ continue;
+ }
+ @SuppressWarnings("unchecked")
+ final List<PackageInfo> packageInfos = mIPackageManager
+ .getPackagesHoldingPermissions(mPermissions, 0, profileId).getList();
+ final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0;
+ for (int i = 0; i < packageInfoCount; i++) {
+ final PackageInfo packageInfo = packageInfos.get(i);
+ final PermissionState pe = entriesForProfile.get(packageInfo.packageName);
+ if (pe != null) {
+ pe.packageInfo = packageInfo;
+ pe.permissionDeclared = true;
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
+ + mPermissions[0], e);
+ return;
+ }
+ }
+
+ /*
+ * This method will set the appOpMode field of the associated PermissionState, which describes
+ * a particular package.
+ */
+ private void loadAppOpsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
+ // Find out which packages have been granted permission from AppOps.
+ final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
+ mAppOpsOpCodes);
+ final int packageOpsCount = packageOps != null ? packageOps.size() : 0;
+ for (int i = 0; i < packageOpsCount; i++) {
+ final AppOpsManager.PackageOps packageOp = packageOps.get(i);
+ final int userId = UserHandle.getUserId(packageOp.getUid());
+ if (!isThisUserAProfileOfCurrentUser(userId)) {
+ // This AppOp does not belong to any of this user's profiles.
+ continue;
+ }
+
+ final ArrayMap<String, PermissionState> entriesForProfile = entries.get(userId);
+ if (entriesForProfile == null) {
+ continue;
+ }
+ final PermissionState pe = entriesForProfile.get(packageOp.getPackageName());
+ if (pe == null) {
+ Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName()
+ + " of user " + userId + " but package doesn't exist or did not request "
+ + mPermissions[0] + " access");
+ continue;
+ }
+
+ if (packageOp.getOps().size() < 1) {
+ Log.w(TAG, "No AppOps permission exists for package " + packageOp.getPackageName());
+ continue;
+ }
+ pe.appOpMode = packageOp.getOps().get(0).getMode();
+ }
+ }
+
+ /*
+ * Check for packages that should be ignored for further processing
+ */
+ private boolean shouldIgnorePackage(String packageName) {
+ return packageName.equals("android") || packageName.equals(mContext.getPackageName());
+ }
+
+ public int getNumPackagesDeclaredPermission() {
+ SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
+ if (entries == null) {
+ return 0;
+ }
+ final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager
+ .getUserHandle());
+ if (entriesForProfile == null) {
+ return 0;
+ }
+ return entriesForProfile.size();
+ }
+
+ public int getNumPackagesAllowedByAppOps() {
+ SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
+ if (entries == null) {
+ return 0;
+ }
+ loadPermissionsStates(entries);
+ loadAppOpsStates(entries);
+ final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager
+ .getUserHandle());
+ if (entriesForProfile == null) {
+ return 0;
+ }
+ Collection<PermissionState> permStates = entriesForProfile.values();
+ int result = 0;
+ for (PermissionState permState : permStates) {
+ if (permState.isPermissible()) {
+ result++;
+ }
+ }
+ return result;
+ }
+
+ public static class PermissionState {
+ public final String packageName;
+ public final UserHandle userHandle;
+ public PackageInfo packageInfo;
+ public boolean permissionDeclared;
+ public int appOpMode;
+
+ public PermissionState(String packageName, UserHandle userHandle) {
+ this.packageName = packageName;
+ this.appOpMode = AppOpsManager.MODE_DEFAULT;
+ this.userHandle = userHandle;
+ }
+
+ public boolean isPermissible() {
+ // defining the default behavior as permissible as long as the package requested this
+ // permission (this means pre-M gets approval during install time; M apps gets approval
+ // during runtime.
+ if (appOpMode == AppOpsManager.MODE_DEFAULT) {
+ return permissionDeclared;
+ }
+ return appOpMode == AppOpsManager.MODE_ALLOWED;
+ }
+ }
+}