From 08847a5eea6ae62a1af0af5caded90cb1e968c07 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Thu, 22 Dec 2022 11:46:49 -0800 Subject: [PATCH] Fix issues in the new permission & app op subsystems. And boot with it. - Added logging decorators for permission and app op interfaces. - Worked around a bug in DefaultPermissionGrantPoicy that may grant permission unrequested by a package in a shared UID. - Reverted to checking user ID existence first inside PermissionService to avoid breaking existing code that may pass user -1. - Regathered known packages in package callbacks. - Added async persistence code according to existing permission persistence. - Other bug fixes. Bug: 263504888 Test: device boots with the new subsystems enabled Change-Id: I04517014c4a97506a35bb79513d3accd119dc5bf --- ...AppOpsCheckingServiceLoggingDecorator.java | 185 ++++++++ .../android/server/appop/AppOpsService.java | 2 + .../DefaultPermissionGrantPolicy.java | 11 +- .../permission/PermissionManagerService.java | 4 +- ...missionManagerServiceLoggingDecorator.java | 433 ++++++++++++++++++ .../access/AccessCheckingService.kt | 100 ++-- .../permission/access/AccessPersistence.kt | 93 +++- .../server/permission/access/AccessPolicy.kt | 32 +- .../access/collection/IntLongMap.kt | 171 +++++++ .../access/permission/Permission.kt | 2 +- .../access/permission/PermissionService.kt | 94 ++-- .../access/permission/UidPermissionPolicy.kt | 44 +- 12 files changed, 1064 insertions(+), 107 deletions(-) create mode 100644 services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java create mode 100644 services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java create mode 100644 services/permission/java/com/android/server/permission/access/collection/IntLongMap.kt diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java new file mode 100644 index 0000000000000..ac479b2a63c4a --- /dev/null +++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2022 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.appop; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.ArraySet; +import android.util.Log; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; + +import java.io.PrintWriter; + +/** + * Logging decorator for {@link AppOpsCheckingServiceInterface}. + */ +public class AppOpsCheckingServiceLoggingDecorator implements AppOpsCheckingServiceInterface { + private static final String LOG_TAG = + AppOpsCheckingServiceLoggingDecorator.class.getSimpleName(); + + @NonNull + private final AppOpsCheckingServiceInterface mService; + + public AppOpsCheckingServiceLoggingDecorator(@NonNull AppOpsCheckingServiceInterface service) { + mService = service; + } + + @Override + public SparseIntArray getNonDefaultUidModes(int uid) { + Log.i(LOG_TAG, "getNonDefaultUidModes(uid = " + uid + ")"); + return mService.getNonDefaultUidModes(uid); + } + + @Override + public int getUidMode(int uid, int op) { + Log.i(LOG_TAG, "getUidMode(uid = " + uid + ", op = " + op + ")"); + return mService.getUidMode(uid, op); + } + + @Override + public boolean setUidMode(int uid, int op, int mode) { + Log.i(LOG_TAG, "setUidMode(uid = " + uid + ", op = " + op + ", mode = " + mode + ")"); + return mService.setUidMode(uid, op, mode); + } + + @Override + public int getPackageMode(@NonNull String packageName, int op, int userId) { + Log.i(LOG_TAG, "getPackageMode(packageName = " + packageName + ", op = " + op + + ", userId = " + userId + ")"); + return mService.getPackageMode(packageName, op, userId); + } + + @Override + public void setPackageMode(@NonNull String packageName, int op, int mode, int userId) { + Log.i(LOG_TAG, "setPackageMode(packageName = " + packageName + ", op = " + op + ", mode = " + + mode + ", userId = " + userId + ")"); + mService.setPackageMode(packageName, op, mode, userId); + } + + @Override + public boolean removePackage(@NonNull String packageName, int userId) { + Log.i(LOG_TAG, "removePackage(packageName = " + packageName + ", userId = " + userId + ")"); + return mService.removePackage(packageName, userId); + } + + @Override + public void removeUid(int uid) { + Log.i(LOG_TAG, "removeUid(uid = " + uid + ")"); + mService.removeUid(uid); + } + + @Override + public boolean areUidModesDefault(int uid) { + Log.i(LOG_TAG, "areUidModesDefault(uid = " + uid + ")"); + return mService.areUidModesDefault(uid); + } + + @Override + public boolean arePackageModesDefault(String packageName, int userId) { + Log.i(LOG_TAG, "arePackageModesDefault(packageName = " + packageName + ", userId = " + + userId + ")"); + return mService.arePackageModesDefault(packageName, userId); + } + + @Override + public void clearAllModes() { + Log.i(LOG_TAG, "clearAllModes()"); + mService.clearAllModes(); + } + + @Override + public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, + int op) { + Log.i(LOG_TAG, "startWatchingOpModeChanged(changedListener = " + changedListener + ", op = " + + op + ")"); + mService.startWatchingOpModeChanged(changedListener, op); + } + + @Override + public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener, + @NonNull String packageName) { + Log.i(LOG_TAG, "startWatchingPackageModeChanged(changedListener = " + changedListener + + ", packageName = " + packageName + ")"); + mService.startWatchingPackageModeChanged(changedListener, packageName); + } + + @Override + public void removeListener(@NonNull OnOpModeChangedListener changedListener) { + Log.i(LOG_TAG, "removeListener(changedListener = " + changedListener + ")"); + mService.removeListener(changedListener); + } + + @Override + public ArraySet getOpModeChangedListeners(int op) { + Log.i(LOG_TAG, "getOpModeChangedListeners(op = " + op + ")"); + return mService.getOpModeChangedListeners(op); + } + + @Override + public ArraySet getPackageModeChangedListeners( + @NonNull String packageName) { + Log.i(LOG_TAG, "getPackageModeChangedListeners(packageName = " + packageName + ")"); + return mService.getPackageModeChangedListeners(packageName); + } + + @Override + public void notifyWatchersOfChange(int op, int uid) { + Log.i(LOG_TAG, "notifyWatchersOfChange(op = " + op + ", uid = " + uid + ")"); + mService.notifyWatchersOfChange(op, uid); + } + + @Override + public void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid, + @Nullable String packageName) { + Log.i(LOG_TAG, "notifyOpChanged(changedListener = " + changedListener + ", op = " + op + + ", uid = " + uid + ", packageName = " + packageName + ")"); + mService.notifyOpChanged(changedListener, op, uid, packageName); + } + + @Override + public void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground, + @Nullable OnOpModeChangedListener callbackToIgnore) { + Log.i(LOG_TAG, "notifyOpChangedForAllPkgsInUid(op = " + op + ", uid = " + uid + + ", onlyForeground = " + onlyForeground + ", callbackToIgnore = " + + callbackToIgnore + ")"); + mService.notifyOpChangedForAllPkgsInUid(op, uid, onlyForeground, callbackToIgnore); + } + + @Override + public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) { + Log.i(LOG_TAG, "evalForegroundUidOps(uid = " + uid + ", foregroundOps = " + foregroundOps + + ")"); + return mService.evalForegroundUidOps(uid, foregroundOps); + } + + @Override + public SparseBooleanArray evalForegroundPackageOps(String packageName, + SparseBooleanArray foregroundOps, int userId) { + Log.i(LOG_TAG, "evalForegroundPackageOps(packageName = " + packageName + + ", foregroundOps = " + foregroundOps + ", userId = " + userId + ")"); + return mService.evalForegroundPackageOps(packageName, foregroundOps, userId); + } + + @Override + public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, + PrintWriter printWriter) { + Log.i(LOG_TAG, "dumpListeners(dumpOp = " + dumpOp + ", dumpUid = " + dumpUid + + ", dumpPackage = " + dumpPackage + ", printWriter = " + printWriter + ")"); + return mService.dumpListeners(dumpOp, dumpUid, dumpPackage, printWriter); + } +} diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 18839a80640d7..9c6cae3552251 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -956,6 +956,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch } mAppOpsCheckingService = new AppOpsCheckingServiceImpl(this, this, handler, context, mSwitchedOps); + //mAppOpsCheckingService = new AppOpsCheckingServiceLoggingDecorator( + // LocalServices.getService(AppOpsCheckingServiceInterface.class)); mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler, mAppOpsCheckingService); diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 83e17a5a90754..58f88c3243546 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1800,6 +1800,15 @@ final class DefaultPermissionGrantPolicy { PermissionState permState; if (permIdx >= 0) { permState = uidState.valueAt(permIdx); + // Quick and dirty fix for shared UID packages - we should grant permission with the + // correct package even if a previous checkPermission() used a package that isn't + // requesting the permission. Ideally we should use package manager snapshot and get + // rid of this entire inner class. + if (!ArrayUtils.contains(permState.mPkgRequestingPerm.requestedPermissions, + permission) && ArrayUtils.contains(pkg.requestedPermissions, + permission)) { + permState.mPkgRequestingPerm = pkg; + } } else { permState = new PermissionState(permission, pkg, user); uidState.put(permission, permState); @@ -1887,7 +1896,7 @@ final class DefaultPermissionGrantPolicy { */ private class PermissionState { private final @NonNull String mPermission; - private final @NonNull PackageInfo mPkgRequestingPerm; + private @NonNull PackageInfo mPkgRequestingPerm; private final @NonNull UserHandle mUser; /** Permission flags when the state was created */ diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index a949f7545f235..268a36f39a607 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -119,7 +119,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { private final AppOpsManager mAppOpsManager; private final Context mContext; - private final PermissionManagerServiceImpl mPermissionManagerServiceImpl; + private final PermissionManagerServiceInterface mPermissionManagerServiceImpl; @NonNull private final AttributionSourceRegistry mAttributionSourceRegistry; @@ -152,6 +152,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { mPermissionManagerServiceImpl = new PermissionManagerServiceImpl(context, availableFeatures); + //mPermissionManagerServiceImpl = new PermissionManagerServiceLoggingDecorator( + // LocalServices.getService(PermissionManagerServiceInterface.class)); } /** diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java new file mode 100644 index 0000000000000..bfe0008bc9e3f --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2022 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.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.permission.SplitPermissionInfoParcelable; +import android.permission.IOnPermissionsChangeListener; +import android.util.Log; + +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageState; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Logging decorator for {@link PermissionManagerServiceInterface}. + */ +public class PermissionManagerServiceLoggingDecorator implements PermissionManagerServiceInterface { + private static final String LOG_TAG = + PermissionManagerServiceLoggingDecorator.class.getSimpleName(); + + @NonNull + private final PermissionManagerServiceInterface mService; + + public PermissionManagerServiceLoggingDecorator( + @NonNull PermissionManagerServiceInterface service + ) { + mService = service; + } + + @Nullable + @Override + public byte[] backupRuntimePermissions(int userId) { + Log.i(LOG_TAG, "backupRuntimePermissions(userId = " + userId + ")"); + return mService.backupRuntimePermissions(userId); + } + + @Override + @SuppressWarnings("ArrayToString") + public void restoreRuntimePermissions(@NonNull byte[] backup, int userId) { + Log.i(LOG_TAG, "restoreRuntimePermissions(backup = " + backup + ", userId = " + userId + + ")"); + mService.restoreRuntimePermissions(backup, userId); + } + + @Override + public void restoreDelayedRuntimePermissions(@NonNull String packageName, int userId) { + Log.i(LOG_TAG, "restoreDelayedRuntimePermissions(packageName = " + packageName + + ", userId = " + userId + ")"); + mService.restoreDelayedRuntimePermissions(packageName, userId); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + Log.i(LOG_TAG, "dump(fd = " + fd + ", pw = " + pw + ", args = " + Arrays.toString(args) + + ")"); + mService.dump(fd, pw, args); + } + + @Override + public List getAllPermissionGroups(int flags) { + Log.i(LOG_TAG, "getAllPermissionGroups(flags = " + flags + ")"); + return mService.getAllPermissionGroups(flags); + } + + @Override + public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { + Log.i(LOG_TAG, "getPermissionGroupInfo(groupName = " + groupName + ", flags = " + flags + + ")"); + return mService.getPermissionGroupInfo(groupName, flags); + } + + @Override + public PermissionInfo getPermissionInfo(@NonNull String permName, int flags, + @NonNull String opPackageName) { + Log.i(LOG_TAG, "getPermissionInfo(permName = " + permName + ", flags = " + flags + + ", opPackageName = " + opPackageName + ")"); + return mService.getPermissionInfo(permName, flags, opPackageName); + } + + @Override + public List queryPermissionsByGroup(String groupName, int flags) { + Log.i(LOG_TAG, "queryPermissionsByGroup(groupName = " + groupName + ", flags = " + flags + + ")"); + return mService.queryPermissionsByGroup(groupName, flags); + } + + @Override + public boolean addPermission(PermissionInfo info, boolean async) { + Log.i(LOG_TAG, "addPermission(info = " + info + ", async = " + async + ")"); + return mService.addPermission(info, async); + } + + @Override + public void removePermission(String permName) { + Log.i(LOG_TAG, "removePermission(permName = " + permName + ")"); + mService.removePermission(permName); + } + + @Override + public int getPermissionFlags(String packageName, String permName, int userId) { + Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = " + + permName + ", userId = " + userId + ")"); + return mService.getPermissionFlags(packageName, permName, userId); + } + + @Override + public void updatePermissionFlags(String packageName, String permName, int flagMask, + int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { + Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = " + + permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues + + ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission + + ", userId = " + userId + ")"); + mService.updatePermissionFlags(packageName, permName, flagMask, flagValues, + checkAdjustPolicyFlagPermission, userId); + } + + @Override + public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { + Log.i(LOG_TAG, "updatePermissionFlagsForAllApps(flagMask = " + flagMask + ", flagValues = " + + flagValues + ", userId = " + userId + ")"); + mService.updatePermissionFlagsForAllApps(flagMask, flagValues, userId); + } + + @Override + public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { + Log.i(LOG_TAG, "addOnPermissionsChangeListener(listener = " + listener + ")"); + mService.addOnPermissionsChangeListener(listener); + } + + @Override + public void removeOnPermissionsChangeListener( + IOnPermissionsChangeListener listener) { + Log.i(LOG_TAG, "removeOnPermissionsChangeListener(listener = " + listener + ")"); + mService.removeOnPermissionsChangeListener(listener); + } + + @Override + public boolean addAllowlistedRestrictedPermission(@NonNull String packageName, + @NonNull String permName, int flags, int userId) { + Log.i(LOG_TAG, "addAllowlistedRestrictedPermission(packageName = " + packageName + + ", permName = " + permName + ", flags = " + flags + ", userId = " + userId + ")"); + return mService.addAllowlistedRestrictedPermission(packageName, permName, flags, userId); + } + + @Override + public List getAllowlistedRestrictedPermissions(@NonNull String packageName, int flags, + int userId) { + Log.i(LOG_TAG, "getAllowlistedRestrictedPermissions(packageName = " + packageName + + ", flags = " + flags + ", userId = " + userId + ")"); + return mService.getAllowlistedRestrictedPermissions(packageName, flags, userId); + } + + @Override + public boolean removeAllowlistedRestrictedPermission(@NonNull String packageName, + @NonNull String permName, int flags, int userId) { + Log.i(LOG_TAG, "removeAllowlistedRestrictedPermission(packageName = " + packageName + + ", permName = " + permName + ", flags = " + flags + ", userId = " + userId + ")"); + return mService.removeAllowlistedRestrictedPermission(packageName, permName, flags, userId); + } + + @Override + public void grantRuntimePermission(String packageName, String permName, int userId) { + Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = " + + permName + ", userId = " + userId + ")"); + mService.grantRuntimePermission(packageName, permName, userId); + } + + @Override + public void revokeRuntimePermission(String packageName, String permName, int userId, + String reason) { + Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = " + + permName + ", userId = " + userId + ", reason = " + reason + ")"); + mService.revokeRuntimePermission(packageName, permName, userId, reason); + } + + @Override + public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) { + Log.i(LOG_TAG, "revokePostNotificationPermissionWithoutKillForTest(packageName = " + + packageName + ", userId = " + userId + ")"); + mService.revokePostNotificationPermissionWithoutKillForTest(packageName, userId); + } + + @Override + public boolean shouldShowRequestPermissionRationale(String packageName, String permName, + int userId) { + Log.i(LOG_TAG, "shouldShowRequestPermissionRationale(packageName = " + packageName + + ", permName = " + permName + ", userId = " + userId + ")"); + return mService.shouldShowRequestPermissionRationale(packageName, permName, userId); + } + + @Override + public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) { + Log.i(LOG_TAG, "isPermissionRevokedByPolicy(packageName = " + packageName + ", permName = " + + permName + ", userId = " + userId + ")"); + return mService.isPermissionRevokedByPolicy(packageName, permName, userId); + } + + @Override + public List getSplitPermissions() { + Log.i(LOG_TAG, "getSplitPermissions()"); + return mService.getSplitPermissions(); + } + + @Override + public int checkPermission(String pkgName, String permName, int userId) { + Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName + + ", userId = " + userId + ")"); + return mService.checkPermission(pkgName, permName, userId); + } + + @Override + public int checkUidPermission(int uid, String permName) { + Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName + ")"); + return mService.checkUidPermission(uid, permName); + } + + @Override + public void addOnRuntimePermissionStateChangedListener( + @NonNull PermissionManagerServiceInternal + .OnRuntimePermissionStateChangedListener listener) { + Log.i(LOG_TAG, "addOnRuntimePermissionStateChangedListener(listener = " + listener + ")"); + mService.addOnRuntimePermissionStateChangedListener(listener); + } + + @Override + public void removeOnRuntimePermissionStateChangedListener( + @NonNull PermissionManagerServiceInternal + .OnRuntimePermissionStateChangedListener listener) { + Log.i(LOG_TAG, "removeOnRuntimePermissionStateChangedListener(listener = " + listener + + ")"); + mService.removeOnRuntimePermissionStateChangedListener(listener); + } + + @Override + public Map> getAllAppOpPermissionPackages() { + Log.i(LOG_TAG, "getAllAppOpPermissionPackages()"); + return mService.getAllAppOpPermissionPackages(); + } + + @Override + public boolean isPermissionsReviewRequired(@NonNull String packageName, int userId) { + Log.i(LOG_TAG, "isPermissionsReviewRequired(packageName = " + packageName + ", userId = " + + userId + ")"); + return mService.isPermissionsReviewRequired(packageName, userId); + } + + @Override + public void resetRuntimePermissions(@NonNull AndroidPackage pkg, int userId) { + Log.i(LOG_TAG, "resetRuntimePermissions(pkg = " + pkg + ", userId = " + userId + ")"); + mService.resetRuntimePermissions(pkg, userId); + } + + @Override + public void resetRuntimePermissionsForUser(int userId) { + Log.i(LOG_TAG, "resetRuntimePermissionsForUser(userId = " + userId + ")"); + mService.resetRuntimePermissionsForUser(userId); + } + + @Override + public void readLegacyPermissionStateTEMP() { + Log.i(LOG_TAG, "readLegacyPermissionStateTEMP()"); + mService.readLegacyPermissionStateTEMP(); + } + + @Override + public void writeLegacyPermissionStateTEMP() { + Log.i(LOG_TAG, "writeLegacyPermissionStateTEMP()"); + mService.writeLegacyPermissionStateTEMP(); + } + + @NonNull + @Override + public Set getGrantedPermissions(@NonNull String packageName, int userId) { + Log.i(LOG_TAG, "getGrantedPermissions(packageName = " + packageName + ", userId = " + + userId + ")"); + return mService.getGrantedPermissions(packageName, userId); + } + + @NonNull + @Override + public int[] getPermissionGids(@NonNull String permissionName, int userId) { + Log.i(LOG_TAG, "getPermissionGids(permissionName = " + permissionName + ", userId = " + + userId + ")"); + return mService.getPermissionGids(permissionName, userId); + } + + @NonNull + @Override + public String[] getAppOpPermissionPackages(@NonNull String permissionName) { + Log.i(LOG_TAG, "getAppOpPermissionPackages(permissionName = " + permissionName + ")"); + return mService.getAppOpPermissionPackages(permissionName); + } + + @Nullable + @Override + public Permission getPermissionTEMP(@NonNull String permName) { + Log.i(LOG_TAG, "getPermissionTEMP(permName = " + permName + ")"); + return mService.getPermissionTEMP(permName); + } + + @NonNull + @Override + public List getAllPermissionsWithProtection(int protection) { + Log.i(LOG_TAG, "getAllPermissionsWithProtection(protection = " + protection + ")"); + return mService.getAllPermissionsWithProtection(protection); + } + + @NonNull + @Override + public List getAllPermissionsWithProtectionFlags(int protectionFlags) { + Log.i(LOG_TAG, "getAllPermissionsWithProtectionFlags(protectionFlags = " + protectionFlags + + ")"); + return mService.getAllPermissionsWithProtectionFlags(protectionFlags); + } + + @NonNull + @Override + public List getLegacyPermissions() { + Log.i(LOG_TAG, "getLegacyPermissions()"); + return mService.getLegacyPermissions(); + } + + @NonNull + @Override + public LegacyPermissionState getLegacyPermissionState(int appId) { + Log.i(LOG_TAG, "getLegacyPermissionState(appId = " + appId + ")"); + return mService.getLegacyPermissionState(appId); + } + + @Override + public void readLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + Log.i(LOG_TAG, "readLegacyPermissionsTEMP(legacyPermissionSettings = " + + legacyPermissionSettings + ")"); + mService.readLegacyPermissionsTEMP(legacyPermissionSettings); + } + + @Override + public void writeLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + Log.i(LOG_TAG, "writeLegacyPermissionsTEMP(legacyPermissionSettings = " + + legacyPermissionSettings + ")"); + mService.writeLegacyPermissionsTEMP(legacyPermissionSettings); + } + + @Override + public void onSystemReady() { + Log.i(LOG_TAG, "onSystemReady()"); + mService.onSystemReady(); + } + + @Override + public void onStorageVolumeMounted(@NonNull String volumeUuid, boolean fingerprintChanged) { + Log.i(LOG_TAG, "onStorageVolumeMounted(volumeUuid = " + volumeUuid + + ", fingerprintChanged = " + fingerprintChanged + ")"); + mService.onStorageVolumeMounted(volumeUuid, fingerprintChanged); + } + + @NonNull + @Override + public int[] getGidsForUid(int uid) { + Log.i(LOG_TAG, "getGidsForUid(uid = " + uid + ")"); + return mService.getGidsForUid(uid); + } + + @Override + public void onUserCreated(int userId) { + Log.i(LOG_TAG, "onUserCreated(userId = " + userId + ")"); + mService.onUserCreated(userId); + } + + @Override + public void onUserRemoved(int userId) { + Log.i(LOG_TAG, "onUserRemoved(userId = " + userId + ")"); + mService.onUserRemoved(userId); + } + + @Override + public void onPackageAdded(@NonNull PackageState packageState, boolean isInstantApp, + @Nullable AndroidPackage oldPkg) { + Log.i(LOG_TAG, "onPackageAdded(packageState = " + packageState + ", isInstantApp = " + + isInstantApp + ", oldPkg = " + oldPkg + ")"); + mService.onPackageAdded(packageState, isInstantApp, oldPkg); + } + + @Override + public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId, + @NonNull PermissionManagerServiceInternal.PackageInstalledParams params, int userId) { + Log.i(LOG_TAG, "onPackageInstalled(pkg = " + pkg + ", previousAppId = " + previousAppId + + ", params = " + params + ", userId = " + userId + ")"); + mService.onPackageInstalled(pkg, previousAppId, params, userId); + } + + @Override + public void onPackageRemoved(@NonNull AndroidPackage pkg) { + Log.i(LOG_TAG, "onPackageRemoved(pkg = " + pkg + ")"); + mService.onPackageRemoved(pkg); + } + + @Override + public void onPackageUninstalled(@NonNull String packageName, int appId, + @NonNull PackageState packageState, @NonNull AndroidPackage pkg, + @NonNull List sharedUserPkgs, int userId) { + Log.i(LOG_TAG, "onPackageUninstalled(packageName = " + packageName + ", appId = " + appId + + ", packageState = " + packageState + ", pkg = " + pkg + ", sharedUserPkgs = " + + sharedUserPkgs + ", userId = " + userId + ")"); + mService.onPackageUninstalled(packageName, appId, packageState, pkg, sharedUserPkgs, + userId); + } +} diff --git a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt index 3b277f8fbb8a9..f549797d42f86 100644 --- a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt +++ b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt @@ -88,6 +88,7 @@ class AccessCheckingService(context: Context) : SystemService(context) { configPermissions, privilegedPermissionAllowlistPackages, permissionAllowlist, implicitToSourcePermissions ) + persistence.initialize() persistence.read(state) this.state = state @@ -99,43 +100,6 @@ class AccessCheckingService(context: Context) : SystemService(context) { permissionService.initialize() } - private val PackageManagerInternal.knownPackages: IntMap> - get() = IntMap>().apply { - this[KnownPackages.PACKAGE_INSTALLER] = getKnownPackageNames( - KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] = getKnownPackageNames( - KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_VERIFIER] = getKnownPackageNames( - KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_SETUP_WIZARD] = getKnownPackageNames( - KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] = getKnownPackageNames( - KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_CONFIGURATOR] = getKnownPackageNames( - KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] = getKnownPackageNames( - KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_APP_PREDICTOR] = getKnownPackageNames( - KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_COMPANION] = getKnownPackageNames( - KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_RETAIL_DEMO] = getKnownPackageNames( - KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM - ) - this[KnownPackages.PACKAGE_RECENTS] = getKnownPackageNames( - KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM - ) - } - private val SystemConfig.isLeanback: Boolean get() = PackageManager.FEATURE_LEANBACK in availableFeatures @@ -187,10 +151,12 @@ class AccessCheckingService(context: Context) : SystemService(context) { internal fun onStorageVolumeMounted(volumeUuid: String?, isSystemUpdated: Boolean) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { onStorageVolumeMounted( - packageStates, disabledSystemPackageStates, volumeUuid, isSystemUpdated + packageStates, disabledSystemPackageStates, knownPackages, volumeUuid, + isSystemUpdated ) } } @@ -198,35 +164,48 @@ class AccessCheckingService(context: Context) : SystemService(context) { internal fun onPackageAdded(packageName: String) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates + val knownPackages = packageManagerInternal.knownPackages mutateState { - with(policy) { onPackageAdded(packageStates, disabledSystemPackageStates, packageName) } + with(policy) { + onPackageAdded( + packageStates, disabledSystemPackageStates, knownPackages, packageName + ) + } } } internal fun onPackageRemoved(packageName: String, appId: Int) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { - onPackageRemoved(packageStates, disabledSystemPackageStates, packageName, appId) + onPackageRemoved( + packageStates, disabledSystemPackageStates, knownPackages, packageName, appId + ) } } } internal fun onPackageInstalled(packageName: String, userId: Int) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { - onPackageInstalled(packageStates, disabledSystemPackageStates, packageName, userId) + onPackageInstalled( + packageStates, disabledSystemPackageStates, knownPackages, packageName, userId + ) } } } internal fun onPackageUninstalled(packageName: String, appId: Int, userId: Int) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { onPackageUninstalled( - packageStates, disabledSystemPackageStates, packageName, appId, userId + packageStates, disabledSystemPackageStates, knownPackages, packageName, appId, + userId ) } } @@ -236,6 +215,43 @@ class AccessCheckingService(context: Context) : SystemService(context) { Pair, Map> get() = withUnfilteredSnapshot().use { it.packageStates to it.disabledSystemPackageStates } + private val PackageManagerInternal.knownPackages: IntMap> + get() = IntMap>().apply { + this[KnownPackages.PACKAGE_INSTALLER] = getKnownPackageNames( + KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] = getKnownPackageNames( + KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_VERIFIER] = getKnownPackageNames( + KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_SETUP_WIZARD] = getKnownPackageNames( + KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] = getKnownPackageNames( + KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_CONFIGURATOR] = getKnownPackageNames( + KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] = getKnownPackageNames( + KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_APP_PREDICTOR] = getKnownPackageNames( + KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_COMPANION] = getKnownPackageNames( + KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_RETAIL_DEMO] = getKnownPackageNames( + KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_RECENTS] = getKnownPackageNames( + KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM + ) + } + @OptIn(ExperimentalContracts::class) internal inline fun getState(action: GetStateScope.() -> T): T { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } diff --git a/services/permission/java/com/android/server/permission/access/AccessPersistence.kt b/services/permission/java/com/android/server/permission/access/AccessPersistence.kt index 91239c6925055..a25b720724425 100644 --- a/services/permission/java/com/android/server/permission/access/AccessPersistence.kt +++ b/services/permission/java/com/android/server/permission/access/AccessPersistence.kt @@ -16,8 +16,15 @@ package com.android.server.permission.access +import android.os.Handler +import android.os.Looper +import android.os.Message +import android.os.SystemClock +import android.os.UserHandle import android.util.AtomicFile import android.util.Log +import com.android.internal.annotations.GuardedBy +import com.android.internal.os.BackgroundThread import com.android.modules.utils.BinaryXmlPullParser import com.android.modules.utils.BinaryXmlSerializer import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports @@ -32,6 +39,20 @@ import java.io.FileNotFoundException class AccessPersistence( private val policy: AccessPolicy ) { + private val scheduleLock = Any() + @GuardedBy("scheduleLock") + private val pendingMutationTimesMillis = IntLongMap() + @GuardedBy("scheduleLock") + private val pendingStates = IntMap() + @GuardedBy("scheduleLock") + private lateinit var writeHandler: WriteHandler + + private val writeLock = Any() + + fun initialize() { + writeHandler = WriteHandler(BackgroundThread.getHandler().looper) + } + fun read(state: AccessState) { readSystemState(state) state.systemState.userIds.forEachIndexed { _, userId -> @@ -64,21 +85,61 @@ class AccessPersistence( } fun write(state: AccessState) { - writeState(state.systemState) { writeSystemState(state) } + state.systemState.write(state, UserHandle.USER_ALL) state.userStates.forEachIndexed { _, userId, userState -> - writeState(userState) { writeUserState(state, userId) } + userState.write(state, userId) } } - private inline fun writeState(state: T, write: () -> Unit) { - when (val writeMode = state.writeMode) { + private fun WritableState.write(state: AccessState, userId: Int) { + when (val writeMode = writeMode) { WriteMode.NONE -> {} - WriteMode.SYNC -> write() - WriteMode.ASYNC -> TODO() + WriteMode.SYNC -> { + synchronized(scheduleLock) { pendingStates[userId] = state } + writePendingState(userId) + } + WriteMode.ASYNC -> { + synchronized(scheduleLock) { + writeHandler.removeMessages(userId) + pendingStates[userId] = state + // SystemClock.uptimeMillis() is used in Handler.sendMessageDelayed(). + val currentTimeMillis = SystemClock.uptimeMillis() + val pendingMutationTimeMillis = + pendingMutationTimesMillis.getOrPut(userId) { currentTimeMillis } + val currentDelayMillis = currentTimeMillis - pendingMutationTimeMillis + val message = writeHandler.obtainMessage(userId) + if (currentDelayMillis > MAX_WRITE_DELAY_MILLIS) { + message.sendToTarget() + } else { + val newDelayMillis = WRITE_DELAY_TIME_MILLIS + .coerceAtMost(MAX_WRITE_DELAY_MILLIS - currentDelayMillis) + writeHandler.sendMessageDelayed(message, newDelayMillis) + } + } + } else -> error(writeMode) } } + private fun writePendingState(userId: Int) { + synchronized(writeLock) { + val state: AccessState? + synchronized(scheduleLock) { + pendingMutationTimesMillis -= userId + state = pendingStates.removeReturnOld(userId) + writeHandler.removeMessages(userId) + } + if (state == null) { + return + } + if (userId == UserHandle.USER_ALL) { + writeSystemState(state) + } else { + writeUserState(state, userId) + } + } + } + private fun writeSystemState(state: AccessState) { systemFile.serialize { with(policy) { serializeSystemState(state) } @@ -109,5 +170,25 @@ class AccessPersistence( private val LOG_TAG = AccessPersistence::class.java.simpleName private const val FILE_NAME = "access.abx" + + private const val WRITE_DELAY_TIME_MILLIS = 1000L + private const val MAX_WRITE_DELAY_MILLIS = 2000L + } + + private inner class WriteHandler(looper: Looper) : Handler(looper) { + fun writeAtTime(userId: Int, timeMillis: Long) { + removeMessages(userId) + val message = obtainMessage(userId) + sendMessageDelayed(message, timeMillis) + } + + fun cancelWrite(userId: Int) { + removeMessages(userId) + } + + override fun handleMessage(message: Message) { + val userId = message.what + writePendingState(userId) + } } } diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt index 2d83bfd5a5269..e0f94c7707a6f 100644 --- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt @@ -82,6 +82,11 @@ class AccessPolicy private constructor( this.permissionAllowlist = permissionAllowlist this.implicitToSourcePermissions = implicitToSourcePermissions } + state.userStates.apply { + userIds.forEachIndexed { _, userId -> + this[userId] = UserState() + } + } } fun GetStateScope.onStateMutated() { @@ -115,12 +120,29 @@ class AccessPolicy private constructor( fun MutateStateScope.onStorageVolumeMounted( packageStates: Map, disabledSystemPackageStates: Map, + knownPackages: IntMap>, volumeUuid: String?, isSystemUpdated: Boolean ) { + val addedAppIds = IntSet() newState.systemState.apply { this.packageStates = packageStates this.disabledSystemPackageStates = disabledSystemPackageStates + packageStates.forEach { (packageName, packageState) -> + if (packageState.volumeUuid == volumeUuid) { + val appId = packageState.appId + appIds.getOrPut(appId) { + addedAppIds += appId + IndexedListSet() + } += packageName + } + } + this.knownPackages = knownPackages + } + addedAppIds.forEachIndexed { _, appId -> + forEachSchemePolicy { + with(it) { onAppIdAdded(appId) } + } } forEachSchemePolicy { with(it) { onStorageVolumeMounted(volumeUuid, isSystemUpdated) } @@ -130,6 +152,7 @@ class AccessPolicy private constructor( fun MutateStateScope.onPackageAdded( packageStates: Map, disabledSystemPackageStates: Map, + knownPackages: IntMap>, packageName: String ) { val packageState = packageStates[packageName] @@ -145,7 +168,8 @@ class AccessPolicy private constructor( appIds.getOrPut(appId) { isAppIdAdded = true IndexedListSet() - }.add(packageName) + } += packageName + this.knownPackages = knownPackages } if (isAppIdAdded) { forEachSchemePolicy { @@ -160,6 +184,7 @@ class AccessPolicy private constructor( fun MutateStateScope.onPackageRemoved( packageStates: Map, disabledSystemPackageStates: Map, + knownPackages: IntMap>, packageName: String, appId: Int ) { @@ -178,6 +203,7 @@ class AccessPolicy private constructor( isAppIdRemoved = true } } + this.knownPackages = knownPackages } forEachSchemePolicy { with(it) { onPackageRemoved(packageName, appId) } @@ -192,12 +218,14 @@ class AccessPolicy private constructor( fun MutateStateScope.onPackageInstalled( packageStates: Map, disabledSystemPackageStates: Map, + knownPackages: IntMap>, packageName: String, userId: Int ) { newState.systemState.apply { this.packageStates = packageStates this.disabledSystemPackageStates = disabledSystemPackageStates + this.knownPackages = knownPackages } val packageState = packageStates[packageName] // TODO(zhanghai): STOPSHIP: Remove check before feature enable. @@ -212,6 +240,7 @@ class AccessPolicy private constructor( fun MutateStateScope.onPackageUninstalled( packageStates: Map, disabledSystemPackageStates: Map, + knownPackages: IntMap>, packageName: String, appId: Int, userId: Int @@ -219,6 +248,7 @@ class AccessPolicy private constructor( newState.systemState.apply { this.packageStates = packageStates this.disabledSystemPackageStates = disabledSystemPackageStates + this.knownPackages = knownPackages } forEachSchemePolicy { with(it) { onPackageUninstalled(packageName, appId, userId) } diff --git a/services/permission/java/com/android/server/permission/access/collection/IntLongMap.kt b/services/permission/java/com/android/server/permission/access/collection/IntLongMap.kt new file mode 100644 index 0000000000000..692bbd65d5da7 --- /dev/null +++ b/services/permission/java/com/android/server/permission/access/collection/IntLongMap.kt @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2022 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.permission.access.collection + +import android.util.SparseLongArray + +typealias IntLongMap = SparseLongArray + +inline fun IntLongMap.allIndexed(predicate: (Int, Int, Long) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (!predicate(index, key, value)) { + return false + } + } + return true +} + +inline fun IntLongMap.anyIndexed(predicate: (Int, Int, Long) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (predicate(index, key, value)) { + return true + } + } + return false +} + +@Suppress("NOTHING_TO_INLINE") +inline fun IntLongMap.copy(): IntLongMap = clone() + +inline fun IntLongMap.firstNotNullOfOrNullIndexed(transform: (Int, Int, Long) -> R): R? { + forEachIndexed { index, key, value -> + transform(index, key, value)?.let { return it } + } + return null +} + +inline fun IntLongMap.forEachIndexed(action: (Int, Int, Long) -> Unit) { + for (index in 0 until size) { + action(index, keyAt(index), valueAt(index)) + } +} + +inline fun IntLongMap.forEachKeyIndexed(action: (Int, Int) -> Unit) { + for (index in 0 until size) { + action(index, keyAt(index)) + } +} + +inline fun IntLongMap.forEachReversedIndexed(action: (Int, Int, Long) -> Unit) { + for (index in lastIndex downTo 0) { + action(index, keyAt(index), valueAt(index)) + } +} + +inline fun IntLongMap.forEachValueIndexed(action: (Int, Long) -> Unit) { + for (index in 0 until size) { + action(index, valueAt(index)) + } +} + +inline fun IntLongMap.getOrPut(key: Int, defaultValue: () -> Long): Long { + val index = indexOfKey(key) + return if (index >= 0) { + valueAt(index) + } else { + defaultValue().also { put(key, it) } + } +} + +@Suppress("NOTHING_TO_INLINE") +inline fun IntLongMap?.getWithDefault(key: Int, defaultValue: Long): Long { + this ?: return defaultValue + return get(key, defaultValue) +} + +inline val IntLongMap.lastIndex: Int + get() = size - 1 + +@Suppress("NOTHING_TO_INLINE") +inline operator fun IntLongMap.minusAssign(key: Int) { + delete(key) +} + +inline fun IntLongMap.noneIndexed(predicate: (Int, Int, Long) -> Boolean): Boolean { + forEachIndexed { index, key, value -> + if (predicate(index, key, value)) { + return false + } + } + return true +} + +@Suppress("NOTHING_TO_INLINE") +inline fun IntLongMap.putWithDefault(key: Int, value: Long, defaultValue: Long): Long { + val index = indexOfKey(key) + if (index >= 0) { + val oldValue = valueAt(index) + if (value != oldValue) { + if (value == defaultValue) { + removeAt(index) + } else { + setValueAt(index, value) + } + } + return oldValue + } else { + if (value != defaultValue) { + put(key, value) + } + return defaultValue + } +} + +fun IntLongMap.remove(key: Int) { + delete(key) +} + +fun IntLongMap.remove(key: Int, defaultValue: Long): Long { + val index = indexOfKey(key) + return if (index >= 0) { + val oldValue = valueAt(index) + removeAt(index) + oldValue + } else { + defaultValue + } +} + +inline fun IntLongMap.removeAllIndexed(predicate: (Int, Int, Long) -> Boolean): Boolean { + var isChanged = false + forEachReversedIndexed { index, key, value -> + if (predicate(index, key, value)) { + removeAt(index) + isChanged = true + } + } + return isChanged +} + +inline fun IntLongMap.retainAllIndexed(predicate: (Int, Int, Long) -> Boolean): Boolean { + var isChanged = false + forEachReversedIndexed { index, key, value -> + if (!predicate(index, key, value)) { + removeAt(index) + isChanged = true + } + } + return isChanged +} + +@Suppress("NOTHING_TO_INLINE") +inline operator fun IntLongMap.set(key: Int, value: Long) { + put(key, value) +} + +inline val IntLongMap.size: Int + get() = size() diff --git a/services/permission/java/com/android/server/permission/access/permission/Permission.kt b/services/permission/java/com/android/server/permission/access/permission/Permission.kt index 35f00a72d3eaa..7bfca1214b537 100644 --- a/services/permission/java/com/android/server/permission/access/permission/Permission.kt +++ b/services/permission/java/com/android/server/permission/access/permission/Permission.kt @@ -157,7 +157,7 @@ data class Permission( if (areGidsPerUser) { IntArray(gids.size) { i -> UserHandle.getUid(userId, gids[i]) } } else { - gids.clone() + gids.copyOf() } companion object { diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index e2c2c498192ec..dd36c38c1bd4a 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -137,6 +137,7 @@ class PermissionService( userManagerService = UserManagerService.getInstance() handlerThread = ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true) + .apply { start() } handler = Handler(handlerThread.looper) onPermissionsChangeListeners = OnPermissionsChangeListeners(FgThread.get().looper) onPermissionFlagsChangedListener = OnPermissionFlagsChangedListener() @@ -578,7 +579,7 @@ class PermissionService( // more consistent with the pre-S-refactor behavior. This is also because we are now // actively trimming the per-UID objects when empty. val permissionFlags = with(policy) { getUidPermissionFlags(appId, userId) } - ?: return globalGids.clone() + ?: return globalGids.copyOf() val gids = GrowingIntArray.wrap(globalGids) permissionFlags.forEachIndexed { _, permissionName, flags -> @@ -654,6 +655,11 @@ class PermissionService( ) } + if (!userManagerInternal.exists(userId)) { + Log.w(LOG_TAG, "$methodName: Unknown user $userId") + return + } + enforceCallingOrSelfCrossUserPermission( userId, enforceFullPermission = true, enforceShellRestriction = true, methodName ) @@ -664,11 +670,6 @@ class PermissionService( } context.enforceCallingOrSelfPermission(enforcedPermissionName, methodName) - if (!userManagerInternal.exists(userId)) { - Log.w(LOG_TAG, "$methodName: Unknown user $userId") - return - } - val packageState: PackageState? val permissionControllerPackageName = packageManagerInternal.getKnownPackageNames( KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM @@ -870,6 +871,11 @@ class PermissionService( } override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int { + if (!userManagerInternal.exists(userId)) { + Log.w(LOG_TAG, "getPermissionFlags: Unknown user $userId") + return 0 + } + enforceCallingOrSelfCrossUserPermission( userId, enforceFullPermission = true, enforceShellRestriction = false, "getPermissionFlags" @@ -880,11 +886,6 @@ class PermissionService( Manifest.permission.GET_RUNTIME_PERMISSIONS ) - if (!userManagerInternal.exists(userId)) { - Log.w(LOG_TAG, "getPermissionFlags: Unknown user $userId") - return 0 - } - val packageState = packageManagerLocal.withFilteredSnapshot() .use { it.getPackageState(packageName) } if (packageState == null) { @@ -910,16 +911,16 @@ class PermissionService( permissionName: String, userId: Int ): Boolean { - enforceCallingOrSelfCrossUserPermission( - userId, enforceFullPermission = true, enforceShellRestriction = false, - "isPermissionRevokedByPolicy" - ) - if (!userManagerInternal.exists(userId)) { Log.w(LOG_TAG, "isPermissionRevokedByPolicy: Unknown user $userId") return false } + enforceCallingOrSelfCrossUserPermission( + userId, enforceFullPermission = true, enforceShellRestriction = false, + "isPermissionRevokedByPolicy" + ) + val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId) .use { it.getPackageState(packageName) } ?: return false @@ -954,16 +955,16 @@ class PermissionService( permissionName: String, userId: Int ): Boolean { - enforceCallingOrSelfCrossUserPermission( - userId, enforceFullPermission = true, enforceShellRestriction = false, - "shouldShowRequestPermissionRationale" - ) - if (!userManagerInternal.exists(userId)) { Log.w(LOG_TAG, "shouldShowRequestPermissionRationale: Unknown user $userId") return false } + enforceCallingOrSelfCrossUserPermission( + userId, enforceFullPermission = true, enforceShellRestriction = false, + "shouldShowRequestPermissionRationale" + ) + val callingUid = Binder.getCallingUid() val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId) .use { it.getPackageState(packageName) } ?: return false @@ -1030,6 +1031,11 @@ class PermissionService( ) } + if (!userManagerInternal.exists(userId)) { + Log.w(LOG_TAG, "updatePermissionFlags: Unknown user $userId") + return + } + enforceCallingOrSelfCrossUserPermission( userId, enforceFullPermission = true, enforceShellRestriction = true, "updatePermissionFlags" @@ -1062,11 +1068,6 @@ class PermissionService( } } - if (!userManagerInternal.exists(userId)) { - Log.w(LOG_TAG, "updatePermissionFlags: Unknown user $userId") - return - } - // Using PackageManagerInternal instead of PackageManagerLocal for now due to need to access // shared user packages. // TODO: We probably shouldn't check the share user packages, since the package name is @@ -1124,6 +1125,11 @@ class PermissionService( ) } + if (!userManagerInternal.exists(userId)) { + Log.w(LOG_TAG, "updatePermissionFlagsForAllApps: Unknown user $userId") + return + } + enforceCallingOrSelfCrossUserPermission( userId, enforceFullPermission = true, enforceShellRestriction = true, "updatePermissionFlagsForAllApps" @@ -1133,11 +1139,6 @@ class PermissionService( Manifest.permission.REVOKE_RUNTIME_PERMISSIONS ) - if (!userManagerInternal.exists(userId)) { - Log.w(LOG_TAG, "updatePermissionFlagsForAllApps: Unknown user $userId") - return - } - val packageStates = packageManagerLocal.withUnfilteredSnapshot() .use { it.packageStates } service.mutateState { @@ -1229,16 +1230,16 @@ class PermissionService( Preconditions.checkFlagsArgument(allowlistedFlags, PERMISSION_ALLOWLIST_MASK) Preconditions.checkArgumentNonnegative(userId, "userId cannot be null") - enforceCallingOrSelfCrossUserPermission( - userId, enforceFullPermission = false, enforceShellRestriction = false, - "getAllowlistedRestrictedPermissions" - ) - if (!userManagerInternal.exists(userId)) { Log.w(LOG_TAG, "AllowlistedRestrictedPermission api: Unknown user $userId") return null } + enforceCallingOrSelfCrossUserPermission( + userId, enforceFullPermission = false, enforceShellRestriction = false, + "getAllowlistedRestrictedPermissions" + ) + val callingUid = Binder.getCallingUid() val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId) .use { it.getPackageState(packageName) } ?: return null @@ -1517,11 +1518,11 @@ class PermissionService( } override fun resetRuntimePermissions(androidPackage: AndroidPackage, userId: Int) { - TODO("Not yet implemented") + // TODO("Not yet implemented") } override fun resetRuntimePermissionsForUser(userId: Int) { - TODO("Not yet implemented") + // TODO("Not yet implemented") } override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) { @@ -1657,33 +1658,36 @@ class PermissionService( override fun getPermissionTEMP( permissionName: String ): com.android.server.pm.permission.Permission? { - TODO("Not yet implemented") + // TODO("Not yet implemented") + return null } override fun getLegacyPermissions(): List { - TODO("Not yet implemented") + // TODO("Not yet implemented") + return emptyList() } override fun readLegacyPermissionsTEMP(legacyPermissionSettings: LegacyPermissionSettings) { // Package settings has been read when this method is called. service.initialize() - TODO("Not yet implemented") + // TODO("Not yet implemented") } override fun writeLegacyPermissionsTEMP(legacyPermissionSettings: LegacyPermissionSettings) { - TODO("Not yet implemented") + // TODO("Not yet implemented") } override fun getLegacyPermissionState(appId: Int): LegacyPermissionState { - TODO("Not yet implemented") + // TODO("Not yet implemented") + return LegacyPermissionState() } override fun readLegacyPermissionStateTEMP() { - TODO("Not yet implemented") + // TODO("Not yet implemented") } override fun writeLegacyPermissionStateTEMP() { - TODO("Not yet implemented") + // TODO("Not yet implemented") } override fun onSystemReady() { diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt index 4c3ffde6b3be7..73fc0b2bd766e 100644 --- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt @@ -271,7 +271,12 @@ class UidPermissionPolicy : SchemePolicy() { this.packageName = packageName protectionLevel = oldPermission.permissionInfo.protectionLevel } - val newPermission = Permission(newPermissionInfo, false, oldPermission.type, 0) + // Different from the old implementation, which removes the GIDs upon permission + // adoption, but adds them back on the next boot, we now just consistently keep the + // GIDs. + val newPermission = oldPermission.copy( + permissionInfo = newPermissionInfo, isReconciled = false, appId = 0 + ) permissions.setValueAt(permissionIndex, newPermission) systemState.requestWrite() changedPermissionNames += permissionName @@ -385,7 +390,10 @@ class UidPermissionPolicy : SchemePolicy() { } if (oldPermission.type == Permission.TYPE_CONFIG && !oldPermission.isReconciled) { // It's a config permission and has no owner, take ownership now. - Permission(newPermissionInfo, true, Permission.TYPE_CONFIG, packageState.appId) + oldPermission.copy( + permissionInfo = newPermissionInfo, isReconciled = true, + appId = packageState.appId + ) } else if (systemState.packageStates[oldPackageName]?.isSystem != true) { Log.w( LOG_TAG, "Overriding permission $permissionName with new declaration in" + @@ -398,8 +406,12 @@ class UidPermissionPolicy : SchemePolicy() { setPermissionFlags(appId, userId, permissionName, 0) } } + // Different from the old implementation, which removes the GIDs upon permission + // override, but adds them back on the next boot, we now just consistently keep + // the GIDs. Permission( - newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId + newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId, + oldPermission.gids, oldPermission.areGidsPerUser ) } else { Log.w( @@ -413,7 +425,17 @@ class UidPermissionPolicy : SchemePolicy() { // Different from the old implementation, which doesn't update the permission // definition upon app update, but does update it on the next boot, we now // consistently update the permission definition upon app update. - Permission(newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId) + @Suppress("IfThenToElvis") + if (oldPermission != null) { + oldPermission.copy( + permissionInfo = newPermissionInfo, isReconciled = true, + appId = packageState.appId + ) + } else { + Permission( + newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId + ) + } } if (parsedPermission.isTree) { @@ -498,7 +520,7 @@ class UidPermissionPolicy : SchemePolicy() { // TODO: STOPSHIP: Retain permissions requested by disabled system packages. } newState.userStates.forEachIndexed { _, userId, userState -> - userState.uidPermissionFlags[appId].forEachReversedIndexed { _, permissionName, _ -> + userState.uidPermissionFlags[appId]?.forEachReversedIndexed { _, permissionName, _ -> if (permissionName !in requestedPermissions) { setPermissionFlags(appId, userId, permissionName, 0) } @@ -852,10 +874,12 @@ class UidPermissionPolicy : SchemePolicy() { permissionName: String ): Boolean? { val permissionAllowlist = newState.systemState.permissionAllowlist - // TODO(b/261913353): STOPSHIP: Add AndroidPackage.apexModuleName. The below is only for - // passing compilation but won't actually work. + // TODO(b/261913353): STOPSHIP: Add AndroidPackage.apexModuleName. // val apexModuleName = androidPackage.apexModuleName - val apexModuleName = packageState.packageName + val apexModuleName = permissionAllowlist.apexPrivilegedAppAllowlists + .firstNotNullOfOrNullIndexed { _, apexModuleName, apexAllowlist -> + if (packageState.packageName in apexAllowlist) apexModuleName else null + } val packageName = packageState.packageName return when { packageState.isVendor -> permissionAllowlist.getVendorPrivilegedAppAllowlistState( @@ -901,7 +925,7 @@ class UidPermissionPolicy : SchemePolicy() { return targetSdkVersion } - private fun MutateStateScope.anyPackageInAppId( + private inline fun MutateStateScope.anyPackageInAppId( appId: Int, state: AccessState = newState, predicate: (PackageState) -> Boolean @@ -913,7 +937,7 @@ class UidPermissionPolicy : SchemePolicy() { } } - private fun MutateStateScope.forEachPackageInAppId( + private inline fun MutateStateScope.forEachPackageInAppId( appId: Int, state: AccessState = newState, action: (PackageState) -> Unit