Loading core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -263,6 +263,7 @@ package android.app { method @RequiresPermission("android.permission.MANAGE_APPOPS") public void setHistoryParameters(int, long, int); method @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES) public void setMode(int, int, String, int); method public static int strOpToOp(@NonNull String); method public int unsafeCheckOpRawNoThrow(@NonNull String, @NonNull android.content.AttributionSource); field public static final int ATTRIBUTION_CHAIN_ID_NONE = -1; // 0xffffffff field public static final int ATTRIBUTION_FLAGS_NONE = 0; // 0x0 field public static final int ATTRIBUTION_FLAG_ACCESSOR = 1; // 0x1 Loading core/java/android/app/AppOpsManager.java +14 −2 Original line number Diff line number Diff line Loading @@ -8783,6 +8783,18 @@ public class AppOpsManager { attributionSource.getPackageName(), attributionSource.getDeviceId()); } /** * Returns the <em>raw</em> mode associated with the op. * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. * @hide */ @TestApi @SuppressLint("UnflaggedApi") // @TestApi without associated feature. public int unsafeCheckOpRawNoThrow( @NonNull String op, @NonNull AttributionSource attributionSource) { return unsafeCheckOpRawNoThrow(strOpToOp(op), attributionSource); } /** * Returns the <em>raw</em> mode associated with the op. * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. Loading @@ -8798,8 +8810,8 @@ public class AppOpsManager { if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) { return mService.checkOperationRaw(op, uid, packageName, null); } else { return mService.checkOperationRawForDevice(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT); return mService.checkOperationRawForDevice( op, uid, packageName, null, virtualDeviceId); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); Loading services/core/java/com/android/server/appop/AppOpsService.java +25 −23 Original line number Diff line number Diff line Loading @@ -2773,15 +2773,15 @@ public class AppOpsService extends IAppOpsService.Stub { } code = AppOpsManager.opToSwitch(code); UidState uidState = getUidStateLocked(uid, false); if (uidState != null && mAppOpsCheckingService.getUidMode( uidState.uid, getPersistentId(virtualDeviceId), code) != AppOpsManager.opToDefaultMode(code)) { final int rawMode = mAppOpsCheckingService.getUidMode( if (uidState != null) { int rawUidMode = mAppOpsCheckingService.getUidMode( uidState.uid, getPersistentId(virtualDeviceId), code); return raw ? rawMode : uidState.evalMode(code, rawMode); if (rawUidMode != AppOpsManager.opToDefaultMode(code)) { return raw ? rawUidMode : uidState.evalMode(code, rawUidMode); } } Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false); if (op == null) { return AppOpsManager.opToDefaultMode(code); Loading Loading @@ -3682,26 +3682,24 @@ public class AppOpsService extends IAppOpsService.Stub { isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, virtualDeviceId, pvr.bypass, false); final int switchCode = AppOpsManager.opToSwitch(code); int rawUidMode; if (isOpAllowedForUid(uid)) { // Op is always allowed for the UID, do nothing. // If there is a non-default per UID policy (we set UID op mode only if // non-default) it takes over, otherwise use the per package policy. } else if (mAppOpsCheckingService.getUidMode( uidState.uid, getPersistentId(virtualDeviceId), switchCode) != AppOpsManager.opToDefaultMode(switchCode)) { final int uidMode = uidState.evalMode( code, } else if ((rawUidMode = mAppOpsCheckingService.getUidMode( uidState.uid, getPersistentId(virtualDeviceId), switchCode)); uidState.uid, getPersistentId(virtualDeviceId), switchCode)) != AppOpsManager.opToDefaultMode(switchCode)) { final int uidMode = uidState.evalMode(code, rawUidMode); if (!shouldStartForMode(uidMode, startIfModeDefault)) { if (DEBUG) { Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName + " flags: " + AppOpsManager.flagsToString(flags)); + packageName + " flags: " + AppOpsManager.flagsToString(flags)); } attributedOp.rejected(uidState.getState(), flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, Loading @@ -3710,8 +3708,8 @@ public class AppOpsService extends IAppOpsService.Stub { return new SyncNotedAppOp(uidMode, code, attributionTag, packageName); } } else { final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true) : op; final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true) : op; final int mode = switchOp.uidState.evalMode( switchOp.op, Loading @@ -3721,9 +3719,12 @@ public class AppOpsService extends IAppOpsService.Stub { UserHandle.getUserId(switchOp.uid))); if (mode != AppOpsManager.MODE_ALLOWED && (!startIfModeDefault || mode != MODE_DEFAULT)) { if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code " if (DEBUG) { Slog.d(TAG, "startOperation: reject #" + mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName + " flags: " + AppOpsManager.flagsToString(flags)); + packageName + " flags: " + AppOpsManager.flagsToString(flags)); } attributedOp.rejected(uidState.getState(), flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, virtualDeviceId, flags, mode, startType, attributionFlags, Loading @@ -3731,6 +3732,7 @@ public class AppOpsService extends IAppOpsService.Stub { return new SyncNotedAppOp(mode, code, attributionTag, packageName); } } if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid + " package " + packageName + " restricted: " + isRestricted + " flags: " + AppOpsManager.flagsToString(flags)); Loading services/permission/java/com/android/server/permission/access/appop/AppOpService.kt +33 −13 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.app.AppOpsManager import android.companion.virtual.VirtualDeviceManager import android.os.Handler import android.os.UserHandle import android.permission.PermissionManager import android.permission.flags.Flags import android.util.ArrayMap import android.util.ArraySet Loading Loading @@ -142,7 +143,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray { override fun getNonDefaultUidModes(uid: Int, deviceId: String): SparseIntArray { val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) service.getState { Loading @@ -150,7 +151,8 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS with(appIdPolicy) { opNameMapToOpSparseArray(getAppOpModes(appId, userId)?.map) } if (Flags.runtimePermissionAppopsMappingEnabled()) { runtimePermissionNameToAppOp.forEachIndexed { _, permissionName, appOpCode -> val mode = getUidModeFromPermissionState(appId, userId, permissionName) val mode = getUidModeFromPermissionState(appId, userId, permissionName, deviceId) if (mode != AppOpsManager.opToDefaultMode(appOpCode)) { modes[appOpCode] = mode } Loading @@ -165,7 +167,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS return opNameMapToOpSparseArray(getPackageModes(packageName, userId)) } override fun getUidMode(uid: Int, persistentDeviceId: String, op: Int): Int { override fun getUidMode(uid: Int, deviceId: String, op: Int): Int { val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) Loading @@ -174,7 +176,9 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS return if (!Flags.runtimePermissionAppopsMappingEnabled() || permissionName == null) { service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } } } else { service.getState { getUidModeFromPermissionState(appId, userId, permissionName) } service.getState { getUidModeFromPermissionState(appId, userId, permissionName, deviceId) } } } Loading @@ -187,16 +191,32 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private fun GetStateScope.getUidModeFromPermissionState( appId: Int, userId: Int, permissionName: String permissionName: String, deviceId: String ): Int { val checkDevicePermissionFlags = deviceId != VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT && permissionName in PermissionManager.DEVICE_AWARE_PERMISSIONS val permissionFlags = if (checkDevicePermissionFlags) { with(devicePermissionPolicy) { getPermissionFlags(appId, deviceId, userId, permissionName) } } else { with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) } } val backgroundPermissionName = foregroundToBackgroundPermissionName[permissionName] val backgroundPermissionFlags = if (backgroundPermissionName != null) { if (checkDevicePermissionFlags) { with(devicePermissionPolicy) { getPermissionFlags(appId, deviceId, userId, backgroundPermissionName) } } else { with(permissionPolicy) { getPermissionFlags(appId, userId, backgroundPermissionName) } } } else { PermissionFlags.RUNTIME_GRANTED } Loading @@ -207,7 +227,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val fullerPermissionName = PermissionService.getFullerPermission(permissionName) ?: return result return getUidModeFromPermissionState(appId, userId, fullerPermissionName) return getUidModeFromPermissionState(appId, userId, fullerPermissionName, deviceId) } private fun evaluateModeFromPermissionFlags( Loading @@ -224,7 +244,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS MODE_IGNORED } override fun setUidMode(uid: Int, persistentDeviceId: String, code: Int, mode: Int): Boolean { override fun setUidMode(uid: Int, deviceId: String, code: Int, mode: Int): Boolean { if ( Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames ) { Loading Loading @@ -308,7 +328,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS // and we have our own persistence. } override fun getForegroundOps(uid: Int, persistentDeviceId: String): SparseBooleanArray { override fun getForegroundOps(uid: Int, deviceId: String): SparseBooleanArray { return SparseBooleanArray().apply { getUidModes(uid)?.forEachIndexed { _, op, mode -> if (mode == AppOpsManager.MODE_FOREGROUND) { Loading @@ -317,7 +337,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } if (Flags.runtimePermissionAppopsMappingEnabled()) { foregroundableOps.forEachIndexed { _, op, _ -> if (getUidMode(uid, persistentDeviceId, op) == AppOpsManager.MODE_FOREGROUND) { if (getUidMode(uid, deviceId, op) == AppOpsManager.MODE_FOREGROUND) { this[op] = true } } Loading services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java +33 −17 Original line number Diff line number Diff line Loading @@ -16,7 +16,8 @@ package com.android.server.appop; import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA; import static com.google.common.truth.Truth.assertThat; Loading @@ -31,6 +32,7 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.Manifest; import android.app.AppOpsManager; import android.app.AppOpsManager.OnOpActiveChangedListener; import android.companion.virtual.VirtualDeviceManager; Loading @@ -38,7 +40,8 @@ import android.companion.virtual.VirtualDeviceParams; import android.content.AttributionSource; import android.content.Context; import android.os.Process; import android.virtualdevice.cts.common.FakeAssociationRule; import android.permission.PermissionManager; import android.virtualdevice.cts.common.VirtualDeviceRule; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; Loading @@ -49,7 +52,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Tests app ops version upgrades Loading @@ -59,7 +61,13 @@ import java.util.concurrent.atomic.AtomicInteger; public class AppOpsActiveWatcherTest { @Rule public FakeAssociationRule mFakeAssociationRule = new FakeAssociationRule(); public VirtualDeviceRule virtualDeviceRule = VirtualDeviceRule.withAdditionalPermissions( Manifest.permission.GRANT_RUNTIME_PERMISSIONS, Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, Manifest.permission.CREATE_VIRTUAL_DEVICE, Manifest.permission.GET_APP_OPS_STATS ); private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000; @Test Loading Loading @@ -145,20 +153,28 @@ public class AppOpsActiveWatcherTest { @Test public void testWatchActiveOpsForExternalDevice() { final VirtualDeviceManager virtualDeviceManager = getContext().getSystemService( VirtualDeviceManager.class); AtomicInteger virtualDeviceId = new AtomicInteger(); runWithShellPermissionIdentity(() -> { final VirtualDeviceManager.VirtualDevice virtualDevice = virtualDeviceManager.createVirtualDevice( mFakeAssociationRule.getAssociationInfo().getId(), new VirtualDeviceParams.Builder().setName("virtual_device").build()); virtualDeviceId.set(virtualDevice.getDeviceId()); }); VirtualDeviceManager.VirtualDevice virtualDevice = virtualDeviceRule.createManagedVirtualDevice( new VirtualDeviceParams.Builder() .setDevicePolicy(POLICY_TYPE_CAMERA, DEVICE_POLICY_CUSTOM) .build() ); PermissionManager permissionManager = getContext().getSystemService(PermissionManager.class); // Unlike runtime permission being automatically granted to the default device, we need to // grant camera permission to the external device first before we can start op. permissionManager.grantRuntimePermission( getContext().getOpPackageName(), Manifest.permission.CAMERA, virtualDevice.getPersistentDeviceId() ); final OnOpActiveChangedListener listener = mock(OnOpActiveChangedListener.class); AttributionSource attributionSource = new AttributionSource(Process.myUid(), getContext().getOpPackageName(), getContext().getAttributionTag(), virtualDeviceId.get()); virtualDevice.getDeviceId()); final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); appOpsManager.startWatchingActive(new String[]{AppOpsManager.OPSTR_CAMERA, Loading @@ -171,7 +187,7 @@ public class AppOpsActiveWatcherTest { verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA), eq(Process.myUid()), eq(getContext().getOpPackageName()), eq(getContext().getAttributionTag()), eq(virtualDeviceId.get()), eq(true), eq(getContext().getAttributionTag()), eq(virtualDevice.getDeviceId()), eq(true), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); verifyNoMoreInteractions(listener); Loading @@ -182,7 +198,7 @@ public class AppOpsActiveWatcherTest { verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA), eq(Process.myUid()), eq(getContext().getOpPackageName()), eq(getContext().getAttributionTag()), eq(virtualDeviceId.get()), eq(false), eq(getContext().getAttributionTag()), eq(virtualDevice.getDeviceId()), eq(false), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); verifyNoMoreInteractions(listener); Loading Loading
core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -263,6 +263,7 @@ package android.app { method @RequiresPermission("android.permission.MANAGE_APPOPS") public void setHistoryParameters(int, long, int); method @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES) public void setMode(int, int, String, int); method public static int strOpToOp(@NonNull String); method public int unsafeCheckOpRawNoThrow(@NonNull String, @NonNull android.content.AttributionSource); field public static final int ATTRIBUTION_CHAIN_ID_NONE = -1; // 0xffffffff field public static final int ATTRIBUTION_FLAGS_NONE = 0; // 0x0 field public static final int ATTRIBUTION_FLAG_ACCESSOR = 1; // 0x1 Loading
core/java/android/app/AppOpsManager.java +14 −2 Original line number Diff line number Diff line Loading @@ -8783,6 +8783,18 @@ public class AppOpsManager { attributionSource.getPackageName(), attributionSource.getDeviceId()); } /** * Returns the <em>raw</em> mode associated with the op. * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. * @hide */ @TestApi @SuppressLint("UnflaggedApi") // @TestApi without associated feature. public int unsafeCheckOpRawNoThrow( @NonNull String op, @NonNull AttributionSource attributionSource) { return unsafeCheckOpRawNoThrow(strOpToOp(op), attributionSource); } /** * Returns the <em>raw</em> mode associated with the op. * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. Loading @@ -8798,8 +8810,8 @@ public class AppOpsManager { if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) { return mService.checkOperationRaw(op, uid, packageName, null); } else { return mService.checkOperationRawForDevice(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT); return mService.checkOperationRawForDevice( op, uid, packageName, null, virtualDeviceId); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); Loading
services/core/java/com/android/server/appop/AppOpsService.java +25 −23 Original line number Diff line number Diff line Loading @@ -2773,15 +2773,15 @@ public class AppOpsService extends IAppOpsService.Stub { } code = AppOpsManager.opToSwitch(code); UidState uidState = getUidStateLocked(uid, false); if (uidState != null && mAppOpsCheckingService.getUidMode( uidState.uid, getPersistentId(virtualDeviceId), code) != AppOpsManager.opToDefaultMode(code)) { final int rawMode = mAppOpsCheckingService.getUidMode( if (uidState != null) { int rawUidMode = mAppOpsCheckingService.getUidMode( uidState.uid, getPersistentId(virtualDeviceId), code); return raw ? rawMode : uidState.evalMode(code, rawMode); if (rawUidMode != AppOpsManager.opToDefaultMode(code)) { return raw ? rawUidMode : uidState.evalMode(code, rawUidMode); } } Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false); if (op == null) { return AppOpsManager.opToDefaultMode(code); Loading Loading @@ -3682,26 +3682,24 @@ public class AppOpsService extends IAppOpsService.Stub { isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, virtualDeviceId, pvr.bypass, false); final int switchCode = AppOpsManager.opToSwitch(code); int rawUidMode; if (isOpAllowedForUid(uid)) { // Op is always allowed for the UID, do nothing. // If there is a non-default per UID policy (we set UID op mode only if // non-default) it takes over, otherwise use the per package policy. } else if (mAppOpsCheckingService.getUidMode( uidState.uid, getPersistentId(virtualDeviceId), switchCode) != AppOpsManager.opToDefaultMode(switchCode)) { final int uidMode = uidState.evalMode( code, } else if ((rawUidMode = mAppOpsCheckingService.getUidMode( uidState.uid, getPersistentId(virtualDeviceId), switchCode)); uidState.uid, getPersistentId(virtualDeviceId), switchCode)) != AppOpsManager.opToDefaultMode(switchCode)) { final int uidMode = uidState.evalMode(code, rawUidMode); if (!shouldStartForMode(uidMode, startIfModeDefault)) { if (DEBUG) { Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName + " flags: " + AppOpsManager.flagsToString(flags)); + packageName + " flags: " + AppOpsManager.flagsToString(flags)); } attributedOp.rejected(uidState.getState(), flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, Loading @@ -3710,8 +3708,8 @@ public class AppOpsService extends IAppOpsService.Stub { return new SyncNotedAppOp(uidMode, code, attributionTag, packageName); } } else { final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true) : op; final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true) : op; final int mode = switchOp.uidState.evalMode( switchOp.op, Loading @@ -3721,9 +3719,12 @@ public class AppOpsService extends IAppOpsService.Stub { UserHandle.getUserId(switchOp.uid))); if (mode != AppOpsManager.MODE_ALLOWED && (!startIfModeDefault || mode != MODE_DEFAULT)) { if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code " if (DEBUG) { Slog.d(TAG, "startOperation: reject #" + mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName + " flags: " + AppOpsManager.flagsToString(flags)); + packageName + " flags: " + AppOpsManager.flagsToString(flags)); } attributedOp.rejected(uidState.getState(), flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, virtualDeviceId, flags, mode, startType, attributionFlags, Loading @@ -3731,6 +3732,7 @@ public class AppOpsService extends IAppOpsService.Stub { return new SyncNotedAppOp(mode, code, attributionTag, packageName); } } if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid + " package " + packageName + " restricted: " + isRestricted + " flags: " + AppOpsManager.flagsToString(flags)); Loading
services/permission/java/com/android/server/permission/access/appop/AppOpService.kt +33 −13 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.app.AppOpsManager import android.companion.virtual.VirtualDeviceManager import android.os.Handler import android.os.UserHandle import android.permission.PermissionManager import android.permission.flags.Flags import android.util.ArrayMap import android.util.ArraySet Loading Loading @@ -142,7 +143,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } } override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray { override fun getNonDefaultUidModes(uid: Int, deviceId: String): SparseIntArray { val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) service.getState { Loading @@ -150,7 +151,8 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS with(appIdPolicy) { opNameMapToOpSparseArray(getAppOpModes(appId, userId)?.map) } if (Flags.runtimePermissionAppopsMappingEnabled()) { runtimePermissionNameToAppOp.forEachIndexed { _, permissionName, appOpCode -> val mode = getUidModeFromPermissionState(appId, userId, permissionName) val mode = getUidModeFromPermissionState(appId, userId, permissionName, deviceId) if (mode != AppOpsManager.opToDefaultMode(appOpCode)) { modes[appOpCode] = mode } Loading @@ -165,7 +167,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS return opNameMapToOpSparseArray(getPackageModes(packageName, userId)) } override fun getUidMode(uid: Int, persistentDeviceId: String, op: Int): Int { override fun getUidMode(uid: Int, deviceId: String, op: Int): Int { val appId = UserHandle.getAppId(uid) val userId = UserHandle.getUserId(uid) val opName = AppOpsManager.opToPublicName(op) Loading @@ -174,7 +176,9 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS return if (!Flags.runtimePermissionAppopsMappingEnabled() || permissionName == null) { service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } } } else { service.getState { getUidModeFromPermissionState(appId, userId, permissionName) } service.getState { getUidModeFromPermissionState(appId, userId, permissionName, deviceId) } } } Loading @@ -187,16 +191,32 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS private fun GetStateScope.getUidModeFromPermissionState( appId: Int, userId: Int, permissionName: String permissionName: String, deviceId: String ): Int { val checkDevicePermissionFlags = deviceId != VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT && permissionName in PermissionManager.DEVICE_AWARE_PERMISSIONS val permissionFlags = if (checkDevicePermissionFlags) { with(devicePermissionPolicy) { getPermissionFlags(appId, deviceId, userId, permissionName) } } else { with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) } } val backgroundPermissionName = foregroundToBackgroundPermissionName[permissionName] val backgroundPermissionFlags = if (backgroundPermissionName != null) { if (checkDevicePermissionFlags) { with(devicePermissionPolicy) { getPermissionFlags(appId, deviceId, userId, backgroundPermissionName) } } else { with(permissionPolicy) { getPermissionFlags(appId, userId, backgroundPermissionName) } } } else { PermissionFlags.RUNTIME_GRANTED } Loading @@ -207,7 +227,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS val fullerPermissionName = PermissionService.getFullerPermission(permissionName) ?: return result return getUidModeFromPermissionState(appId, userId, fullerPermissionName) return getUidModeFromPermissionState(appId, userId, fullerPermissionName, deviceId) } private fun evaluateModeFromPermissionFlags( Loading @@ -224,7 +244,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS MODE_IGNORED } override fun setUidMode(uid: Int, persistentDeviceId: String, code: Int, mode: Int): Boolean { override fun setUidMode(uid: Int, deviceId: String, code: Int, mode: Int): Boolean { if ( Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames ) { Loading Loading @@ -308,7 +328,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS // and we have our own persistence. } override fun getForegroundOps(uid: Int, persistentDeviceId: String): SparseBooleanArray { override fun getForegroundOps(uid: Int, deviceId: String): SparseBooleanArray { return SparseBooleanArray().apply { getUidModes(uid)?.forEachIndexed { _, op, mode -> if (mode == AppOpsManager.MODE_FOREGROUND) { Loading @@ -317,7 +337,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } if (Flags.runtimePermissionAppopsMappingEnabled()) { foregroundableOps.forEachIndexed { _, op, _ -> if (getUidMode(uid, persistentDeviceId, op) == AppOpsManager.MODE_FOREGROUND) { if (getUidMode(uid, deviceId, op) == AppOpsManager.MODE_FOREGROUND) { this[op] = true } } Loading
services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java +33 −17 Original line number Diff line number Diff line Loading @@ -16,7 +16,8 @@ package com.android.server.appop; import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA; import static com.google.common.truth.Truth.assertThat; Loading @@ -31,6 +32,7 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.Manifest; import android.app.AppOpsManager; import android.app.AppOpsManager.OnOpActiveChangedListener; import android.companion.virtual.VirtualDeviceManager; Loading @@ -38,7 +40,8 @@ import android.companion.virtual.VirtualDeviceParams; import android.content.AttributionSource; import android.content.Context; import android.os.Process; import android.virtualdevice.cts.common.FakeAssociationRule; import android.permission.PermissionManager; import android.virtualdevice.cts.common.VirtualDeviceRule; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; Loading @@ -49,7 +52,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Tests app ops version upgrades Loading @@ -59,7 +61,13 @@ import java.util.concurrent.atomic.AtomicInteger; public class AppOpsActiveWatcherTest { @Rule public FakeAssociationRule mFakeAssociationRule = new FakeAssociationRule(); public VirtualDeviceRule virtualDeviceRule = VirtualDeviceRule.withAdditionalPermissions( Manifest.permission.GRANT_RUNTIME_PERMISSIONS, Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, Manifest.permission.CREATE_VIRTUAL_DEVICE, Manifest.permission.GET_APP_OPS_STATS ); private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000; @Test Loading Loading @@ -145,20 +153,28 @@ public class AppOpsActiveWatcherTest { @Test public void testWatchActiveOpsForExternalDevice() { final VirtualDeviceManager virtualDeviceManager = getContext().getSystemService( VirtualDeviceManager.class); AtomicInteger virtualDeviceId = new AtomicInteger(); runWithShellPermissionIdentity(() -> { final VirtualDeviceManager.VirtualDevice virtualDevice = virtualDeviceManager.createVirtualDevice( mFakeAssociationRule.getAssociationInfo().getId(), new VirtualDeviceParams.Builder().setName("virtual_device").build()); virtualDeviceId.set(virtualDevice.getDeviceId()); }); VirtualDeviceManager.VirtualDevice virtualDevice = virtualDeviceRule.createManagedVirtualDevice( new VirtualDeviceParams.Builder() .setDevicePolicy(POLICY_TYPE_CAMERA, DEVICE_POLICY_CUSTOM) .build() ); PermissionManager permissionManager = getContext().getSystemService(PermissionManager.class); // Unlike runtime permission being automatically granted to the default device, we need to // grant camera permission to the external device first before we can start op. permissionManager.grantRuntimePermission( getContext().getOpPackageName(), Manifest.permission.CAMERA, virtualDevice.getPersistentDeviceId() ); final OnOpActiveChangedListener listener = mock(OnOpActiveChangedListener.class); AttributionSource attributionSource = new AttributionSource(Process.myUid(), getContext().getOpPackageName(), getContext().getAttributionTag(), virtualDeviceId.get()); virtualDevice.getDeviceId()); final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); appOpsManager.startWatchingActive(new String[]{AppOpsManager.OPSTR_CAMERA, Loading @@ -171,7 +187,7 @@ public class AppOpsActiveWatcherTest { verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA), eq(Process.myUid()), eq(getContext().getOpPackageName()), eq(getContext().getAttributionTag()), eq(virtualDeviceId.get()), eq(true), eq(getContext().getAttributionTag()), eq(virtualDevice.getDeviceId()), eq(true), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); verifyNoMoreInteractions(listener); Loading @@ -182,7 +198,7 @@ public class AppOpsActiveWatcherTest { verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA), eq(Process.myUid()), eq(getContext().getOpPackageName()), eq(getContext().getAttributionTag()), eq(virtualDeviceId.get()), eq(false), eq(getContext().getAttributionTag()), eq(virtualDevice.getDeviceId()), eq(false), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); verifyNoMoreInteractions(listener); Loading