Add tests for attribution chain indicators
Test: atest CameraMicIndicatorsPermissionTest
Bug: 212434116
Change-Id: I03e820cfb7653ea7febdcd7bce2e2e05fcef3062
diff --git a/tests/tests/permission4/AndroidManifest.xml b/tests/tests/permission4/AndroidManifest.xml
index d4cc71a..4c5e49a 100644
--- a/tests/tests/permission4/AndroidManifest.xml
+++ b/tests/tests/permission4/AndroidManifest.xml
@@ -22,12 +22,10 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
<application>
-
<uses-library android:name="android.test.runner" />
-
- <activity android:name=".StartForFutureActivity" />
</application>
<instrumentation
diff --git a/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt b/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
index 06aef15..659f228 100644
--- a/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
+++ b/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
@@ -83,6 +83,7 @@
}
override fun finish() {
+ super.finish()
cameraDevice?.close()
cameraDevice = null
recorder?.stop()
@@ -95,7 +96,6 @@
packageName)
}
appOpsManager = null
- super.finish()
}
override fun onStop() {
diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
index 4128de9..de277d4 100644
--- a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
+++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
@@ -19,11 +19,13 @@
import android.app.Instrumentation
import android.app.UiAutomation
import android.app.compat.CompatChanges
+import android.content.AttributionSource
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.hardware.camera2.CameraManager
import android.os.Process
+import android.permission.PermissionManager
import android.provider.DeviceConfig
import android.provider.Settings
import android.server.wm.WindowManagerStateHelper
@@ -37,6 +39,8 @@
import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import org.junit.After
+import org.junit.Assert
+import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
@@ -47,11 +51,14 @@
import org.junit.Test
private const val APP_LABEL = "CtsCameraMicAccess"
+private const val APP_PKG = "android.permission4.cts.appthataccessescameraandmic"
+private const val SHELL_PKG = "com.android.shell"
private const val USE_CAMERA = "use_camera"
private const val USE_MICROPHONE = "use_microphone"
private const val USE_HOTWORD = "use_hotword"
private const val INTENT_ACTION = "test.action.USE_CAMERA_OR_MIC"
private const val PRIVACY_CHIP_ID = "com.android.systemui:id/privacy_chip"
+private const val PRIVACY_ITEM_ID = "com.android.systemui:id/privacy_item"
private const val INDICATORS_FLAG = "camera_mic_icons_enabled"
private const val PERMISSION_INDICATORS_NOT_PRESENT = 162547999L
private const val IDLE_TIMEOUT_MILLIS: Long = 1000
@@ -65,6 +72,8 @@
private val uiAutomation: UiAutomation = instrumentation.uiAutomation
private val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
private val packageManager: PackageManager = context.packageManager
+ private val permissionManager: PermissionManager =
+ context.getSystemService(PermissionManager::class.java)!!
private val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
private var wasEnabled = false
@@ -160,26 +169,50 @@
testCameraAndMicIndicator(useMic = false, useCamera = false, useHotword = true)
}
+ @Test
+ fun testChainUsageWithOtherUsage() {
+ // TV has only the mic icon
+ assumeFalse(isTv)
+ testCameraAndMicIndicator(useMic = false, useCamera = true, chainUsage = true)
+ }
+
private fun testCameraAndMicIndicator(
useMic: Boolean,
useCamera: Boolean,
- useHotword: Boolean = false
+ useHotword: Boolean = false,
+ chainUsage: Boolean = false
) {
+ var chainAttribution: AttributionSource? = null
openApp(useMic, useCamera, useHotword)
- eventually {
- val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
- assertTrue("View with text $APP_LABEL not found", appView.exists())
- }
+ try {
+ eventually {
+ val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+ assertTrue("View with text $APP_LABEL not found", appView.exists())
+ }
+ if (chainUsage) {
+ chainAttribution = createChainAttribution()
+ runWithShellPermissionIdentity {
+ val ret = permissionManager.checkPermissionForStartDataDelivery(
+ Manifest.permission.RECORD_AUDIO, chainAttribution!!, "")
+ Assert.assertEquals(PermissionManager.PERMISSION_GRANTED, ret)
+ }
+ }
- if (isTv) {
- assertTvIndicatorsShown(useMic, useCamera, useHotword)
- } else if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- assertCarIndicatorsShown(useMic, useCamera, useHotword)
- } else {
- // Hotword gets remapped to RECORD_AUDIO on handheld, so handheld should show a mic
- // indicator
- uiDevice.openQuickSettings()
- assertPrivacyChipAndIndicatorsPresent(useMic || useHotword, useCamera)
+ if (isTv) {
+ assertTvIndicatorsShown(useMic, useCamera, useHotword)
+ } else if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ assertCarIndicatorsShown(useMic, useCamera, useHotword, chainUsage)
+ } else {
+ // Hotword gets remapped to RECORD_AUDIO on handheld, so handheld should show a mic
+ // indicator
+ uiDevice.openQuickSettings()
+ assertPrivacyChipAndIndicatorsPresent(useMic || useHotword, useCamera, chainUsage)
+ }
+ } finally {
+ if (chainAttribution != null) {
+ permissionManager.finishDataDelivery(Manifest.permission.RECORD_AUDIO,
+ chainAttribution!!)
+ }
}
}
@@ -201,7 +234,12 @@
}
}
- private fun assertCarIndicatorsShown(useMic: Boolean, useCamera: Boolean, useHotword: Boolean) {
+ private fun assertCarIndicatorsShown(
+ useMic: Boolean,
+ useCamera: Boolean,
+ useHotword: Boolean,
+ chainUsage: Boolean
+ ) {
// Ensure the privacy chip is present (or not)
var chipFound = false
try {
@@ -222,6 +260,10 @@
}
eventually {
+ if (chainUsage) {
+ assertChainMicAndOtherCameraUsed()
+ return@eventually
+ }
if (useCamera || useHotword) {
// There should be no microphone dialog when using hot word and/or camera
val micLabelView = uiDevice.findObject(UiSelector().textContains(micLabel))
@@ -239,7 +281,11 @@
}
}
- private fun assertPrivacyChipAndIndicatorsPresent(useMic: Boolean, useCamera: Boolean) {
+ private fun assertPrivacyChipAndIndicatorsPresent(
+ useMic: Boolean,
+ useCamera: Boolean,
+ chainUsage: Boolean
+ ) {
// Ensure the privacy chip is present
eventually {
val privacyChip = uiDevice.findObject(UiSelector().resourceId(PRIVACY_CHIP_ID))
@@ -248,6 +294,10 @@
}
eventually {
+ if (chainUsage) {
+ assertChainMicAndOtherCameraUsed()
+ return@eventually
+ }
if (useMic) {
val iconView = uiDevice.findObject(UiSelector().descriptionContains(micLabel))
assertTrue("View with description $micLabel not found", iconView.exists())
@@ -261,6 +311,38 @@
}
}
+ private fun createChainAttribution(): AttributionSource? {
+ var attrSource: AttributionSource? = null
+ runWithShellPermissionIdentity {
+ try {
+ val appUid = packageManager.getPackageUid(APP_PKG, 0)
+ val childAttribution = AttributionSource(appUid, APP_PKG, null)
+ val attribution = AttributionSource(Process.myUid(), context.packageName, null,
+ null, permissionManager.registerAttributionSource(childAttribution))
+ attrSource = permissionManager.registerAttributionSource(attribution)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Assert.fail("Expected to find a UID for $APP_LABEL")
+ }
+ }
+ return attrSource
+ }
+
+ private fun assertChainMicAndOtherCameraUsed() {
+ val shellLabel = try {
+ context.packageManager.getApplicationInfo(SHELL_PKG, 0)
+ .loadLabel(context.packageManager).toString()
+ } catch (e: PackageManager.NameNotFoundException) {
+ "Did not find shell package"
+ }
+
+ val usageViews = uiDevice.findObjects(By.res(PRIVACY_ITEM_ID))
+ assertEquals("Expected two usage views", 2, usageViews.size)
+ val appViews = uiDevice.findObjects(By.textContains(APP_LABEL))
+ assertEquals("Expected two $APP_LABEL view", 2, appViews.size)
+ val shellView = uiDevice.findObjects(By.textContains(shellLabel))
+ assertEquals("Expected only one shell view", 1, shellView.size)
+ }
+
private fun pressBack() {
uiDevice.pressBack()
waitForIdle()