summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java180
1 files changed, 130 insertions, 50 deletions
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 3011808d281c..67f30dc2e9fc 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -22,25 +22,28 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
+import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManagerInternal;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import com.android.internal.util.function.QuadConsumer;
-import com.android.internal.util.function.TriConsumer;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -51,11 +54,17 @@ import java.util.concurrent.CountDownLatch;
* and app ops - and vise versa.
*/
public final class PermissionPolicyService extends SystemService {
+ private static final String PLATFORM_PACKAGE = "android";
private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName();
+ // No need to lock as this is populated on boot when the OS is
+ // single threaded and is never mutated until a reboot.
+ private static final ArraySet<String> sAllRestrictedPermissions = new ArraySet<>();
+
public PermissionPolicyService(@NonNull Context context) {
super(context);
+ cacheAllRestrictedPermissions(context);
}
@Override
@@ -89,6 +98,20 @@ public final class PermissionPolicyService extends SystemService {
startWatchingRuntimePermissionChanges(getContext(), userId);
}
+ private static void cacheAllRestrictedPermissions(@NonNull Context context) {
+ try {
+ final PackageInfo packageInfo = context.getPackageManager()
+ .getPackageInfo(PLATFORM_PACKAGE, PackageManager.GET_PERMISSIONS);
+ for (PermissionInfo permissionInfo : packageInfo.permissions) {
+ if (permissionInfo.isRestricted()) {
+ sAllRestrictedPermissions.add(permissionInfo.name);
+ }
+ }
+ } catch (NameNotFoundException impossible) {
+ /* cannot happen */
+ }
+ }
+
private static void grantOrUpgradeDefaultRuntimePermissionsInNeeded(@NonNull Context context,
@UserIdInt int userId) {
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -123,16 +146,6 @@ public final class PermissionPolicyService extends SystemService {
}
}
- private static void onRestrictedPermissionEnabledChange(@NonNull Context context) {
- final PermissionManagerInternal permissionManagerInternal = LocalServices
- .getService(PermissionManagerInternal.class);
- final UserManagerInternal userManagerInternal = LocalServices.getService(
- UserManagerInternal.class);
- for (int userId : userManagerInternal.getUserIds()) {
- synchronizePermissionsAndAppOpsForUser(context, userId);
- }
- }
-
private static void startWatchingRuntimePermissionChanges(@NonNull Context context,
int userId) {
final PermissionManagerInternal permissionManagerInternal = LocalServices.getService(
@@ -149,40 +162,66 @@ public final class PermissionPolicyService extends SystemService {
@NonNull String packageName, @UserIdInt int userId) {
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
- final PackageParser.Package pkg = packageManagerInternal
- .getPackage(packageName);
- if (pkg != null) {
- PermissionToOpSynchronizer.syncPackage(context, pkg, userId);
+ final PackageParser.Package pkg = packageManagerInternal.getPackage(packageName);
+ if (pkg == null) {
+ return;
+ }
+ final PermissionToOpSynchroniser synchroniser = new PermissionToOpSynchroniser(context);
+ synchroniser.addPackage(context, pkg, userId);
+ final String[] sharedPkgNames = packageManagerInternal.getPackagesForSharedUserId(
+ pkg.mSharedUserId, userId);
+ if (sharedPkgNames != null) {
+ for (String sharedPkgName : sharedPkgNames) {
+ final PackageParser.Package sharedPkg = packageManagerInternal
+ .getPackage(sharedPkgName);
+ if (sharedPkg != null) {
+ synchroniser.addPackage(context, sharedPkg, userId);
+ }
+ }
}
+ synchroniser.syncPackages();
}
private static void synchronizePermissionsAndAppOpsForUser(@NonNull Context context,
@UserIdInt int userId) {
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
- final PermissionToOpSynchronizer synchronizer = new PermissionToOpSynchronizer(context);
+ final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(context);
packageManagerInternal.forEachPackage((pkg) ->
synchronizer.addPackage(context, pkg, userId));
synchronizer.syncPackages();
}
- private static class PermissionToOpSynchronizer {
+ /**
+ * Synchronizes permission to app ops. You *must* always sync all packages
+ * in a shared UID at the same time to ensure proper synchronization.
+ */
+ private static class PermissionToOpSynchroniser {
private final @NonNull Context mContext;
+ private final @NonNull SparseIntArray mUids = new SparseIntArray();
private final @NonNull SparseArray<String> mPackageNames = new SparseArray<>();
private final @NonNull SparseIntArray mAllowedUidOps = new SparseIntArray();
private final @NonNull SparseIntArray mDefaultUidOps = new SparseIntArray();
- PermissionToOpSynchronizer(@NonNull Context context) {
+ PermissionToOpSynchroniser(@NonNull Context context) {
mContext = context;
}
- private void addPackage(@NonNull Context context,
- @NonNull PackageParser.Package pkg, @UserIdInt int userId) {
- addPackage(context, pkg, userId, this::addAllowedEntry, this::addIgnoredEntry);
- }
-
void syncPackages() {
+ // TRICKY: we set the app op for a restricted permission to allow if the app
+ // requesting the permission is whitelisted and to deny if the app requesting
+ // the permission is not whitelisted. However, there is another case where an
+ // app in a shared user can access a component in another app in the same shared
+ // user due to being in the same shared user and not by having the permission
+ // that guards the component form the rest of the world. We need to handle this.
+ // The way we do this is by setting app ops corresponding to non requested
+ // restricted permissions to allow as this would allow the shared uid access
+ // case and be okay for other apps as they would not have the permission and
+ // would fail on the permission checks before reaching the app op check.
+ final SparseArray<List<String>> unrequestedRestrictedPermissionsForUid =
+ new SparseArray<>();
+
final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
final int allowedCount = mAllowedUidOps.size();
for (int i = 0; i < allowedCount; i++) {
@@ -190,31 +229,84 @@ public final class PermissionPolicyService extends SystemService {
final int uid = mAllowedUidOps.valueAt(i);
final String packageName = mPackageNames.valueAt(i);
setUidModeAllowed(appOpsManager, opCode, uid, packageName);
+
+ // Keep track this permission was requested by the UID.
+ List<String> unrequestedRestrictedPermissions =
+ unrequestedRestrictedPermissionsForUid.get(uid);
+ if (unrequestedRestrictedPermissions == null) {
+ unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions);
+ unrequestedRestrictedPermissionsForUid.put(uid,
+ unrequestedRestrictedPermissions);
+ }
+ unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(opCode));
+
+ mUids.delete(uid);
}
final int defaultCount = mDefaultUidOps.size();
for (int i = 0; i < defaultCount; i++) {
final int opCode = mDefaultUidOps.keyAt(i);
final int uid = mDefaultUidOps.valueAt(i);
setUidModeDefault(appOpsManager, opCode, uid);
+
+ // Keep track this permission was requested by the UID.
+ List<String> unrequestedRestrictedPermissions =
+ unrequestedRestrictedPermissionsForUid.get(uid);
+ if (unrequestedRestrictedPermissions == null) {
+ unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions);
+ unrequestedRestrictedPermissionsForUid.put(uid,
+ unrequestedRestrictedPermissions);
+ }
+ unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(opCode));
+
+ mUids.delete(uid);
}
- }
- static void syncPackage(@NonNull Context context, @NonNull PackageParser.Package pkg,
- @UserIdInt int userId) {
- addPackage(context, pkg, userId, PermissionToOpSynchronizer::setUidModeAllowed,
- PermissionToOpSynchronizer::setUidModeDefault);
+ // Give root access
+ mUids.put(Process.ROOT_UID, Process.ROOT_UID);
+
+ // Add records for UIDs that don't use any restricted permissions.
+ final int uidCount = mUids.size();
+ for (int i = 0; i < uidCount; i++) {
+ final int uid = mUids.keyAt(i);
+ unrequestedRestrictedPermissionsForUid.put(uid,
+ new ArrayList<>(sAllRestrictedPermissions));
+ }
+
+ // Flip ops for all unrequested restricted permission for the UIDs.
+ final int unrequestedUidCount = unrequestedRestrictedPermissionsForUid.size();
+ for (int i = 0; i < unrequestedUidCount; i++) {
+ final List<String> unrequestedRestrictedPermissions =
+ unrequestedRestrictedPermissionsForUid.valueAt(i);
+ if (unrequestedRestrictedPermissions != null) {
+ final int uid = unrequestedRestrictedPermissionsForUid.keyAt(i);
+ final String[] packageNames = (uid != Process.ROOT_UID)
+ ? mContext.getPackageManager().getPackagesForUid(uid)
+ : new String[] {"root"};
+ if (packageNames == null) {
+ continue;
+ }
+ final int permissionCount = unrequestedRestrictedPermissions.size();
+ for (int j = 0; j < permissionCount; j++) {
+ final String permission = unrequestedRestrictedPermissions.get(j);
+ for (String packageName : packageNames) {
+ setUidModeAllowed(appOpsManager,
+ AppOpsManager.permissionToOpCode(permission), uid,
+ packageName);
+ }
+ }
+ }
+ }
}
- private static void addPackage(@NonNull Context context,
- @NonNull PackageParser.Package pkg, @UserIdInt int userId,
- @NonNull QuadConsumer<AppOpsManager, Integer, Integer, String> allowedConsumer,
- @NonNull TriConsumer<AppOpsManager, Integer, Integer> defaultConsumer) {
+ private void addPackage(@NonNull Context context,
+ @NonNull PackageParser.Package pkg, @UserIdInt int userId) {
final PackageManager packageManager = context.getPackageManager();
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.applicationInfo.uid));
final UserHandle userHandle = UserHandle.of(userId);
+ mUids.put(uid, uid);
+
final int permissionCount = pkg.requestedPermissions.size();
for (int i = 0; i < permissionCount; i++) {
final String permission = pkg.requestedPermissions.get(i);
@@ -241,9 +333,10 @@ public final class PermissionPolicyService extends SystemService {
if (permissionInfo.isHardRestricted()) {
if (applyRestriction) {
- defaultConsumer.accept(appOpsManager, opCode, uid);
+ mDefaultUidOps.put(opCode, uid);
} else {
- allowedConsumer.accept(appOpsManager, opCode, uid, pkg.packageName);
+ mPackageNames.put(opCode, pkg.packageName);
+ mAllowedUidOps.put(opCode, uid);
}
} else if (permissionInfo.isSoftRestricted()) {
//TODO: Implement soft restrictions like storage here.
@@ -251,19 +344,6 @@ public final class PermissionPolicyService extends SystemService {
}
}
- @SuppressWarnings("unused")
- private void addAllowedEntry(@NonNull AppOpsManager appOpsManager, int opCode,
- int uid, @NonNull String packageName) {
- mPackageNames.put(opCode, packageName);
- mAllowedUidOps.put(opCode, uid);
- }
-
- @SuppressWarnings("unused")
- private void addIgnoredEntry(@NonNull AppOpsManager appOpsManager,
- int opCode, int uid) {
- mDefaultUidOps.put(opCode, uid);
- }
-
private static void setUidModeAllowed(@NonNull AppOpsManager appOpsManager,
int opCode, int uid, @NonNull String packageName) {
final int currentMode = appOpsManager.unsafeCheckOpRaw(AppOpsManager