summaryrefslogtreecommitdiffstats
path: root/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java
diff options
context:
space:
mode:
authorSvet Ganov <svetoslavganov@google.com>2015-05-16 22:45:19 -0700
committerSvet Ganov <svetoslavganov@google.com>2015-05-17 00:01:15 -0700
commit267c2bd39c248eea2679c0a8efa334d3155e262c (patch)
tree74a752ae544c45fb8f4a6e0b1003c76703f68104 /src/com/android/packageinstaller/permission/model/AppPermissionGroup.java
parentd7f8200201563ca300572e2b56100acfd010140d (diff)
downloadandroid_packages_apps_PackageInstaller-267c2bd39c248eea2679c0a8efa334d3155e262c.tar.gz
android_packages_apps_PackageInstaller-267c2bd39c248eea2679c0a8efa334d3155e262c.tar.bz2
android_packages_apps_PackageInstaller-267c2bd39c248eea2679c0a8efa334d3155e262c.zip
Hide platform platform legacy permissions behind a menu option - package installer.
bug:21195624 Change-Id: If6de516d76969c3627316d091893da58f81af832
Diffstat (limited to 'src/com/android/packageinstaller/permission/model/AppPermissionGroup.java')
-rw-r--r--src/com/android/packageinstaller/permission/model/AppPermissionGroup.java535
1 files changed, 535 insertions, 0 deletions
diff --git a/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java b/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java
new file mode 100644
index 00000000..e3c9160a
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java
@@ -0,0 +1,535 @@
+/*
+ * 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.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+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.os.Build;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+
+import com.android.packageinstaller.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class AppPermissionGroup implements Comparable<AppPermissionGroup> {
+ private static final String PLATFORM_PACKAGE_NAME = "android";
+
+ private static final String KILL_REASON_APP_OP_CHANGE = "Permission related app op changed";
+
+ private final Context mContext;
+ private final UserHandle mUserHandle;
+ private final PackageManager mPackageManager;
+ private final AppOpsManager mAppOps;
+ private final ActivityManager mActivityManager;
+
+ private final PackageInfo mPackageInfo;
+ private final String mName;
+ private final String mDeclaringPackage;
+ private final CharSequence mLabel;
+ private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>();
+ private final String mIconPkg;
+ private final int mIconResId;
+
+ private final boolean mAppSupportsRuntimePermissions;
+
+ public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
+ String permissionName) {
+ PermissionInfo permissionInfo;
+ try {
+ permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+
+ if (permissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) {
+ return null;
+ }
+
+ PackageItemInfo groupInfo = permissionInfo;
+ if (permissionInfo.group != null) {
+ try {
+ groupInfo = context.getPackageManager().getPermissionGroupInfo(
+ permissionInfo.group, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ /* ignore */
+ }
+ }
+
+ List<PermissionInfo> permissionInfos = null;
+ if (groupInfo instanceof PermissionGroupInfo) {
+ try {
+ permissionInfos = context.getPackageManager().queryPermissionsByGroup(
+ groupInfo.name, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ /* ignore */
+ }
+ }
+
+ return create(context, packageInfo, groupInfo, permissionInfos);
+ }
+
+ public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
+ PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos) {
+
+ AppPermissionGroup group = new AppPermissionGroup(context, packageInfo, groupInfo.name,
+ groupInfo.packageName, groupInfo.loadLabel(context.getPackageManager()),
+ groupInfo.packageName, groupInfo.icon);
+
+ if (groupInfo instanceof PermissionInfo) {
+ permissionInfos = new ArrayList<>();
+ permissionInfos.add((PermissionInfo) groupInfo);
+ }
+
+ if (permissionInfos == null || permissionInfos.isEmpty()) {
+ return null;
+ }
+
+ final int permissionCount = packageInfo.requestedPermissions.length;
+ for (int i = 0; i < permissionCount; i++) {
+ String requestedPermission = packageInfo.requestedPermissions[i];
+
+ PermissionInfo requestedPermissionInfo = null;
+
+ for (PermissionInfo permissionInfo : permissionInfos) {
+ if (requestedPermission.equals(permissionInfo.name)) {
+ requestedPermissionInfo = permissionInfo;
+ break;
+ }
+ }
+
+ if (requestedPermissionInfo == null) {
+ continue;
+ }
+
+ // Collect only runtime permissions.
+ if (requestedPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) {
+ continue;
+ }
+
+ // Don't allow toggle of non platform defined permissions for legacy apps via app ops.
+ if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
+ && !PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)) {
+ continue;
+ }
+
+ final boolean granted = (packageInfo.requestedPermissionsFlags[i]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
+
+ final int appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
+ ? AppOpsManager.permissionToOpCode(requestedPermissionInfo.name)
+ : AppOpsManager.OP_NONE;
+
+ final boolean appOpAllowed = appOp != AppOpsManager.OP_NONE
+ && context.getSystemService(AppOpsManager.class).checkOp(appOp,
+ packageInfo.applicationInfo.uid, packageInfo.packageName)
+ == AppOpsManager.MODE_ALLOWED;
+
+ final int flags = context.getPackageManager().getPermissionFlags(
+ requestedPermission, packageInfo.packageName,
+ new UserHandle(context.getUserId()));
+
+ Permission permission = new Permission(requestedPermission, granted,
+ appOp, appOpAllowed, flags);
+ group.addPermission(permission);
+ }
+
+ return group;
+ }
+
+ private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
+ String declaringPackage, CharSequence label, String iconPkg, int iconResId) {
+ mContext = context;
+ mUserHandle = new UserHandle(mContext.getUserId());
+ mPackageManager = mContext.getPackageManager();
+ mPackageInfo = packageInfo;
+ mAppSupportsRuntimePermissions = packageInfo.applicationInfo
+ .targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mActivityManager = context.getSystemService(ActivityManager.class);
+ mDeclaringPackage = declaringPackage;
+ mName = name;
+ mLabel = label;
+ mIconPkg = iconPkg;
+ mIconResId = iconResId != 0 ? iconResId : R.drawable.ic_perm_device_info;
+ }
+
+ public boolean hasRuntimePermission() {
+ return mAppSupportsRuntimePermissions;
+ }
+
+ public boolean hasAppOpPermission() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.getAppOp() != AppOpsManager.OP_NONE) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public PackageInfo getApp() {
+ return mPackageInfo;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getDeclaringPackage() {
+ return mDeclaringPackage;
+ }
+
+ public String getIconPkg() {
+ return mIconPkg;
+ }
+
+ public int getIconResId() {
+ return mIconResId;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public boolean hasPermission(String permission) {
+ return mPermissions.get(permission) != null;
+ }
+
+ public boolean areRuntimePermissionsGranted() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (mAppSupportsRuntimePermissions) {
+ if (!permission.isGranted()) {
+ return false;
+ }
+ } else if (permission.isGranted() && permission.getAppOp()
+ != AppOpsManager.OP_NONE && !permission.isAppOpAllowed()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean grantRuntimePermissions(boolean fixedByTheUser) {
+ final boolean isSharedUser = mPackageInfo.sharedUserId != null;
+ final int uid = mPackageInfo.applicationInfo.uid;
+
+ // We toggle permissions only to apps that support runtime
+ // permissions, otherwise we toggle the app op corresponding
+ // to the permission if the permission is granted to the app.
+ for (Permission permission : mPermissions.values()) {
+ if (mAppSupportsRuntimePermissions) {
+ // Grant the permission if needed.
+ if (!permission.isGranted()) {
+ permission.setGranted(true);
+ mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
+ permission.getName(), new UserHandle(mContext.getUserId()));
+ }
+
+ // Update the permission flags.
+ if (!fixedByTheUser) {
+ // Now the apps can ask for the permission as the user
+ // no longer has it fixed in a denied state.
+ if (permission.isUserFixed() || !permission.isUserSet()) {
+ permission.setUserFixed(false);
+ permission.setUserSet(true);
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_USER_FIXED
+ | PackageManager.FLAG_PERMISSION_USER_SET,
+ PackageManager.FLAG_PERMISSION_USER_SET,
+ mUserHandle);
+ }
+ }
+
+ // Enable the permission app op.
+ if (permission.hasAppOp() && !permission.isAppOpAllowed()) {
+ permission.setAppOpAllowed(true);
+ mAppOps.setMode(permission.getAppOp(), android.os.Process.myUid(),
+ mPackageInfo.packageName, AppOpsManager.MODE_ALLOWED);
+ }
+ } else {
+ // Legacy apps cannot have a not granted permission but just in case.
+ // Also if the permissions has no corresponding app op, then it is a
+ // third-party one and we do not offer toggling of such permissions.
+ if (!permission.isGranted() || !permission.hasAppOp()) {
+ continue;
+ }
+
+ if (!permission.isAppOpAllowed()) {
+ permission.setAppOpAllowed(true);
+ // It this is a shared user we want to enable the app op for all
+ // packages in the shared user to match the behavior of this
+ // shared user having a runtime permission.
+ if (isSharedUser) {
+ // Enable the app op.
+ String[] packageNames = mPackageManager.getPackagesForUid(uid);
+ for (String packageName : packageNames) {
+ mAppOps.setMode(permission.getAppOp(), uid, packageName,
+ AppOpsManager.MODE_ALLOWED);
+ }
+ } else {
+ // Enable the app op.
+ mAppOps.setMode(permission.getAppOp(), uid, mPackageInfo.packageName,
+ AppOpsManager.MODE_ALLOWED);
+ }
+
+ // Mark that the permission should not be be granted on upgrade
+ // when the app begins supporting runtime permissions.
+ if (permission.shouldRevokeOnUpgrade()) {
+ permission.setRevokeOnUpgrade(false);
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE,
+ 0, mUserHandle);
+ }
+
+ // Legacy apps do not know that they have to retry access to a
+ // resource due to changes in runtime permissions (app ops in this
+ // case). Therefore, we restart them on app op change, so they
+ // can pick up the change.
+ mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public boolean revokeRuntimePermissions(boolean fixedByTheUser) {
+ final boolean isSharedUser = mPackageInfo.sharedUserId != null;
+ final int uid = mPackageInfo.applicationInfo.uid;
+
+ // We toggle permissions only to apps that support runtime
+ // permissions, otherwise we toggle the app op corresponding
+ // to the permission if the permission is granted to the app.
+ for (Permission permission : mPermissions.values()) {
+ if (mAppSupportsRuntimePermissions) {
+ // Revoke the permission if needed.
+ if (permission.isGranted()) {
+ permission.setGranted(false);
+ mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
+ permission.getName(), mUserHandle);
+ }
+
+ // Update the permission flags.
+ if (fixedByTheUser) {
+ // Take a note that the user fixed the permission.
+ if (permission.isUserSet() || !permission.isUserFixed()) {
+ permission.setUserSet(false);
+ permission.setUserFixed(true);
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_USER_SET
+ | PackageManager.FLAG_PERMISSION_USER_FIXED,
+ PackageManager.FLAG_PERMISSION_USER_FIXED,
+ mUserHandle);
+ }
+ } else {
+ if (!permission.isUserSet()) {
+ permission.setUserSet(true);
+ // Take a note that the user already chose once.
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_USER_SET,
+ PackageManager.FLAG_PERMISSION_USER_SET,
+ mUserHandle);
+ }
+ }
+
+ // Disable the permission app op.
+ if (permission.hasAppOp() && permission.isAppOpAllowed()) {
+ permission.setAppOpAllowed(false);
+ mAppOps.setMode(permission.getAppOp(), android.os.Process.myUid(),
+ mPackageInfo.packageName, AppOpsManager.MODE_IGNORED);
+ }
+ } else {
+ // Legacy apps cannot have a non-granted permission but just in case.
+ // Also if the permission has no corresponding app op, then it is a
+ // third-party one and we do not offer toggling of such permissions.
+ if (!permission.isGranted() || !permission.hasAppOp()) {
+ continue;
+ }
+
+ if (permission.isAppOpAllowed()) {
+ permission.setAppOpAllowed(false);
+ // It this is a shared user we want to enable the app op for all
+ // packages the the shared user to match the behavior of this
+ // shared user having a runtime permission.
+ if (isSharedUser) {
+ String[] packageNames = mPackageManager.getPackagesForUid(uid);
+ for (String packageName : packageNames) {
+ // Disable the app op.
+ mAppOps.setMode(permission.getAppOp(), uid,
+ packageName, AppOpsManager.MODE_IGNORED);
+ }
+ } else {
+ // Disable the app op.
+ mAppOps.setMode(permission.getAppOp(), uid,
+ mPackageInfo.packageName, AppOpsManager.MODE_IGNORED);
+ }
+
+ // Mark that the permission should not be granted on upgrade
+ // when the app begins supporting runtime permissions.
+ if (!permission.shouldRevokeOnUpgrade()) {
+ permission.setRevokeOnUpgrade(true);
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE,
+ PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE,
+ mUserHandle);
+ }
+
+ // Disabling an app op may put the app in a situation in which it
+ // has a handle to state it shouldn't have, so we have to kill the
+ // app. This matches the revoke runtime permission behavior.
+ mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public List<Permission> getPermissions() {
+ return new ArrayList<>(mPermissions.values());
+ }
+
+ public int getFlags() {
+ int flags = 0;
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ flags |= permission.getFlags();
+ }
+ return flags;
+ }
+
+ public boolean isUserFixed() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (!permission.isUserFixed()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isPolicyFixed() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isPolicyFixed()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isUserSet() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (!permission.isUserSet()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isSystemFixed() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isSystemFixed()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int compareTo(AppPermissionGroup another) {
+ final int result = mLabel.toString().compareTo(another.mLabel.toString());
+ if (result == 0) {
+ // Unbadged before badged.
+ return mPackageInfo.applicationInfo.uid
+ - another.mPackageInfo.applicationInfo.uid;
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ AppPermissionGroup other = (AppPermissionGroup) obj;
+
+ if (mName == null) {
+ if (other.mName != null) {
+ return false;
+ }
+ } else if (!mName.equals(other.mName)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mName != null ? mName.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getClass().getSimpleName());
+ builder.append("{name=").append(mName);
+ if (!mPermissions.isEmpty()) {
+ builder.append(", <has permissions>}");
+ } else {
+ builder.append('}');
+ }
+ return builder.toString();
+ }
+
+ private void addPermission(Permission permission) {
+ mPermissions.put(permission.getName(), permission);
+ }
+}