Merge changes from topic "SoftHardStoragePerm" into qt-dev
* changes: Link to SoftRestrictedPermissionPolicy twin Pre-Q storage perm should behave hard restricted Factor out soft restricted permissions policy
This commit is contained in:
committed by
Android (Google) Code Review
commit
7a9a83f6a1
@@ -97,6 +97,7 @@ import com.android.server.pm.SharedUserSetting;
|
||||
import com.android.server.pm.UserManagerService;
|
||||
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
|
||||
import com.android.server.pm.permission.PermissionsState.PermissionState;
|
||||
import com.android.server.policy.SoftRestrictedPermissionPolicy;
|
||||
|
||||
import libcore.util.EmptyArray;
|
||||
|
||||
@@ -2121,11 +2122,18 @@ public class PermissionManagerService {
|
||||
|
||||
if (bp.isHardRestricted()
|
||||
&& (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
|
||||
Log.e(TAG, "Cannot grant restricted non-exempt permission "
|
||||
Log.e(TAG, "Cannot grant hard restricted non-exempt permission "
|
||||
+ permName + " for package " + packageName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
|
||||
pkg.applicationInfo, permName).canBeGranted()) {
|
||||
Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
|
||||
+ packageName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bp.isDevelopment()) {
|
||||
// Development permissions must be handled specially, since they are not
|
||||
// normal runtime permissions. For now they apply to all users.
|
||||
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
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 {
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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 static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
|
||||
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
|
||||
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* The behavior of soft restricted permissions is different for each permission. This class collects
|
||||
* the policies in one place.
|
||||
*
|
||||
* This is the twin of
|
||||
* {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy}
|
||||
*/
|
||||
public abstract class SoftRestrictedPermissionPolicy {
|
||||
private static final String LOG_TAG = SoftRestrictedPermissionPolicy.class.getSimpleName();
|
||||
|
||||
private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
|
||||
FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
|
||||
| FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
|
||||
| FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeGranted() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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: {
|
||||
int flags = context.getPackageManager().getPermissionFlags(
|
||||
permission, appInfo.packageName, context.getUser());
|
||||
boolean applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
|
||||
boolean isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
|
||||
boolean hasRequestedLegacyExternalStorage =
|
||||
appInfo.hasRequestedLegacyExternalStorage();
|
||||
int targetSDK = appInfo.targetSdkVersion;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeGranted() {
|
||||
if (isWhiteListed || targetSDK >= Build.VERSION_CODES.Q) {
|
||||
return true;
|
||||
} else {
|
||||
Log.w(LOG_TAG, permission + " for " + appInfo.packageName
|
||||
+ " is not whitelisted and targetSDK " + targetSDK + "<"
|
||||
+ Build.VERSION_CODES.Q);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
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();
|
||||
|
||||
/**
|
||||
* @return If the permission can be granted
|
||||
*/
|
||||
public abstract boolean canBeGranted();
|
||||
}
|
||||
@@ -27,6 +27,14 @@
|
||||
"exclude-annotation": "androidx.test.filters.FlakyTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CtsPermission2TestCases",
|
||||
"options": [
|
||||
{
|
||||
"include-filter": "android.permission2.cts.RestrictedPermissionsTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"postsubmit": [
|
||||
|
||||
Reference in New Issue
Block a user