summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt61
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt22
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleManagerSecurityTest.kt148
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleManagerTest.java17
4 files changed, 207 insertions, 41 deletions
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt
index f52e32344..9ec09137e 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt
@@ -36,6 +36,7 @@ import android.os.Build
import android.os.Process
import android.provider.DeviceConfig
import android.provider.Settings
+import android.server.wm.WindowManagerStateHelper
import android.text.Spanned
import android.text.style.ClickableSpan
import android.util.Log
@@ -63,6 +64,7 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
+import java.util.concurrent.TimeoutException
abstract class BaseUsePermissionTest : BasePermissionTest() {
companion object {
@@ -247,6 +249,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
DENIED_WITH_PREJUDICE
}
+ private val windowManagerStateHelper = WindowManagerStateHelper()
+
private val platformResources = context.createPackageContext("android", 0).resources
private val permissionToLabelResNameMap =
mapOf(
@@ -674,21 +678,20 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
crossinline block: () -> Unit,
): Instrumentation.ActivityResult {
// Request the permissions
- lateinit var future: CompletableFuture<Instrumentation.ActivityResult>
- doAndWaitForWindowTransition {
- future =
- startActivityForFuture(
- Intent().apply {
- component =
- ComponentName(
- APP_PACKAGE_NAME,
- "$APP_PACKAGE_NAME.RequestPermissionsActivity"
- )
- putExtra("$APP_PACKAGE_NAME.PERMISSIONS", permissions)
- putExtra("$APP_PACKAGE_NAME.ASK_TWICE", askTwice)
- }
- )
- }
+ val future =
+ startActivityForFuture(
+ Intent().apply {
+ component =
+ ComponentName(
+ APP_PACKAGE_NAME,
+ "$APP_PACKAGE_NAME.RequestPermissionsActivity"
+ )
+ putExtra("$APP_PACKAGE_NAME.PERMISSIONS", permissions)
+ putExtra("$APP_PACKAGE_NAME.ASK_TWICE", askTwice)
+ }
+ )
+
+ waitForPermissionRequestActivity()
// Notification permission prompt is shown first, so get it out of the way
clickNotificationPermissionRequestAllowButtonIfAvailable()
@@ -698,7 +701,33 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
} else {
block()
}
- return future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ try {
+ return future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ } catch (e: TimeoutException) {
+ val uiDump = StringBuilder()
+ UiDumpUtils.dumpNodes(uiDump)
+ Log.e(LOG_TAG, "Timed out waiting for activity result, UI dump: $uiDump")
+ throw e
+ }
+ }
+
+ /**
+ * This method waits for permission controller activity to be in a valid state, the timeout
+ * is 5 seconds.
+ */
+ fun waitForPermissionRequestActivity() {
+ val requestPermissionIntent = Intent(PackageManager.ACTION_REQUEST_PERMISSIONS)
+ val componentName =
+ requestPermissionIntent.resolveActivity(context.packageManager)
+ ?: throw RuntimeException("Permission request is not handled by any activity.")
+ try {
+ windowManagerStateHelper.waitForValidState(componentName)
+ } catch (ex: Exception) {
+ // It doesn't mean a test would fail, it just meant that the test would proceed before
+ // waiting for permission request dialog. Permission request dialog should eventually
+ // come on the screen when ui-automator is trying to search for ui element.
+ Log.w(LOG_TAG, "Couldn't wait for permission request activity.", ex)
+ }
}
protected inline fun requestAppPermissionsAndAssertResult(
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
index 798b1942f..e1cb76171 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
@@ -123,7 +123,6 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
.toString()
private val cameraLabel = originalCameraLabel.lowercase()
private val micLabel = originalMicLabel.lowercase()
- private var wasEnabled = false
private var isScreenOn = false
private var screenTimeoutBeforeTest: Long = 0L
private lateinit var carMicPrivacyChipId: String
@@ -181,7 +180,6 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
isScreenOn = true
}
uiDevice.findObject(By.text("Close"))?.click()
- wasEnabled = setIndicatorsEnabledStateIfNeeded(true)
// If the change Id is not present, then isChangeEnabled will return true. To bypass this,
// the change is set to "false" if present.
assumeFalse(
@@ -193,23 +191,6 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
install()
}
- private fun setIndicatorsEnabledStateIfNeeded(shouldBeEnabled: Boolean): Boolean {
- var currentlyEnabled = false
- runWithShellPermissionIdentity {
- currentlyEnabled =
- DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, INDICATORS_FLAG, true)
- if (currentlyEnabled != shouldBeEnabled) {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_PRIVACY,
- INDICATORS_FLAG,
- shouldBeEnabled.toString(),
- false,
- )
- }
- }
- return currentlyEnabled
- }
-
@After
fun tearDown() {
// Skip the tests as Camera and Mic are not supported for visible background users.
@@ -225,9 +206,6 @@ class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
{ assertIndicatorsShown(false, false, false) },
AUTO_MIC_INDICATOR_DISMISSAL_TIMEOUT_MS,
)
- if (!wasEnabled) {
- setIndicatorsEnabledStateIfNeeded(false)
- }
runWithShellPermissionIdentity {
Settings.System.putLong(
context.contentResolver,
diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerSecurityTest.kt b/tests/cts/role/src/android/app/role/cts/RoleManagerSecurityTest.kt
new file mode 100644
index 000000000..59a8c21b2
--- /dev/null
+++ b/tests/cts/role/src/android/app/role/cts/RoleManagerSecurityTest.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.role.cts
+
+import android.app.role.RoleManager
+import android.content.Context
+import android.os.Build
+import android.os.Process
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests {@link RoleManager} security fixes. */
+@RunWith(AndroidJUnit4::class)
+class RoleManagerSecurityTest {
+ private var browserRoleHolder: String? = null
+
+ @Before
+ fun setUp() {
+ saveBrowserRoleHolder()
+ }
+
+ @After
+ fun tearDown() {
+ restoreBrowserRoleHolder()
+ }
+
+ private fun saveBrowserRoleHolder() {
+ val roleHolders: List<String> = getRoleHolders(RoleManager.ROLE_BROWSER, roleManager)
+ browserRoleHolder = if (roleHolders.isNotEmpty()) roleHolders[0] else null
+ }
+
+ private fun restoreBrowserRoleHolder() {
+ browserRoleHolder?.let { packageName ->
+ addRoleHolderAsUser(
+ RoleManager.ROLE_BROWSER,
+ packageName,
+ Process.myUserHandle(),
+ true,
+ roleManager,
+ context.mainExecutor,
+ )
+ }
+ }
+
+ private fun getRoleHolders(roleName: String, roleManager: RoleManager): List<String> {
+ return SystemUtil.callWithShellPermissionIdentity { roleManager.getRoleHolders(roleName) }
+ }
+
+ private fun addRoleHolderAsUser(
+ roleName: String,
+ packageName: String,
+ userHandle: UserHandle,
+ expectSuccess: Boolean,
+ roleManager: RoleManager,
+ executor: Executor,
+ ) {
+ val future = CallbackFuture()
+ SystemUtil.runWithShellPermissionIdentity {
+ roleManager.addRoleHolderAsUser(roleName, packageName, 0, userHandle, executor, future)
+ }
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isEqualTo(expectSuccess)
+ }
+
+ @SdkSuppress(
+ minSdkVersion = Build.VERSION_CODES.S,
+ maxSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ )
+ @Test
+ fun cannotGetDefaultApplicationOnOlderSdk() {
+ assumeTrue(roleManager.isRoleAvailable(RoleManager.ROLE_BROWSER))
+ try {
+ roleManager.getDefaultApplication(RoleManager.ROLE_BROWSER)
+ } catch (e: NoSuchMethodError) {
+ // Expected when permission module hasn't been updated
+ } catch (e: IllegalStateException) {
+ // Expected when permission module has been updated, and SDK 33 or below
+ } catch (e: Throwable) {
+ fail("Missing patch for cveBugId = [379362792]")
+ }
+ }
+
+ @SdkSuppress(
+ minSdkVersion = Build.VERSION_CODES.S,
+ maxSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ )
+ @Test
+ fun cannotSetDefaultApplicationOnOlderSdk() {
+ assumeTrue(roleManager.isRoleAvailable(RoleManager.ROLE_BROWSER))
+ val future = CallbackFuture()
+ try {
+ roleManager.setDefaultApplication(
+ RoleManager.ROLE_BROWSER,
+ APP_PACKAGE_NAME,
+ 0,
+ context.mainExecutor,
+ future,
+ )
+ future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ } catch (e: NoSuchMethodError) {
+ // Expected when permission module hasn't been updated
+ } catch (e: IllegalStateException) {
+ // Expected when permission module has been updated, and SDK 33 or below
+ } catch (e: Throwable) {
+ fail("Missing patch for cveBugId = [379362792]")
+ }
+ }
+
+ private class CallbackFuture : CompletableFuture<Boolean?>(), Consumer<Boolean?> {
+ override fun accept(successful: Boolean?) {
+ complete(successful)
+ }
+ }
+
+ companion object {
+ private const val TIMEOUT_MILLIS: Long = (15 * 1000).toLong()
+ private const val APP_PACKAGE_NAME: String = "android.app.role.cts.app"
+
+ private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val roleManager: RoleManager = context.getSystemService(RoleManager::class.java)
+ }
+}
diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
index 5e61c66be..3bc3ff12b 100644
--- a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
@@ -105,7 +105,6 @@ public class RoleManagerTest {
private static final String ROLE_NAME = RoleManager.ROLE_BROWSER;
private static final String ROLE_PHONE_NAME = RoleManager.ROLE_DIALER;
- private static final String ROLE_SMS_NAME = RoleManager.ROLE_SMS;
private static final String PROFILE_GROUP_EXCLUSIVE_ROLE_NAME =
RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY;
private static final String ROLE_SHORT_LABEL = "Browser app";
@@ -288,8 +287,11 @@ public class RoleManagerTest {
@RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
@FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
public void requestRoleThenBlockRequestRoleDialogByRestrictedSettingDialog() throws Exception {
- assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
assumeFalse(sIsWatch || sIsAutomotive || sIsTelevision);
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+ assumeFalse(callWithShellPermissionIdentity(
+ () -> getRoleHolders(RoleManager.ROLE_SMS)).contains(APP_PACKAGE_NAME));
+
// TODO: b/388960315 - Remove wait after addressing race condition
runWithShellPermissionIdentity(
() -> waitForEnhancedConfirmationRestrictedAppOpMode(sContext, APP_PACKAGE_NAME,
@@ -298,7 +300,7 @@ public class RoleManagerTest {
() -> setEnhancedConfirmationRestrictedAppOpMode(sContext, APP_PACKAGE_NAME,
AppOpsManager.MODE_ERRORED));
- requestRole(ROLE_SMS_NAME);
+ requestRole(RoleManager.ROLE_SMS);
waitFindObject(ENHANCED_CONFIRMATION_DIALOG_SELECTOR, TIMEOUT_MILLIS);
pressBack();
@@ -829,6 +831,13 @@ public class RoleManagerTest {
@FlakyTest
@Test
public void openDefaultAppListThenIsNotDefaultAppInList() throws Exception {
+ assumeFalse(callWithShellPermissionIdentity(
+ () -> getRoleHolders(RoleManager.ROLE_BROWSER)).contains(APP_PACKAGE_NAME));
+ assumeFalse(callWithShellPermissionIdentity(
+ () -> getRoleHolders(RoleManager.ROLE_DIALER)).contains(APP_PACKAGE_NAME));
+ assumeFalse(callWithShellPermissionIdentity(
+ () -> getRoleHolders(RoleManager.ROLE_SMS)).contains(APP_PACKAGE_NAME));
+
sContext.startActivity(new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
.addCategory(Intent.CATEGORY_DEFAULT)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
@@ -1193,6 +1202,8 @@ public class RoleManagerTest {
@Test
public void removeSmsRoleHolderThenPermissionIsRevoked() throws Exception {
assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+ assumeFalse(callWithShellPermissionIdentity(
+ () -> getRoleHolders(RoleManager.ROLE_SMS)).contains(APP_PACKAGE_NAME));
String smsRoleHolder = getRoleHolders(RoleManager.ROLE_SMS).get(0);
addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME);