summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt151
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt2
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt437
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt9
4 files changed, 589 insertions, 10 deletions
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index f3ab0e33d026..f4d7a8ec5484 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager
import android.content.pm.PermissionGroupInfo
import android.content.pm.PermissionInfo
import android.content.pm.SigningDetails
+import android.health.connect.HealthPermissions
import android.os.Build
import android.permission.flags.Flags
import android.util.Slog
@@ -701,6 +702,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private fun MutateStateScope.revokePermissionsOnPackageUpdate(appId: Int) {
revokeStorageAndMediaPermissionsOnPackageUpdate(appId)
+ revokeHeartRatePermissionsOnPackageUpdate(appId)
}
private fun MutateStateScope.revokeStorageAndMediaPermissionsOnPackageUpdate(appId: Int) {
@@ -751,23 +753,154 @@ class AppIdPermissionPolicy : SchemePolicy() {
// SYSTEM_FIXED. Otherwise the user cannot grant back the permission.
if (
permissionName in STORAGE_AND_MEDIA_PERMISSIONS &&
- oldFlags.hasBits(PermissionFlags.RUNTIME_GRANTED) &&
- !oldFlags.hasAnyBit(SYSTEM_OR_POLICY_FIXED_MASK)
+ oldFlags.hasBits(PermissionFlags.RUNTIME_GRANTED)
) {
- Slog.v(
- LOG_TAG,
- "Revoking storage permission: $permissionName for appId: " +
- " $appId and user: $userId",
+ revokeRuntimePermission(appId, userId, permissionName)
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * If the app is updated, the legacy BODY_SENSOR and READ_HEART_RATE permissions may go out of
+ * sync (for example, when the app eventually requests the implicit new permission). If this
+ * occurs, revoke both permissions to force a re-prompt.
+ */
+ private fun MutateStateScope.revokeHeartRatePermissionsOnPackageUpdate(appId: Int) {
+ val targetSdkVersion = getAppIdTargetSdkVersion(appId, null)
+ // Apps targeting BAKLAVA and above shouldn't be using BODY_SENSORS.
+ if (targetSdkVersion >= Build.VERSION_CODES.BAKLAVA) {
+ return
+ }
+
+ val isBodySensorsRequested =
+ anyPackageInAppId(appId, newState) {
+ Manifest.permission.BODY_SENSORS in it.androidPackage!!.requestedPermissions
+ }
+ val isReadHeartRateRequested =
+ anyPackageInAppId(appId, newState) {
+ HealthPermissions.READ_HEART_RATE in it.androidPackage!!.requestedPermissions
+ }
+ val isBodySensorsBackgroundRequested =
+ anyPackageInAppId(appId, newState) {
+ Manifest.permission.BODY_SENSORS_BACKGROUND in
+ it.androidPackage!!.requestedPermissions
+ }
+ val isReadHealthDataInBackgroundRequested =
+ anyPackageInAppId(appId, newState) {
+ HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND in
+ it.androidPackage!!.requestedPermissions
+ }
+
+ // Walk the list of user IDs and revoke states as needed.
+ newState.userStates.forEachIndexed { _, userId, _ ->
+ // First sync BODY_SENSORS and READ_HEART_RATE, if required.
+ var isBodySensorsGranted =
+ isRuntimePermissionGranted(appId, userId, Manifest.permission.BODY_SENSORS)
+ if (isBodySensorsRequested && isReadHeartRateRequested) {
+ val isReadHeartRateGranted =
+ isRuntimePermissionGranted(appId, userId, HealthPermissions.READ_HEART_RATE)
+ if (isBodySensorsGranted != isReadHeartRateGranted) {
+ if (isBodySensorsGranted) {
+ if (
+ revokeRuntimePermission(appId, userId, Manifest.permission.BODY_SENSORS)
+ ) {
+ isBodySensorsGranted = false
+ }
+ }
+ if (isReadHeartRateGranted) {
+ revokeRuntimePermission(appId, userId, HealthPermissions.READ_HEART_RATE)
+ }
+ }
+ }
+
+ // Then check to ensure we haven't put the background/foreground permissions out of
+ // sync.
+ var isBodySensorsBackgroundGranted =
+ isRuntimePermissionGranted(
+ appId,
+ userId,
+ Manifest.permission.BODY_SENSORS_BACKGROUND,
+ )
+ if (isBodySensorsBackgroundGranted && !isBodySensorsGranted) {
+ if (
+ revokeRuntimePermission(
+ appId,
+ userId,
+ Manifest.permission.BODY_SENSORS_BACKGROUND,
+ )
+ ) {
+ isBodySensorsBackgroundGranted = false
+ }
+ }
+
+ // Finally sync BODY_SENSORS_BACKGROUND and READ_HEALTH_DATA_IN_BACKGROUND, if required.
+ if (isBodySensorsBackgroundRequested && isReadHealthDataInBackgroundRequested) {
+ val isReadHealthDataInBackgroundGranted =
+ isRuntimePermissionGranted(
+ appId,
+ userId,
+ HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND,
+ )
+ if (isBodySensorsBackgroundGranted != isReadHealthDataInBackgroundGranted) {
+ if (isBodySensorsBackgroundGranted) {
+ revokeRuntimePermission(
+ appId,
+ userId,
+ Manifest.permission.BODY_SENSORS_BACKGROUND,
+ )
+ }
+ if (isReadHealthDataInBackgroundGranted) {
+ revokeRuntimePermission(
+ appId,
+ userId,
+ HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND,
)
- val newFlags =
- oldFlags andInv (PermissionFlags.RUNTIME_GRANTED or USER_SETTABLE_MASK)
- setPermissionFlags(appId, userId, permissionName, newFlags)
}
}
}
}
}
+ private fun GetStateScope.isRuntimePermissionGranted(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ ): Boolean {
+ val flags = getPermissionFlags(appId, userId, permissionName)
+ return PermissionFlags.isAppOpGranted(flags)
+ }
+
+ fun MutateStateScope.revokeRuntimePermission(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ ): Boolean {
+ Slog.v(
+ LOG_TAG,
+ "Revoking runtime permission for appId: $appId, " +
+ "permission: $permissionName, userId: $userId",
+ )
+ var flags = getPermissionFlags(appId, userId, permissionName)
+ if (flags.hasAnyBit(SYSTEM_OR_POLICY_FIXED_MASK)) {
+ Slog.v(
+ LOG_TAG,
+ "Not allowed to revoke $permissionName for appId: $appId, userId: $userId",
+ )
+ return false
+ }
+
+ flags =
+ flags andInv
+ (PermissionFlags.RUNTIME_GRANTED or
+ USER_SETTABLE_MASK or
+ PermissionFlags.PREGRANT or
+ PermissionFlags.ROLE)
+ setPermissionFlags(appId, userId, permissionName, flags)
+ return true
+ }
+
private fun MutateStateScope.evaluatePermissionStateForAllPackages(
permissionName: String,
installedPackageState: PackageState?,
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
index bd63918e751b..e3e965de4559 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
@@ -441,7 +441,7 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) {
return false
}
- val newFlags =
+ flags =
flags andInv
(PermissionFlags.RUNTIME_GRANTED or
MASK_USER_SETTABLE or
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
index bf9033981442..c0f0369d4774 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
@@ -29,6 +29,7 @@ import com.android.server.pm.pkg.PackageState
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -537,6 +538,442 @@ class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
.isEqualTo(expectedNewFlags)
}
+ /** Setup: BODY_SENSORS: granted, READ_HEART_RATE: not granted Result: BODY_SENSORS: revoked */
+ @Test
+ fun testEvaluatePermissionState_bodySensorReadHrOutOfSync_revokesGrantedBodySensor() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BODY_SENSORS,
+ requestedPermissions = setOf(PERMISSION_BODY_SENSORS, PERMISSION_READ_HEART_RATE),
+ ) {}
+
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime body sensors" +
+ " permission that was granted while read hr was not, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * Setup: BODY_SENSORS: not granted, READ_HEART_RATE: granted Result: READ_HEART_RATE: revoked
+ */
+ @Test
+ fun testEvaluatePermissionState_bodySensorReadHrOutOfSync_revokesGrantedReadHr() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BODY_SENSORS,
+ requestedPermissions = setOf(PERMISSION_BODY_SENSORS, PERMISSION_READ_HEART_RATE),
+ ) {
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_READ_HEART_RATE,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ }
+
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_READ_HEART_RATE)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime body sensors" +
+ " permission that was not granted while read hr was, the actual permission" +
+ "flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * Setup: BODY_SENSORS: granted, READ_HEART_RATE: not granted Result: nothing revoked since the
+ * targetSdk is Baklava
+ */
+ @Test
+ fun testEvaluatePermissionState_bodySensorReadHrOutOfSync_baklavaTargetSdk_nothingRevoked() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BODY_SENSORS,
+ installedPackageTargetSdkVersion = Build.VERSION_CODES.BAKLAVA,
+ requestedPermissions = setOf(PERMISSION_BODY_SENSORS, PERMISSION_READ_HEART_RATE),
+ ) {}
+
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime body sensors" +
+ " permission that was granted while read hr was not targeting Baklava," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * Setup: BODY_SENSORS_BACKGROUND: granted, BODY_SENSORS: not granted Result:
+ * BODY_SENSORS_BACKGROUND: revoked
+ */
+ @Test
+ fun testEvaluatePermissionState_bodySensorBackgroundGrantMismatch_revokesBackground() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BODY_SENSORS,
+ requestedPermissions =
+ setOf(PERMISSION_BODY_SENSORS, PERMISSION_BODY_SENSORS_BACKGROUND),
+ ) {
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_BODY_SENSORS_BACKGROUND,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ }
+
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS_BACKGROUND)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime body sensors" +
+ " permission that was not granted while body sensors background was," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * Setup: BODY_SENSORS_BACKGROUND: granted, BODY_SENSORS: not requested Result:
+ * BODY_SENSORS_BACKGROUND: revoked
+ */
+ @Test
+ fun testEvaluatePermissionState_bodySensorBackgroundMissingForeground_baklavaTargetSdk_revokesBackground() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BODY_SENSORS_BACKGROUND,
+ requestedPermissions = setOf(PERMISSION_BODY_SENSORS_BACKGROUND),
+ ) {}
+
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS_BACKGROUND)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that has runtime body sensors background" +
+ " permission granted but is not requesting the body sensors foreground" +
+ " permission, the actual permission flags $actualFlags should match the" +
+ " expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * Setup: BODY_SENSORS_BACKGROUND: granted, BODY_SENSORS: granted, READ_HEART_RATE: not granted
+ * Result: BODY_SENSORS_BACKGROUND: revoked
+ */
+ @Test
+ fun testEvaluatePermissionState_bodySensorHeartRateOutOfSync_revokesGrantedBodySensorBackground() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BODY_SENSORS,
+ installedPackageTargetSdkVersion = Build.VERSION_CODES.BAKLAVA,
+ requestedPermissions =
+ setOf(PERMISSION_BODY_SENSORS, PERMISSION_READ_HEART_RATE, PERMISSION_BODY_SENSORS),
+ ) {
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_BODY_SENSORS_BACKGROUND,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ }
+
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS_BACKGROUND)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime body sensors" +
+ " permission that was granted while read hr was not targeting Baklava," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * Setup: BODY_SENSORS_BACKGROUND: granted, READ_HEALTH_DATA_IN_BACKGROUND: not granted Result:
+ * BODY_SENSORS_BACKGROUND: revoked
+ */
+ @Test
+ fun testEvaluatePermissionState_bodySensorReadHealthBackgroundOutOfSync_revokesGrantedBodySensorBackground() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BODY_SENSORS_BACKGROUND,
+ requestedPermissions =
+ setOf(PERMISSION_BODY_SENSORS_BACKGROUND, PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND),
+ ) {
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ }
+
+ val actualFlags =
+ getPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND,
+ )
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime body sensors" +
+ " background permission that was not granted while read health data in" +
+ " background was, the actual permission flags $actualFlags should match" +
+ " the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * Setup: BODY_SENSORS_BACKGROUND: not granted, READ_HEALTH_DATA_IN_BACKGROUND: granted Result:
+ * READ_HEALTH_DATA_IN_BACKGROUND: revoked
+ */
+ @Test
+ fun testEvaluatePermissionState_bodySensorReadHealthBackgroundOutOfSync_revokesGrantedReadHealthBackground() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BODY_SENSORS_BACKGROUND,
+ requestedPermissions =
+ setOf(PERMISSION_BODY_SENSORS_BACKGROUND, PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND),
+ ) {}
+
+ val actualFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS_BACKGROUND)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime body sensors" +
+ " background permission that was granted while read health data in" +
+ " background was not, the actual permission flags $actualFlags should match" +
+ " the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * The sequence of events here is:
+ *
+ * Starting:
+ * - READ_HR=not granted
+ * - BODY_SENSORS=granted
+ * - BODY_SENSORS_BACKGROUND=granted,
+ * - READ_HEALTH_DATA_IN_BACKGROUND=granted
+ *
+ * Actions:
+ * - BODY_SENSORS->revoked (due to READ_HR mismatch)
+ * - BODY_SENSORS_BACKGROUND->revoked (due to new BODY_SENSORS mismatch)
+ * - READ_HEALTH_DATA_IN_BACKGROUND->revoked (due to new BODY_SENSORS_BACKGROUND mismatch)
+ *
+ * End result: All permissions revoked.
+ */
+ @Test
+ fun testEvaluatePermissionState_healthPermissionsSync_revocationChain() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_READ_HEART_RATE,
+ requestedPermissions =
+ setOf(
+ PERMISSION_READ_HEART_RATE,
+ PERMISSION_BODY_SENSORS,
+ PERMISSION_BODY_SENSORS_BACKGROUND,
+ PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND,
+ ),
+ ) {
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_BODY_SENSORS,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_BODY_SENSORS_BACKGROUND,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ }
+
+ val bodySensorsFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS)
+ val bodySensorsBackgroundFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS_BACKGROUND)
+ val readHealthDataInBackgroundFlags =
+ getPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND,
+ )
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that has mismatching health permissions," +
+ " the actual permission flags for body sensors $bodySensorsFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(bodySensorsFlags)
+ .isEqualTo(expectedNewFlags)
+ assertWithMessage(
+ "After $action is called for a package that has mismatching health permissions," +
+ " the actual permission flags for body sensors background" +
+ " $bodySensorsBackgroundFlags should match the expected flags" +
+ " $expectedNewFlags"
+ )
+ .that(bodySensorsBackgroundFlags)
+ .isEqualTo(expectedNewFlags)
+ assertWithMessage(
+ "After $action is called for a package that has mismatching health permissions," +
+ " the actual permission flags for read health data in background" +
+ " $readHealthDataInBackgroundFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(readHealthDataInBackgroundFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * Similar to test case above but this time the READ_HR permission is going implicitly to
+ * explicitly granted (which causes it's grant to be revoked).
+ *
+ * Starting:
+ * - READ_HR=imlpicitly granted
+ * - BODY_SENSORS=granted
+ * - BODY_SENSORS_BACKGROUND=granted,
+ * - READ_HEALTH_DATA_IN_BACKGROUND=granted
+ *
+ * Actions:
+ * - READ_HR->revoked (due to implicit permission being explicitly requested)
+ * - BODY_SENSORS->revoked (due to READ_HR mismatch)
+ * - BODY_SENSORS_BACKGROUND->revoked (due to new BODY_SENSORS mismatch)
+ * - READ_HEALTH_DATA_IN_BACKGROUND->revoked (due to new BODY_SENSORS_BACKGROUND mismatch)
+ *
+ * End result: All permissions revoked.
+ */
+ @Test
+ fun testEvaluatePermissionState_implicitHealthPermissionRequested_causesRevocationChain() {
+ assumeTrue(action != Action.ON_USER_ADDED)
+ val oldFlags =
+ PermissionFlags.IMPLICIT or
+ PermissionFlags.RUNTIME_GRANTED or
+ PermissionFlags.USER_SET or
+ PermissionFlags.USER_FIXED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_READ_HEART_RATE,
+ requestedPermissions =
+ setOf(
+ PERMISSION_READ_HEART_RATE,
+ PERMISSION_BODY_SENSORS,
+ PERMISSION_BODY_SENSORS_BACKGROUND,
+ PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND,
+ ),
+ ) {
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_BODY_SENSORS,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_BODY_SENSORS_BACKGROUND,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND,
+ PermissionFlags.RUNTIME_GRANTED,
+ )
+ }
+
+ val bodySensorsFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS)
+ val bodySensorsBackgroundFlags =
+ getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_BODY_SENSORS_BACKGROUND)
+ val readHealthDataInBackgroundFlags =
+ getPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND,
+ )
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that has mismatching health permissions," +
+ "the actual permission flags for body sensors $bodySensorsFlags should match the" +
+ "expected flags $expectedNewFlags"
+ )
+ .that(bodySensorsFlags)
+ .isEqualTo(expectedNewFlags)
+ assertWithMessage(
+ "After $action is called for a package that has mismatching health permissions," +
+ "the actual permission flags for body sensors background $bodySensorsBackgroundFlags should" +
+ "match the expected flags $expectedNewFlags"
+ )
+ .that(bodySensorsBackgroundFlags)
+ .isEqualTo(expectedNewFlags)
+ assertWithMessage(
+ "After $action is called for a package that has mismatching health permissions," +
+ "the actual permission flags for read health data in background $readHealthDataInBackgroundFlags" +
+ "should match the expected flags $expectedNewFlags"
+ )
+ .that(readHealthDataInBackgroundFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
@Test
fun testEvaluatePermissionState_noLongerImplicitSystemOrPolicyFixedWasGranted_runtimeGranted() {
val oldFlags =
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
index 207820cc3135..6dfd2611e0af 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager
import android.content.pm.PermissionGroupInfo
import android.content.pm.PermissionInfo
import android.content.pm.SigningDetails
+import android.health.connect.HealthPermissions
import android.os.Build
import android.os.Bundle
import android.util.ArrayMap
@@ -390,6 +391,14 @@ abstract class BasePermissionPolicyTest {
Manifest.permission.ACCESS_BACKGROUND_LOCATION
@JvmStatic
protected val PERMISSION_ACCESS_MEDIA_LOCATION = Manifest.permission.ACCESS_MEDIA_LOCATION
+ @JvmStatic protected val PERMISSION_BODY_SENSORS = Manifest.permission.BODY_SENSORS
+ @JvmStatic
+ protected val PERMISSION_BODY_SENSORS_BACKGROUND =
+ Manifest.permission.BODY_SENSORS_BACKGROUND
+ @JvmStatic protected val PERMISSION_READ_HEART_RATE = HealthPermissions.READ_HEART_RATE
+ @JvmStatic
+ protected val PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND =
+ HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND
@JvmStatic protected val USER_ID_0 = 0
@JvmStatic protected val USER_ID_NEW = 1