Loading core/java/android/app/AppOpsManager.java +146 −10 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.IBinder; import android.os.IpcDataCache; import android.os.Looper; import android.os.PackageTagsList; import android.os.Parcel; Loading @@ -78,12 +79,14 @@ import android.permission.PermissionGroupUsage; import android.permission.PermissionUsageHelper; import android.permission.flags.Flags; import android.provider.DeviceConfig; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LongSparseArray; import android.util.LongSparseLongArray; import android.util.Pools; import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.Immutable; Loading Loading @@ -7797,6 +7800,116 @@ public class AppOpsManager { } } private static final String APP_OP_MODE_CACHING_API = "getAppOpMode"; private static final String APP_OP_MODE_CACHING_NAME = "appOpModeCache"; private static final int APP_OP_MODE_CACHING_SIZE = 2048; private static final IpcDataCache.QueryHandler<AppOpModeQuery, Integer> sGetAppOpModeQuery = new IpcDataCache.QueryHandler<>() { @Override public Integer apply(AppOpModeQuery query) { IAppOpsService service = getService(); try { return service.checkOperationRawForDevice(query.op, query.uid, query.packageName, query.attributionTag, query.virtualDeviceId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override public boolean shouldBypassCache(@NonNull AppOpModeQuery query) { // If the flag to enable the new caching behavior is off, bypass the cache. return !Flags.appopModeCachingEnabled(); } }; // A LRU cache on binder clients that caches AppOp mode by uid, packageName, virtualDeviceId // and attributionTag. private static final IpcDataCache<AppOpModeQuery, Integer> sAppOpModeCache = new IpcDataCache<>(APP_OP_MODE_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM, APP_OP_MODE_CACHING_API, APP_OP_MODE_CACHING_NAME, sGetAppOpModeQuery); // Ops that we don't want to cache due to: // 1) Discrepancy of attributionTag support in checkOp and noteOp that determines if a package // can bypass user restriction of an op: b/240617242. COARSE_LOCATION and FINE_LOCATION are // the only two ops that are impacted. private static final SparseBooleanArray OPS_WITHOUT_CACHING = new SparseBooleanArray(); static { OPS_WITHOUT_CACHING.put(OP_COARSE_LOCATION, true); OPS_WITHOUT_CACHING.put(OP_FINE_LOCATION, true); } private static boolean isAppOpModeCachingEnabled(int opCode) { if (!Flags.appopModeCachingEnabled()) { return false; } return !OPS_WITHOUT_CACHING.get(opCode, false); } /** * @hide */ public static void invalidateAppOpModeCache() { if (Flags.appopModeCachingEnabled()) { IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, APP_OP_MODE_CACHING_API); } } /** * Bypass AppOpModeCache in the local process * * @hide */ public static void disableAppOpModeCache() { if (Flags.appopModeCachingEnabled()) { sAppOpModeCache.disableLocal(); } } private static final class AppOpModeQuery { final int op; final int uid; final String packageName; final int virtualDeviceId; final String attributionTag; final String methodName; AppOpModeQuery(int op, int uid, @Nullable String packageName, int virtualDeviceId, @Nullable String attributionTag, @Nullable String methodName) { this.op = op; this.uid = uid; this.packageName = packageName; this.virtualDeviceId = virtualDeviceId; this.attributionTag = attributionTag; this.methodName = methodName; } @Override public String toString() { return TextUtils.formatSimple("AppOpModeQuery(op=%d, uid=%d, packageName=%s, " + "virtualDeviceId=%d, attributionTag=%s, methodName=%s", op, uid, packageName, virtualDeviceId, attributionTag, methodName); } @Override public int hashCode() { return Objects.hash(op, uid, packageName, virtualDeviceId, attributionTag); } @Override public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null) return false; if (this.getClass() != o.getClass()) return false; AppOpModeQuery other = (AppOpModeQuery) o; return op == other.op && uid == other.uid && Objects.equals(packageName, other.packageName) && virtualDeviceId == other.virtualDeviceId && Objects.equals(attributionTag, other.attributionTag); } } AppOpsManager(Context context, IAppOpsService service) { mContext = context; mService = service; Loading Loading @@ -8851,12 +8964,16 @@ public class AppOpsManager { private int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName, int virtualDeviceId) { try { if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) { return mService.checkOperationRaw(op, uid, packageName, null); int mode; if (isAppOpModeCachingEnabled(op)) { mode = sAppOpModeCache.query( new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null, "unsafeCheckOpRawNoThrow")); } else { return mService.checkOperationRawForDevice( mode = mService.checkOperationRawForDevice( op, uid, packageName, null, virtualDeviceId); } return mode; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading Loading @@ -9284,8 +9401,21 @@ public class AppOpsManager { @UnsupportedAppUsage public int checkOp(int op, int uid, String packageName) { try { int mode = mService.checkOperationForDevice(op, uid, packageName, int mode; if (isAppOpModeCachingEnabled(op)) { mode = sAppOpModeCache.query( new AppOpModeQuery(op, uid, packageName, Context.DEVICE_ID_DEFAULT, null, "checkOp")); if (mode == MODE_FOREGROUND) { // We only cache raw mode. If the mode is FOREGROUND, we need another binder // call to fetch translated value based on the process state. mode = mService.checkOperationForDevice(op, uid, packageName, Context.DEVICE_ID_DEFAULT); } } else { mode = mService.checkOperationForDevice(op, uid, packageName, Context.DEVICE_ID_DEFAULT); } if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } Loading Loading @@ -9324,13 +9454,19 @@ public class AppOpsManager { private int checkOpNoThrow(int op, int uid, String packageName, int virtualDeviceId) { try { int mode; if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) { mode = mService.checkOperation(op, uid, packageName); if (isAppOpModeCachingEnabled(op)) { mode = sAppOpModeCache.query( new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null, "checkOpNoThrow")); if (mode == MODE_FOREGROUND) { // We only cache raw mode. If the mode is FOREGROUND, we need another binder // call to fetch translated value based on the process state. mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId); } } else { mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId); } return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode; return mode; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java +22 −5 Original line number Diff line number Diff line Loading @@ -65,27 +65,31 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { @Override public boolean setGlobalRestriction(Object clientToken, int code, boolean restricted) { boolean changed; if (restricted) { if (!mGlobalRestrictions.containsKey(clientToken)) { mGlobalRestrictions.put(clientToken, new SparseBooleanArray()); } SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken); Objects.requireNonNull(restrictedCodes); boolean changed = !restrictedCodes.get(code); changed = !restrictedCodes.get(code); restrictedCodes.put(code, true); return changed; } else { SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken); if (restrictedCodes == null) { return false; } boolean changed = restrictedCodes.get(code); changed = restrictedCodes.get(code); restrictedCodes.delete(code); if (restrictedCodes.size() == 0) { mGlobalRestrictions.remove(clientToken); } return changed; } if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } @Override Loading @@ -104,7 +108,11 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { @Override public boolean clearGlobalRestrictions(Object clientToken) { return mGlobalRestrictions.remove(clientToken) != null; boolean changed = mGlobalRestrictions.remove(clientToken) != null; if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } @RequiresPermission(anyOf = { Loading @@ -122,6 +130,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { changed |= putUserRestrictionExclusions(clientToken, userIds[i], excludedPackageTags); } if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } Loading Loading @@ -191,6 +202,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { changed |= mUserRestrictions.remove(clientToken) != null; changed |= mUserRestrictionExcludedPackageTags.remove(clientToken) != null; notifyAllUserRestrictions(allUserRestrictedCodes); if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } Loading Loading @@ -244,6 +258,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { } } if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } Loading services/core/java/com/android/server/appop/AppOpsService.java +77 −3 Original line number Diff line number Diff line Loading @@ -998,6 +998,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void onUidModeChanged(int uid, int code, int mode, String persistentDeviceId) { AppOpsManager.invalidateAppOpModeCache(); mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChangedForAllPkgsInUid, AppOpsService.this, code, uid, false, persistentDeviceId)); Loading @@ -1006,6 +1007,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void onPackageModeChanged(String packageName, int userId, int code, int mode) { AppOpsManager.invalidateAppOpModeCache(); mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChangedForPkg, AppOpsService.this, packageName, code, mode, userId)); Loading @@ -1032,6 +1034,11 @@ public class AppOpsService extends IAppOpsService.Stub { // To migrate storageFile to recentAccessesFile, these reads must be called in this order. readRecentAccesses(); mAppOpsCheckingService.readState(); // The system property used by the cache is created the first time it is written, that only // happens inside invalidateCache(). Until the service calls invalidateCache() the property // will not exist and the nonce will be UNSET. AppOpsManager.invalidateAppOpModeCache(); AppOpsManager.disableAppOpModeCache(); } public void publish() { Loading Loading @@ -2830,6 +2837,13 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkOperationRaw(int code, int uid, String packageName, @Nullable String attributionTag) { if (Binder.getCallingPid() != Process.myPid() && Flags.appopAccessTrackingLoggingEnabled()) { FrameworkStatsLog.write( APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code, APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION, false); } return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag, Context.DEVICE_ID_DEFAULT, true /*raw*/); } Loading @@ -2837,6 +2851,13 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkOperationRawForDevice(int code, int uid, @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId) { if (Binder.getCallingPid() != Process.myPid() && Flags.appopAccessTrackingLoggingEnabled()) { FrameworkStatsLog.write( APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code, APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION, false); } return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag, virtualDeviceId, true /*raw*/); } Loading Loading @@ -2894,9 +2915,15 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } } if (Flags.appopModeCachingEnabled()) { return getAppOpMode(code, uid, resolvedPackageName, attributionTag, virtualDeviceId, raw, true); } else { return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag, virtualDeviceId, raw); } } /** * Get the mode of an app-op. Loading Loading @@ -2961,6 +2988,54 @@ public class AppOpsService extends IAppOpsService.Stub { } } /** * This method unifies mode checking logic between checkOperationUnchecked and * noteOperationUnchecked. It can replace those two methods once the flag is fully rolled out. * * @param isCheckOp This param is only used in user's op restriction. When checking if a package * can bypass user's restriction we should account for attributionTag as well. * But existing checkOp APIs don't accept attributionTag so we added a hack to * skip attributionTag check for checkOp. After we add an overload of checkOp * that accepts attributionTag we should remove this param. */ private @Mode int getAppOpMode(int code, int uid, @NonNull String packageName, @Nullable String attributionTag, int virtualDeviceId, boolean raw, boolean isCheckOp) { PackageVerificationResult pvr; try { pvr = verifyAndGetBypass(uid, packageName, attributionTag); } catch (SecurityException e) { logVerifyAndGetBypassFailure(uid, e, "getAppOpMode"); return MODE_IGNORED; } if (isOpRestrictedDueToSuspend(code, packageName, uid)) { return MODE_IGNORED; } synchronized (this) { if (isOpRestrictedLocked(uid, code, packageName, attributionTag, virtualDeviceId, pvr.bypass, isCheckOp)) { return MODE_IGNORED; } if (isOpAllowedForUid(uid)) { return MODE_ALLOWED; } int switchCode = AppOpsManager.opToSwitch(code); int rawUidMode = mAppOpsCheckingService.getUidMode(uid, getPersistentId(virtualDeviceId), switchCode); if (rawUidMode != AppOpsManager.opToDefaultMode(switchCode)) { return raw ? rawUidMode : evaluateForegroundMode(uid, switchCode, rawUidMode); } int rawPackageMode = mAppOpsCheckingService.getPackageMode(packageName, switchCode, UserHandle.getUserId(uid)); return raw ? rawPackageMode : evaluateForegroundMode(uid, switchCode, rawPackageMode); } } @Override public int checkAudioOperation(int code, int usage, int uid, String packageName) { return mCheckOpsDelegateDispatcher.checkAudioOperation(code, usage, uid, packageName); Loading Loading @@ -3213,7 +3288,6 @@ public class AppOpsService extends IAppOpsService.Stub { PackageVerificationResult pvr; try { pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName); boolean wasNull = attributionTag == null; if (!pvr.isAttributionTagValid) { attributionTag = null; } Loading services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java +2 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.AppOpsManager.OP_FINE_LOCATION; import static org.junit.Assert.assertEquals; import android.app.PropertyInvalidatedCache; import android.content.Context; import android.os.Handler; Loading Loading @@ -63,6 +64,7 @@ public class AppOpsLegacyRestrictionsTest { @Before public void setUp() { PropertyInvalidatedCache.disableForTestMode(); mSession = ExtendedMockito.mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) Loading services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java +2 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.app.PropertyInvalidatedCache; import android.companion.virtual.VirtualDeviceManager; import android.content.Context; import android.os.FileUtils; Loading Loading @@ -69,6 +70,7 @@ public class AppOpsRecentAccessPersistenceTest { @Before public void setUp() { PropertyInvalidatedCache.disableForTestMode(); when(mAppOpCheckingService.addAppOpsModeChangedListener(any())).thenReturn(true); LocalServices.addService(AppOpsCheckingServiceInterface.class, mAppOpCheckingService); Loading Loading
core/java/android/app/AppOpsManager.java +146 −10 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.IBinder; import android.os.IpcDataCache; import android.os.Looper; import android.os.PackageTagsList; import android.os.Parcel; Loading @@ -78,12 +79,14 @@ import android.permission.PermissionGroupUsage; import android.permission.PermissionUsageHelper; import android.permission.flags.Flags; import android.provider.DeviceConfig; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LongSparseArray; import android.util.LongSparseLongArray; import android.util.Pools; import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.Immutable; Loading Loading @@ -7797,6 +7800,116 @@ public class AppOpsManager { } } private static final String APP_OP_MODE_CACHING_API = "getAppOpMode"; private static final String APP_OP_MODE_CACHING_NAME = "appOpModeCache"; private static final int APP_OP_MODE_CACHING_SIZE = 2048; private static final IpcDataCache.QueryHandler<AppOpModeQuery, Integer> sGetAppOpModeQuery = new IpcDataCache.QueryHandler<>() { @Override public Integer apply(AppOpModeQuery query) { IAppOpsService service = getService(); try { return service.checkOperationRawForDevice(query.op, query.uid, query.packageName, query.attributionTag, query.virtualDeviceId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override public boolean shouldBypassCache(@NonNull AppOpModeQuery query) { // If the flag to enable the new caching behavior is off, bypass the cache. return !Flags.appopModeCachingEnabled(); } }; // A LRU cache on binder clients that caches AppOp mode by uid, packageName, virtualDeviceId // and attributionTag. private static final IpcDataCache<AppOpModeQuery, Integer> sAppOpModeCache = new IpcDataCache<>(APP_OP_MODE_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM, APP_OP_MODE_CACHING_API, APP_OP_MODE_CACHING_NAME, sGetAppOpModeQuery); // Ops that we don't want to cache due to: // 1) Discrepancy of attributionTag support in checkOp and noteOp that determines if a package // can bypass user restriction of an op: b/240617242. COARSE_LOCATION and FINE_LOCATION are // the only two ops that are impacted. private static final SparseBooleanArray OPS_WITHOUT_CACHING = new SparseBooleanArray(); static { OPS_WITHOUT_CACHING.put(OP_COARSE_LOCATION, true); OPS_WITHOUT_CACHING.put(OP_FINE_LOCATION, true); } private static boolean isAppOpModeCachingEnabled(int opCode) { if (!Flags.appopModeCachingEnabled()) { return false; } return !OPS_WITHOUT_CACHING.get(opCode, false); } /** * @hide */ public static void invalidateAppOpModeCache() { if (Flags.appopModeCachingEnabled()) { IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, APP_OP_MODE_CACHING_API); } } /** * Bypass AppOpModeCache in the local process * * @hide */ public static void disableAppOpModeCache() { if (Flags.appopModeCachingEnabled()) { sAppOpModeCache.disableLocal(); } } private static final class AppOpModeQuery { final int op; final int uid; final String packageName; final int virtualDeviceId; final String attributionTag; final String methodName; AppOpModeQuery(int op, int uid, @Nullable String packageName, int virtualDeviceId, @Nullable String attributionTag, @Nullable String methodName) { this.op = op; this.uid = uid; this.packageName = packageName; this.virtualDeviceId = virtualDeviceId; this.attributionTag = attributionTag; this.methodName = methodName; } @Override public String toString() { return TextUtils.formatSimple("AppOpModeQuery(op=%d, uid=%d, packageName=%s, " + "virtualDeviceId=%d, attributionTag=%s, methodName=%s", op, uid, packageName, virtualDeviceId, attributionTag, methodName); } @Override public int hashCode() { return Objects.hash(op, uid, packageName, virtualDeviceId, attributionTag); } @Override public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null) return false; if (this.getClass() != o.getClass()) return false; AppOpModeQuery other = (AppOpModeQuery) o; return op == other.op && uid == other.uid && Objects.equals(packageName, other.packageName) && virtualDeviceId == other.virtualDeviceId && Objects.equals(attributionTag, other.attributionTag); } } AppOpsManager(Context context, IAppOpsService service) { mContext = context; mService = service; Loading Loading @@ -8851,12 +8964,16 @@ public class AppOpsManager { private int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName, int virtualDeviceId) { try { if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) { return mService.checkOperationRaw(op, uid, packageName, null); int mode; if (isAppOpModeCachingEnabled(op)) { mode = sAppOpModeCache.query( new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null, "unsafeCheckOpRawNoThrow")); } else { return mService.checkOperationRawForDevice( mode = mService.checkOperationRawForDevice( op, uid, packageName, null, virtualDeviceId); } return mode; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading Loading @@ -9284,8 +9401,21 @@ public class AppOpsManager { @UnsupportedAppUsage public int checkOp(int op, int uid, String packageName) { try { int mode = mService.checkOperationForDevice(op, uid, packageName, int mode; if (isAppOpModeCachingEnabled(op)) { mode = sAppOpModeCache.query( new AppOpModeQuery(op, uid, packageName, Context.DEVICE_ID_DEFAULT, null, "checkOp")); if (mode == MODE_FOREGROUND) { // We only cache raw mode. If the mode is FOREGROUND, we need another binder // call to fetch translated value based on the process state. mode = mService.checkOperationForDevice(op, uid, packageName, Context.DEVICE_ID_DEFAULT); } } else { mode = mService.checkOperationForDevice(op, uid, packageName, Context.DEVICE_ID_DEFAULT); } if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } Loading Loading @@ -9324,13 +9454,19 @@ public class AppOpsManager { private int checkOpNoThrow(int op, int uid, String packageName, int virtualDeviceId) { try { int mode; if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) { mode = mService.checkOperation(op, uid, packageName); if (isAppOpModeCachingEnabled(op)) { mode = sAppOpModeCache.query( new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null, "checkOpNoThrow")); if (mode == MODE_FOREGROUND) { // We only cache raw mode. If the mode is FOREGROUND, we need another binder // call to fetch translated value based on the process state. mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId); } } else { mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId); } return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode; return mode; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading
services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java +22 −5 Original line number Diff line number Diff line Loading @@ -65,27 +65,31 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { @Override public boolean setGlobalRestriction(Object clientToken, int code, boolean restricted) { boolean changed; if (restricted) { if (!mGlobalRestrictions.containsKey(clientToken)) { mGlobalRestrictions.put(clientToken, new SparseBooleanArray()); } SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken); Objects.requireNonNull(restrictedCodes); boolean changed = !restrictedCodes.get(code); changed = !restrictedCodes.get(code); restrictedCodes.put(code, true); return changed; } else { SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken); if (restrictedCodes == null) { return false; } boolean changed = restrictedCodes.get(code); changed = restrictedCodes.get(code); restrictedCodes.delete(code); if (restrictedCodes.size() == 0) { mGlobalRestrictions.remove(clientToken); } return changed; } if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } @Override Loading @@ -104,7 +108,11 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { @Override public boolean clearGlobalRestrictions(Object clientToken) { return mGlobalRestrictions.remove(clientToken) != null; boolean changed = mGlobalRestrictions.remove(clientToken) != null; if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } @RequiresPermission(anyOf = { Loading @@ -122,6 +130,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { changed |= putUserRestrictionExclusions(clientToken, userIds[i], excludedPackageTags); } if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } Loading Loading @@ -191,6 +202,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { changed |= mUserRestrictions.remove(clientToken) != null; changed |= mUserRestrictionExcludedPackageTags.remove(clientToken) != null; notifyAllUserRestrictions(allUserRestrictedCodes); if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } Loading Loading @@ -244,6 +258,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { } } if (changed) { AppOpsManager.invalidateAppOpModeCache(); } return changed; } Loading
services/core/java/com/android/server/appop/AppOpsService.java +77 −3 Original line number Diff line number Diff line Loading @@ -998,6 +998,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void onUidModeChanged(int uid, int code, int mode, String persistentDeviceId) { AppOpsManager.invalidateAppOpModeCache(); mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChangedForAllPkgsInUid, AppOpsService.this, code, uid, false, persistentDeviceId)); Loading @@ -1006,6 +1007,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void onPackageModeChanged(String packageName, int userId, int code, int mode) { AppOpsManager.invalidateAppOpModeCache(); mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChangedForPkg, AppOpsService.this, packageName, code, mode, userId)); Loading @@ -1032,6 +1034,11 @@ public class AppOpsService extends IAppOpsService.Stub { // To migrate storageFile to recentAccessesFile, these reads must be called in this order. readRecentAccesses(); mAppOpsCheckingService.readState(); // The system property used by the cache is created the first time it is written, that only // happens inside invalidateCache(). Until the service calls invalidateCache() the property // will not exist and the nonce will be UNSET. AppOpsManager.invalidateAppOpModeCache(); AppOpsManager.disableAppOpModeCache(); } public void publish() { Loading Loading @@ -2830,6 +2837,13 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkOperationRaw(int code, int uid, String packageName, @Nullable String attributionTag) { if (Binder.getCallingPid() != Process.myPid() && Flags.appopAccessTrackingLoggingEnabled()) { FrameworkStatsLog.write( APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code, APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION, false); } return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag, Context.DEVICE_ID_DEFAULT, true /*raw*/); } Loading @@ -2837,6 +2851,13 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkOperationRawForDevice(int code, int uid, @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId) { if (Binder.getCallingPid() != Process.myPid() && Flags.appopAccessTrackingLoggingEnabled()) { FrameworkStatsLog.write( APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code, APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION, false); } return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag, virtualDeviceId, true /*raw*/); } Loading Loading @@ -2894,9 +2915,15 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } } if (Flags.appopModeCachingEnabled()) { return getAppOpMode(code, uid, resolvedPackageName, attributionTag, virtualDeviceId, raw, true); } else { return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag, virtualDeviceId, raw); } } /** * Get the mode of an app-op. Loading Loading @@ -2961,6 +2988,54 @@ public class AppOpsService extends IAppOpsService.Stub { } } /** * This method unifies mode checking logic between checkOperationUnchecked and * noteOperationUnchecked. It can replace those two methods once the flag is fully rolled out. * * @param isCheckOp This param is only used in user's op restriction. When checking if a package * can bypass user's restriction we should account for attributionTag as well. * But existing checkOp APIs don't accept attributionTag so we added a hack to * skip attributionTag check for checkOp. After we add an overload of checkOp * that accepts attributionTag we should remove this param. */ private @Mode int getAppOpMode(int code, int uid, @NonNull String packageName, @Nullable String attributionTag, int virtualDeviceId, boolean raw, boolean isCheckOp) { PackageVerificationResult pvr; try { pvr = verifyAndGetBypass(uid, packageName, attributionTag); } catch (SecurityException e) { logVerifyAndGetBypassFailure(uid, e, "getAppOpMode"); return MODE_IGNORED; } if (isOpRestrictedDueToSuspend(code, packageName, uid)) { return MODE_IGNORED; } synchronized (this) { if (isOpRestrictedLocked(uid, code, packageName, attributionTag, virtualDeviceId, pvr.bypass, isCheckOp)) { return MODE_IGNORED; } if (isOpAllowedForUid(uid)) { return MODE_ALLOWED; } int switchCode = AppOpsManager.opToSwitch(code); int rawUidMode = mAppOpsCheckingService.getUidMode(uid, getPersistentId(virtualDeviceId), switchCode); if (rawUidMode != AppOpsManager.opToDefaultMode(switchCode)) { return raw ? rawUidMode : evaluateForegroundMode(uid, switchCode, rawUidMode); } int rawPackageMode = mAppOpsCheckingService.getPackageMode(packageName, switchCode, UserHandle.getUserId(uid)); return raw ? rawPackageMode : evaluateForegroundMode(uid, switchCode, rawPackageMode); } } @Override public int checkAudioOperation(int code, int usage, int uid, String packageName) { return mCheckOpsDelegateDispatcher.checkAudioOperation(code, usage, uid, packageName); Loading Loading @@ -3213,7 +3288,6 @@ public class AppOpsService extends IAppOpsService.Stub { PackageVerificationResult pvr; try { pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName); boolean wasNull = attributionTag == null; if (!pvr.isAttributionTagValid) { attributionTag = null; } Loading
services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java +2 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.AppOpsManager.OP_FINE_LOCATION; import static org.junit.Assert.assertEquals; import android.app.PropertyInvalidatedCache; import android.content.Context; import android.os.Handler; Loading Loading @@ -63,6 +64,7 @@ public class AppOpsLegacyRestrictionsTest { @Before public void setUp() { PropertyInvalidatedCache.disableForTestMode(); mSession = ExtendedMockito.mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) Loading
services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java +2 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.app.PropertyInvalidatedCache; import android.companion.virtual.VirtualDeviceManager; import android.content.Context; import android.os.FileUtils; Loading Loading @@ -69,6 +70,7 @@ public class AppOpsRecentAccessPersistenceTest { @Before public void setUp() { PropertyInvalidatedCache.disableForTestMode(); when(mAppOpCheckingService.addAppOpsModeChangedListener(any())).thenReturn(true); LocalServices.addService(AppOpsCheckingServiceInterface.class, mAppOpCheckingService); Loading