OSDN Git Service

Factor out soft restricted permissions policy
authorPhilip P. Moltmann <moltmann@google.com>
Wed, 29 May 2019 23:18:18 +0000 (16:18 -0700)
committerPhilip P. Moltmann <moltmann@google.com>
Mon, 3 Jun 2019 15:36:58 +0000 (08:36 -0700)
So that later we can add more policy to it.

Test: atest CtsPermission2TestCases:RestrictedPermissionsTest
Bug: 131188778
Change-Id: Idc8738cbdb418fa7494100309345660139f32373

services/core/java/com/android/server/policy/PermissionPolicyService.java
services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java [new file with mode: 0644]

index a799cd9..1d01a84 100644 (file)
 
 package com.android.server.policy;
 
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 
-import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -426,20 +430,34 @@ public final class PermissionPolicyService extends SystemService {
                     mOpsToAllowIfDefault.add(new OpToUnrestrict(uid, pkg.packageName, opCode));
                 }
             } else if (permissionInfo.isSoftRestricted()) {
-                // Storage uses a special app op to decide the mount state and
-                // supports soft restriction where the restricted state allows
-                // the permission but only for accessing the medial collections.
-                if (Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission)
-                        || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
-                    if (applyRestriction) {
-                        mOpsToDefault.add(new OpToRestrict(uid,
-                                AppOpsManager.OP_LEGACY_STORAGE));
-                    } else if (pkg.applicationInfo.hasRequestedLegacyExternalStorage()) {
-                        mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName,
-                                AppOpsManager.OP_LEGACY_STORAGE));
-                    } else {
-                        mOpsToIgnoreIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
-                                AppOpsManager.OP_LEGACY_STORAGE));
+                final SoftRestrictedPermissionPolicy policy =
+                        SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.applicationInfo,
+                                permission);
+
+                final int op = policy.getAppOp();
+                if (op != OP_NONE) {
+                    switch (policy.getAppOpMode()) {
+                        case MODE_DEFAULT:
+                            mOpsToDefault.add(new OpToRestrict(uid, op));
+                            break;
+                        case MODE_ALLOWED:
+                            if (policy.shouldSetAppOpIfNotDefault()) {
+                                mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName, op));
+                            } else {
+                                mOpsToAllowIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
+                                        op));
+                            }
+                            break;
+                        case MODE_IGNORED:
+                            if (policy.shouldSetAppOpIfNotDefault()) {
+                                Slog.wtf(LOG_TAG, "Always ignoring appops is not implemented");
+                            } else {
+                                mOpsToIgnoreIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
+                                        op));
+                            }
+                            break;
+                        case MODE_ERRORED:
+                            Slog.wtf(LOG_TAG, "Setting appop to errored is not implemented");
                     }
                 }
             }
@@ -483,7 +501,7 @@ public final class PermissionPolicyService extends SystemService {
 
             for (String permission : pkg.requestedPermissions) {
                 final int opCode = AppOpsManager.permissionToOpCode(permission);
-                if (opCode == AppOpsManager.OP_NONE) {
+                if (opCode == OP_NONE) {
                     continue;
                 }
 
@@ -515,13 +533,13 @@ public final class PermissionPolicyService extends SystemService {
                 @NonNull String packageName) {
             final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
                     .opToPublicName(opCode), uid, packageName);
-            if (currentMode == AppOpsManager.MODE_DEFAULT) {
+            if (currentMode == MODE_DEFAULT) {
                 mAppOpsManager.setUidMode(opCode, uid, mode);
             }
         }
 
         private void setUidModeDefault(int opCode, int uid) {
-            mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_DEFAULT);
+            mAppOpsManager.setUidMode(opCode, uid, MODE_DEFAULT);
         }
 
         private class OpToRestrict {
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
new file mode 100644 (file)
index 0000000..87dfe3e
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2019 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.server.policy;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+
+/**
+ * The behavior of soft restricted permissions is different for each permission. This class collects
+ * the policies in one place.
+ */
+public abstract class SoftRestrictedPermissionPolicy {
+    private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
+            new SoftRestrictedPermissionPolicy() {
+                @Override
+                public int getAppOp() {
+                    return OP_NONE;
+                }
+
+                @Override
+                public int getAppOpMode() {
+                    return MODE_DEFAULT;
+                }
+
+                @Override
+                public boolean shouldSetAppOpIfNotDefault() {
+                    return false;
+                }
+            };
+
+    /**
+     * Get the policy for a soft restricted permission.
+     *
+     * @param context A context to use
+     * @param appInfo The application the permission belongs to
+     * @param permission The name of the permission
+     *
+     * @return The policy for this permission
+     */
+    public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
+            @NonNull ApplicationInfo appInfo, @NonNull String permission) {
+        switch (permission) {
+            // Storage uses a special app op to decide the mount state and supports soft restriction
+            // where the restricted state allows the permission but only for accessing the medial
+            // collections.
+            case READ_EXTERNAL_STORAGE:
+            case WRITE_EXTERNAL_STORAGE: {
+                boolean applyRestriction = (context.getPackageManager().getPermissionFlags(
+                        permission, appInfo.packageName, context.getUser())
+                        & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+                boolean hasRequestedLegacyExternalStorage =
+                        appInfo.hasRequestedLegacyExternalStorage();
+
+                return new SoftRestrictedPermissionPolicy() {
+                    @Override
+                    public int getAppOp() {
+                        return OP_LEGACY_STORAGE;
+                    }
+
+                    @Override
+                    public int getAppOpMode() {
+                        if (applyRestriction) {
+                            return MODE_DEFAULT;
+                        } else if (hasRequestedLegacyExternalStorage) {
+                            return MODE_ALLOWED;
+                        } else {
+                            return MODE_IGNORED;
+                        }
+                    }
+
+                    @Override
+                    public boolean shouldSetAppOpIfNotDefault() {
+                        // Do not switch from allowed -> ignored as this would mean to retroactively
+                        // turn on isolated storage. This will make the app loose all its files.
+                        return getAppOpMode() != MODE_IGNORED;
+                    }
+                };
+            }
+            default:
+                return DUMMY_POLICY;
+        }
+    }
+
+    /**
+     * @return An app op to be changed based on the state of the permission or
+     * {@link AppOpsManager#OP_NONE} if not app-op should be set.
+     */
+    public abstract int getAppOp();
+
+    /**
+     * @return The mode the {@link #getAppOp() app op} should be in.
+     */
+    public abstract @AppOpsManager.Mode int getAppOpMode();
+
+    /**
+     * @return If the {@link #getAppOp() app op} should be set even if the app-op is currently not
+     * {@link AppOpsManager#MODE_DEFAULT}.
+     */
+    public abstract boolean shouldSetAppOpIfNotDefault();
+}