diff options
author | 2023-10-06 13:01:22 +0000 | |
---|---|---|
committer | 2023-10-11 09:53:40 +0000 | |
commit | 1a104954590080caac06ded0e63e452e9bd8bf06 (patch) | |
tree | 4715c21ce5ef0c4270f1bb372087143f997b44ed | |
parent | 4b0638624690a39de2c35ac5e00f17136973aad8 (diff) |
Fix Intent resolution ordering.
The intended order was to have the implicit intent checked first.
However, we changed it to check the explicit intent first in
ag/21033470 as resolveActivity always returned a value regardless
of whether the non-exported internal activity actually resolved.
By checking manually if the activity is exported, we can use
the original (intended) order, which fixes an issue with the
overriding of the OTA source intent by setting a higher priority.
Test: atest SafetyCenterFunctionalTestCases
Bug: 296053629
Bug: 282920658
Relnote: Fix intent resolution ordering for Safety Center, which
allows overridng OTA source intent by setting higher priority.
Change-Id: I332200401340deb48f9ebd23d841f4e01c9d9e0d
8 files changed, 329 insertions, 166 deletions
diff --git a/service/java/com/android/safetycenter/PendingIntentFactory.java b/service/java/com/android/safetycenter/PendingIntentFactory.java index b52365c38..a857a07cb 100644 --- a/service/java/com/android/safetycenter/PendingIntentFactory.java +++ b/service/java/com/android/safetycenter/PendingIntentFactory.java @@ -22,6 +22,7 @@ import android.annotation.UserIdInt; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ResolveInfoFlags; import android.content.pm.ResolveInfo; @@ -60,9 +61,9 @@ public final class PendingIntentFactory { * Creates or retrieves a {@link PendingIntent} that will start a new {@code Activity} matching * the given {@code intentAction}. * - * <p>If the given {@code intentAction} resolves for the given {@code packageName}, the {@link - * PendingIntent} will explicitly target the {@code packageName}. If the {@code intentAction} - * resolves elsewhere, the {@link PendingIntent} will be implicit. + * <p>If the given {@code intentAction} resolves, the {@link PendingIntent} will use an implicit + * {@link Intent}. Otherwise, the {@link PendingIntent} will explicitly target the {@code + * packageName} if it resolves. * * <p>The {@code PendingIntent} is associated with a specific source given by {@code sourceId}. * @@ -113,26 +114,25 @@ public final class PendingIntentFactory { intent.setIdentifier("with_settings_homepage_extra"); } + if (intentResolvesToActivity(packageContext, intent)) { + return intent; + } + // If the intent resolves for the package provided, then we make the assumption that it is // the desired app and make the intent explicit. This is to workaround implicit internal // intents that may not be exported which will stop working on Android U+. - // This assumes that the source or the caller has the highest priority to resolve the intent - // action. Intent explicitIntent = new Intent(intent).setPackage(packageContext.getPackageName()); - if (intentResolves(packageContext, explicitIntent)) { + if (intentResolvesToActivity(packageContext, explicitIntent)) { return explicitIntent; } - if (intentResolves(packageContext, intent)) { - return intent; - } - // resolveActivity does not return any activity when the work profile is in quiet mode, even // though it opens the quiet mode dialog and/or the original intent would otherwise resolve // when quiet mode is turned off. So, we assume that the explicit intent will always resolve // to this dialog. This heuristic is preferable on U+ as it has a higher chance of resolving // once the work profile is enabled considering the implicit internal intent restriction. if (isQuietModeEnabled) { + // TODO(b/266538628): Find a way to fix this, this heuristic isn't ideal. return explicitIntent; } @@ -147,8 +147,20 @@ public final class PendingIntentFactory { .contains(sourceId); } - private static boolean intentResolves(Context packageContext, Intent intent) { - return resolveActivity(packageContext, intent) != null; + private static boolean intentResolvesToActivity(Context packageContext, Intent intent) { + ResolveInfo resolveInfo = resolveActivity(packageContext, intent); + if (resolveInfo == null) { + return false; + } + ActivityInfo activityInfo = resolveInfo.activityInfo; + if (activityInfo == null) { + return false; + } + boolean intentIsImplicit = intent.getPackage() == null && intent.getComponent() == null; + if (intentIsImplicit) { + return activityInfo.exported; + } + return true; } @Nullable diff --git a/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt index 9534b597a..acbc5cfc0 100644 --- a/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt +++ b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt @@ -20,7 +20,6 @@ import android.Manifest.permission.INTERACT_ACROSS_USERS import android.Manifest.permission.INTERACT_ACROSS_USERS_FULL import android.app.PendingIntent import android.content.Context -import android.content.Intent import android.os.UserHandle import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterEntry @@ -60,7 +59,6 @@ import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.get import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.setSafetySourceDataWithPermission import com.android.safetycenter.testing.SafetyCenterFlags import com.android.safetycenter.testing.SafetyCenterTestConfigs -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_BAREBONE_ID import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_DISABLED_ID import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_GROUP_ID @@ -244,69 +242,67 @@ class SafetyCenterMultiUsersTest { private val staticAllOptional = safetyCenterTestData.safetyCenterEntryDefaultStaticBuilder(STATIC_ALL_OPTIONAL_ID).build() - private fun staticAllOptionalForWorkBuilder(inQuietMode: Boolean = false) = - safetyCenterTestData - .safetyCenterEntryDefaultStaticBuilder( - STATIC_ALL_OPTIONAL_ID, - userId = deviceState.workProfile().id(), - title = "Paste" - ) - .setPendingIntent( - createTestActivityRedirectPendingIntentForUser( - deviceState.workProfile().userHandle(), - inQuietMode + private val staticAllOptionalForWorkBuilder + get() = + safetyCenterTestData + .safetyCenterEntryDefaultStaticBuilder( + STATIC_ALL_OPTIONAL_ID, + userId = deviceState.workProfile().id(), + title = "Paste" + ) + .setPendingIntent( + createTestActivityRedirectPendingIntentForUser( + deviceState.workProfile().userHandle(), + explicit = false + ) ) - ) private val staticAllOptionalForWork - get() = staticAllOptionalForWorkBuilder().build() + get() = staticAllOptionalForWorkBuilder.build() private val staticAllOptionalForWorkPaused get() = - staticAllOptionalForWorkBuilder(inQuietMode = true) + staticAllOptionalForWorkBuilder .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused")) .setEnabled(false) .build() - private val staticEntry: SafetyCenterStaticEntry - get() = - SafetyCenterStaticEntry.Builder("OK") - .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) - .build() + private fun createStaticEntry(explicit: Boolean = true): SafetyCenterStaticEntry = + SafetyCenterStaticEntry.Builder("OK") + .setSummary("OK") + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent(explicit) + ) + .build() private val staticEntryUpdated: SafetyCenterStaticEntry get() = SafetyCenterStaticEntry.Builder("Unspecified title") .setSummary("Unspecified summary") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent()) .build() - private fun staticEntryForWorkBuilder( - title: CharSequence = "Paste", - inQuietMode: Boolean = false - ) = + private fun staticEntryForWorkBuilder(title: CharSequence = "Paste", explicit: Boolean = true) = SafetyCenterStaticEntry.Builder(title) .setSummary("OK") .setPendingIntent( createTestActivityRedirectPendingIntentForUser( deviceState.workProfile().userHandle(), - inQuietMode + explicit ) ) - private val staticEntryForWork: SafetyCenterStaticEntry - get() = staticEntryForWorkBuilder().build() + private fun createStaticEntryForWork(explicit: Boolean = true): SafetyCenterStaticEntry = + staticEntryForWorkBuilder(explicit = explicit).build() - private val staticEntryForWorkPaused: SafetyCenterStaticEntry - get() = - staticEntryForWorkBuilder(inQuietMode = true) - .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused")) - .build() + private fun createStaticEntryForWorkPaused(): SafetyCenterStaticEntry = + staticEntryForWorkBuilder(explicit = false) + .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused")) + .build() - private val staticEntryForWorkPausedUpdated + private val staticEntryForWorkPausedUpdated: SafetyCenterStaticEntry get() = - staticEntryForWorkBuilder(title = "Unspecified title for Work", inQuietMode = true) + staticEntryForWorkBuilder(title = "Unspecified title for Work") .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused")) .build() @@ -314,7 +310,7 @@ class SafetyCenterMultiUsersTest { get() = SafetyCenterStaticEntry.Builder("Unspecified title for Work") .setSummary("Unspecified summary") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent()) .build() private val safetyCenterDataForAdditionalUser @@ -543,7 +539,12 @@ class SafetyCenterMultiUsersTest { .build() ) ), - listOf(SafetyCenterStaticEntryGroup("OK", listOf(staticEntry, staticEntry))) + listOf( + SafetyCenterStaticEntryGroup( + "OK", + listOf(createStaticEntry(), createStaticEntry(explicit = false)) + ) + ) ) assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig) } @@ -589,7 +590,12 @@ class SafetyCenterMultiUsersTest { listOf( SafetyCenterStaticEntryGroup( "OK", - listOf(staticEntry, staticEntryForWork, staticEntry, staticEntryForWork) + listOf( + createStaticEntry(), + createStaticEntryForWork(), + createStaticEntry(explicit = false), + createStaticEntryForWork(explicit = false) + ) ) ) ) @@ -639,9 +645,9 @@ class SafetyCenterMultiUsersTest { "OK", listOf( staticEntryUpdated, - staticEntryForWork, - staticEntry, - staticEntryForWork + createStaticEntryForWork(), + createStaticEntry(explicit = false), + createStaticEntryForWork(explicit = false) ) ) ) @@ -749,8 +755,8 @@ class SafetyCenterMultiUsersTest { listOf( staticEntryUpdated, staticEntryForWorkUpdated, - staticEntry, - staticEntryForWork + createStaticEntry(explicit = false), + createStaticEntryForWork(explicit = false) ) ) ) @@ -809,8 +815,8 @@ class SafetyCenterMultiUsersTest { listOf( staticEntryUpdated, staticEntryForWorkPausedUpdated, - staticEntry, - staticEntryForWorkPaused + createStaticEntry(explicit = false), + createStaticEntryForWorkPaused() ) ) ) @@ -1228,13 +1234,12 @@ class SafetyCenterMultiUsersTest { private fun createTestActivityRedirectPendingIntentForUser( user: UserHandle, - inQuietMode: Boolean = false + explicit: Boolean = true ): PendingIntent { return callWithShellPermissionIdentity(INTERACT_ACROSS_USERS) { SafetySourceTestData.createRedirectPendingIntent( getContextForUser(user), - Intent(ACTION_TEST_ACTIVITY), - inQuietMode + SafetySourceTestData.createTestActivityIntent(getContextForUser(user), explicit) ) } } diff --git a/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt b/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt index 73c33f904..73f435615 100644 --- a/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt +++ b/tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt @@ -1452,7 +1452,7 @@ class SafetyCenterActivityTest { @Test fun startStaticEntryActivity_withConfigToBeSettingsActivity_trueExtraInBundle() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleStaticSettingsSource) + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleStaticSettingsSourceConfig) context.launchSafetyCenterActivity { waitDisplayed(By.text("OK")) { it.click() } diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt index e2a7802b3..19922e4f7 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt @@ -16,7 +16,6 @@ package android.safetycenter.functional -import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build.VERSION_CODES.TIRAMISU @@ -81,6 +80,7 @@ import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.ref import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.reportSafetySourceErrorWithPermission import com.android.safetycenter.testing.SafetyCenterFlags import com.android.safetycenter.testing.SafetyCenterTestConfigs +import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY_EXPORTED import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.DYNAMIC_ALL_OPTIONAL_ID @@ -332,7 +332,9 @@ class SafetyCenterManagerTest { .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) .setSummary("OK") .setPendingIntent( - safetySourceTestData.testActivityRedirectPendingIntent + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) ) .setSeverityUnspecifiedIconType( SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON @@ -352,11 +354,19 @@ class SafetyCenterManagerTest { "OK", listOf( SafetyCenterStaticEntry.Builder("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) .build(), SafetyCenterStaticEntry.Builder("OK") .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) .build() ) ) @@ -368,11 +378,17 @@ class SafetyCenterManagerTest { listOf( SafetyCenterStaticEntry.Builder("OK") .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent() + ) .build(), SafetyCenterStaticEntry.Builder("OK") .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) .build() ) ) @@ -385,11 +401,17 @@ class SafetyCenterManagerTest { listOf( SafetyCenterStaticEntry.Builder("Unspecified title") .setSummary("Unspecified summary") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent() + ) .build(), SafetyCenterStaticEntry.Builder("OK") .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) .build() ) ) @@ -457,7 +479,7 @@ class SafetyCenterManagerTest { .safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID) .setIconAction( ICON_ACTION_TYPE_INFO, - safetySourceTestData.testActivityRedirectPendingIntent + safetySourceTestData.createTestActivityRedirectPendingIntent() ) .build() ) @@ -974,28 +996,105 @@ class SafetyCenterManagerTest { } @Test + fun getSafetyCenterData_withoutDataExplicitIntentConfig_defaultEntryHasExplicitIntent() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + val expectedExplicitPendingIntent = + SafetySourceTestData.createRedirectPendingIntent( + context, + Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName) + ) + val defaultEntryPendingIntent = + apiSafetyCenterData.entriesOrGroups.firstOrNull()?.entry?.pendingIntent + val defaultEntryIntentFilterEqualsToExplicitIntent = + callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") { + expectedExplicitPendingIntent.intentFilterEquals(defaultEntryPendingIntent) + } + assertThat(defaultEntryIntentFilterEqualsToExplicitIntent).isTrue() + } + + @Test fun getSafetyCenterData_withoutDataImplicitIntentConfig_defaultEntryHasImplicitIntent() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.implicitIntentSingleSourceConfig) val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() - val implicitPendingIntentCreatedByCts = - PendingIntent.getActivity( + val expectedImplicitPendingIntent = + SafetySourceTestData.createRedirectPendingIntent( context, - 0 /* requestCode */, - Intent(ACTION_TEST_ACTIVITY_EXPORTED), - PendingIntent.FLAG_IMMUTABLE + Intent(ACTION_TEST_ACTIVITY_EXPORTED) ) val defaultEntryPendingIntent = apiSafetyCenterData.entriesOrGroups.firstOrNull()?.entry?.pendingIntent val defaultEntryIntentFilterEqualsToImplicitIntent = callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") { - implicitPendingIntentCreatedByCts.intentFilterEquals(defaultEntryPendingIntent) + expectedImplicitPendingIntent.intentFilterEquals(defaultEntryPendingIntent) } assertThat(defaultEntryIntentFilterEqualsToImplicitIntent).isTrue() } @Test + fun getSafetyCenterData_withStaticImplicitResolving_implicitStaticEntry() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.staticSourcesConfig) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + val expectedImplicitPendingIntent = + SafetySourceTestData.createRedirectPendingIntent( + context, + Intent(ACTION_TEST_ACTIVITY_EXPORTED) + ) + val staticEntryPendingIntent = + apiSafetyCenterData.staticEntryGroups + .firstOrNull() + ?.staticEntries + ?.firstOrNull() + ?.pendingIntent + val staticEntryIntentFilterEqualsToImplicitIntent = + callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") { + expectedImplicitPendingIntent.intentFilterEquals(staticEntryPendingIntent) + } + assertThat(staticEntryIntentFilterEqualsToImplicitIntent).isTrue() + } + + @Test + fun getSafetyCenterData_withStaticImplicitNotExported_explicitStaticEntryUsingCallerPackage() { + safetyCenterTestHelper.setConfig( + safetyCenterTestConfigs.singleStaticImplicitIntentNotExportedConfig + ) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + val expectedExplicitPendingIntent = + SafetySourceTestData.createRedirectPendingIntent( + context, + Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName) + ) + val staticEntryPendingIntent = + apiSafetyCenterData.staticEntryGroups + .firstOrNull() + ?.staticEntries + ?.firstOrNull() + ?.pendingIntent + val staticEntryIntentFilterEqualsToExplicitIntent = + callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") { + expectedExplicitPendingIntent.intentFilterEquals(staticEntryPendingIntent) + } + assertThat(staticEntryIntentFilterEqualsToExplicitIntent).isTrue() + } + + @Test + fun getSafetyCenterData_withStaticNotResolving_noStaticEntry() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleStaticInvalidIntentConfig) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + assertThat(apiSafetyCenterData.staticEntryGroups).isEmpty() + } + + @Test fun getSafetyCenterData_withComplexConfigWithoutDataProvided_returnsDataFromConfig() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexConfig) diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt index 20802dd2f..9c9e9b009 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt @@ -296,8 +296,8 @@ class SafetyCenterNotificationTest { @Test fun setSafetySourceData_issueWithTwoActions_notificationWithTwoActions() { - val intent1 = safetySourceTestData.testActivityRedirectPendingIntent(identifier = "1") - val intent2 = safetySourceTestData.testActivityRedirectPendingIntent(identifier = "2") + val intent1 = safetySourceTestData.createTestActivityRedirectPendingIntent(identifier = "1") + val intent2 = safetySourceTestData.createTestActivityRedirectPendingIntent(identifier = "2") val data = safetySourceTestData @@ -361,8 +361,8 @@ class SafetyCenterNotificationTest { @Test @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_withCustomNotification_usesCustomValues() { - val intent1 = safetySourceTestData.testActivityRedirectPendingIntent(identifier = "1") - val intent2 = safetySourceTestData.testActivityRedirectPendingIntent(identifier = "2") + val intent1 = safetySourceTestData.createTestActivityRedirectPendingIntent(identifier = "1") + val intent2 = safetySourceTestData.createTestActivityRedirectPendingIntent(identifier = "2") val notification = SafetySourceIssue.Notification.Builder("Custom title", "Custom text") @@ -384,7 +384,7 @@ class SafetyCenterNotificationTest { SafetySourceIssue.Action.Builder( "default_action", "Default action", - safetySourceTestData.testActivityRedirectPendingIntent + safetySourceTestData.createTestActivityRedirectPendingIntent() ) .build() ) @@ -422,7 +422,7 @@ class SafetyCenterNotificationTest { SafetySourceIssue.Action.Builder( "default_action", "Default action", - safetySourceTestData.testActivityRedirectPendingIntent + safetySourceTestData.createTestActivityRedirectPendingIntent() ) .build() ) @@ -475,7 +475,7 @@ class SafetyCenterNotificationTest { SafetySourceIssue.Action.Builder( "new_action", "New action", - safetySourceTestData.testActivityRedirectPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( identifier = "new_action" ) ) diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt index de4cf7094..fea9b45cc 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt @@ -64,7 +64,9 @@ class SafetyCenterTestConfigs(private val context: Context) { */ val singleSourceInvalidIntentConfig = singleSourceConfig( - dynamicSafetySourceBuilder(SINGLE_SOURCE_ID).setIntentAction("stub").build() + dynamicSafetySourceBuilder(SINGLE_SOURCE_ID) + .setIntentAction(INTENT_ACTION_NOT_RESOLVING) + .build() ) /** @@ -348,7 +350,9 @@ class SafetyCenterTestConfigs(private val context: Context) { .addSafetySourcesGroup( safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1) .addSafetySource( - dynamicSafetySourceBuilder(SOURCE_ID_1).setIntentAction("stub").build() + dynamicSafetySourceBuilder(SOURCE_ID_1) + .setIntentAction(INTENT_ACTION_NOT_RESOLVING) + .build() ) .addSafetySource(dynamicSafetySource(SOURCE_ID_2)) .build() @@ -373,9 +377,7 @@ class SafetyCenterTestConfigs(private val context: Context) { * Source group provided by [staticSourcesConfig] containing a single source [staticSource1]. */ val staticSourceGroup1 = - SafetySourcesGroup.Builder() - .setId("test_static_sources_group_id_1") - .setTitleResId(android.R.string.paste) + staticSafetySourcesGroupBuilder("test_static_sources_group_id_1") .addSafetySource(staticSource1) .build() @@ -383,8 +385,7 @@ class SafetyCenterTestConfigs(private val context: Context) { * Source group provided by [staticSourcesConfig] containing a single source [staticSource2]. */ val staticSourceGroup2 = - SafetySourcesGroup.Builder() - .setId("test_static_sources_group_id_2") + staticSafetySourcesGroupBuilder("test_static_sources_group_id_2") .setTitleResId(android.R.string.copy) .addSafetySource(staticSource2) .build() @@ -402,7 +403,44 @@ class SafetyCenterTestConfigs(private val context: Context) { * The particular source ID is configured in the same way as sources hosted by the Settings app, * to launch as if it is part of the Settings app UI. */ - val singleStaticSettingsSource = singleSourceConfig(staticSafetySource("TestSource")) + val singleStaticSettingsSourceConfig = + SafetyCenterConfig.Builder() + .addSafetySourcesGroup( + staticSafetySourcesGroupBuilder("single_static_source_group") + .addSafetySource(staticSafetySource("TestSource")) + .build() + ) + .build() + + /** A [SafetyCenterConfig] with a single static source and an intent that doesn't resolve */ + val singleStaticInvalidIntentConfig = + SafetyCenterConfig.Builder() + .addSafetySourcesGroup( + staticSafetySourcesGroupBuilder("single_static_source_group") + .addSafetySource( + staticSafetySourceBuilder(SINGLE_SOURCE_ID) + .setIntentAction(INTENT_ACTION_NOT_RESOLVING) + .build() + ) + .build() + ) + .build() + + /** + * A [SafetyCenterConfig] with a single static source and an implicit intent that isn't exported + */ + val singleStaticImplicitIntentNotExportedConfig = + SafetyCenterConfig.Builder() + .addSafetySourcesGroup( + staticSafetySourcesGroupBuilder("single_static_source_group") + .addSafetySource( + staticSafetySourceBuilder(SINGLE_SOURCE_ID) + .setIntentAction(ACTION_TEST_ACTIVITY) + .build() + ) + .build() + ) + .build() /** [SafetyCenterConfig] used in tests for Your Work Policy Info source. */ val workPolicyInfoConfig = @@ -750,7 +788,7 @@ class SafetyCenterTestConfigs(private val context: Context) { .setId(id) .setTitleResId(android.R.string.ok) .setSummaryResId(android.R.string.ok) - .setIntentAction(ACTION_TEST_ACTIVITY) + .setIntentAction(ACTION_TEST_ACTIVITY_EXPORTED) .setProfile(SafetySource.PROFILE_PRIMARY) private fun staticAllProfileSafetySourceBuilder(id: String) = @@ -780,6 +818,9 @@ class SafetyCenterTestConfigs(private val context: Context) { .setTitleResId(android.R.string.ok) .setSummaryResId(android.R.string.ok) + private fun staticSafetySourcesGroupBuilder(id: String) = + SafetySourcesGroup.Builder().setId(id).setTitleResId(android.R.string.paste) + fun singleSourceConfig(safetySource: SafetySource) = SafetyCenterConfig.Builder() .addSafetySourcesGroup( @@ -1033,5 +1074,7 @@ class SafetyCenterTestConfigs(private val context: Context) { * [privacySubpageWithoutDataSourcesConfig], to replicate the privacy sources group. */ const val ANDROID_PRIVACY_SOURCES_GROUP_ID = "AndroidPrivacySources" + + private const val INTENT_ACTION_NOT_RESOLVING = "there.is.no.way.this.resolves" } } diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt index 31e9855ae..289bc32a8 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt @@ -164,7 +164,8 @@ class SafetyCenterTestData(context: Context) { sourceId: String, userId: Int = UserHandle.myUserId(), title: CharSequence = "OK", - pendingIntent: PendingIntent? = safetySourceTestData.testActivityRedirectPendingIntent + pendingIntent: PendingIntent? = + safetySourceTestData.createTestActivityRedirectPendingIntent() ) = SafetyCenterEntry.Builder(entryId(sourceId, userId), title) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN) @@ -181,7 +182,8 @@ class SafetyCenterTestData(context: Context) { sourceId: String, userId: Int = UserHandle.myUserId(), title: CharSequence = "OK", - pendingIntent: PendingIntent? = safetySourceTestData.testActivityRedirectPendingIntent + pendingIntent: PendingIntent? = + safetySourceTestData.createTestActivityRedirectPendingIntent() ) = safetyCenterEntryDefaultBuilder(sourceId, userId, title, pendingIntent).build() /** @@ -197,7 +199,9 @@ class SafetyCenterTestData(context: Context) { SafetyCenterEntry.Builder(entryId(sourceId, userId), title) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent(explicit = false) + ) .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON) /** @@ -214,7 +218,8 @@ class SafetyCenterTestData(context: Context) { */ fun safetyCenterEntryUnspecified( sourceId: String, - pendingIntent: PendingIntent? = safetySourceTestData.testActivityRedirectPendingIntent + pendingIntent: PendingIntent? = + safetySourceTestData.createTestActivityRedirectPendingIntent() ) = SafetyCenterEntry.Builder(entryId(sourceId), "Unspecified title") .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) @@ -237,7 +242,7 @@ class SafetyCenterTestData(context: Context) { SafetyCenterEntry.Builder(entryId(sourceId, userId), title) .setSeverityLevel(ENTRY_SEVERITY_LEVEL_OK) .setSummary("Ok summary") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent()) .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION) /** @@ -262,7 +267,7 @@ class SafetyCenterTestData(context: Context) { SafetyCenterEntry.Builder(entryId(sourceId), "Recommendation title") .setSeverityLevel(ENTRY_SEVERITY_LEVEL_RECOMMENDATION) .setSummary(summary) - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent()) .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION) .build() @@ -274,7 +279,7 @@ class SafetyCenterTestData(context: Context) { SafetyCenterEntry.Builder(entryId(sourceId), "Critical title") .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING) .setSummary("Critical summary") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) + .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent()) .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION) .build() @@ -305,7 +310,7 @@ class SafetyCenterTestData(context: Context) { userId ), "Review", - safetySourceTestData.testActivityRedirectPendingIntent + safetySourceTestData.createTestActivityRedirectPendingIntent() ) .build() ) @@ -345,7 +350,7 @@ class SafetyCenterTestData(context: Context) { userId ), "See issue", - safetySourceTestData.testActivityRedirectPendingIntent + safetySourceTestData.createTestActivityRedirectPendingIntent() ) .apply { if (confirmationDialog && SdkLevel.isAtLeastU()) { diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt index 0dda827ee..0b2a6c840 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt @@ -20,7 +20,6 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.Intent.FLAG_RECEIVER_FOREGROUND -import android.content.pm.PackageManager.ResolveInfoFlags import android.os.Build.VERSION_CODES.TIRAMISU import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.SafetyEvent @@ -39,13 +38,13 @@ import android.safetycenter.SafetySourceStatus.IconAction.ICON_TYPE_INFO import androidx.annotation.RequiresApi import com.android.modules.utils.build.SdkLevel import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY +import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.ACTION_TEST_ACTIVITY_EXPORTED import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.ACTION_DISMISS_ISSUE import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.ACTION_RESOLVE_ACTION import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ID import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ISSUE_ACTION_ID import com.android.safetycenter.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ISSUE_ID -import java.lang.IllegalStateException import kotlin.math.max /** @@ -55,16 +54,21 @@ import kotlin.math.max @RequiresApi(TIRAMISU) class SafetySourceTestData(private val context: Context) { - /** A [PendingIntent] that redirects to the [TestActivity] page. */ - val testActivityRedirectPendingIntent = - createRedirectPendingIntent(context, Intent(ACTION_TEST_ACTIVITY)) - /** - * A [PendingIntent] that redirects to the [TestActivity] page, the [Intent] is constructed with - * the given [identifier]. + * A [PendingIntent] that redirects to the [TestActivity] page. + * + * @param explicit whether the returned [PendingIntent] should use an explicit [Intent] (default + * [true]) + * @param identifier the [Intent] identifier (default [null]) */ - fun testActivityRedirectPendingIntent(identifier: String? = null) = - createRedirectPendingIntent(context, Intent(ACTION_TEST_ACTIVITY).setIdentifier(identifier)) + fun createTestActivityRedirectPendingIntent( + explicit: Boolean = true, + identifier: String? = null + ) = + createRedirectPendingIntent( + context, + createTestActivityIntent(context, explicit).setIdentifier(identifier) + ) /** A [SafetySourceData] with a [SEVERITY_LEVEL_UNSPECIFIED] [SafetySourceStatus]. */ val unspecified = @@ -93,7 +97,7 @@ class SafetySourceTestData(private val context: Context) { SEVERITY_LEVEL_UNSPECIFIED ) .setEnabled(false) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .build() @@ -114,7 +118,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( INFORMATION_ISSUE_ACTION_ID, "Review", - testActivityRedirectPendingIntent + createTestActivityRedirectPendingIntent() ) .build() ) @@ -136,7 +140,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( INFORMATION_ISSUE_ACTION_ID, "Review", - testActivityRedirectPendingIntent + createTestActivityRedirectPendingIntent() ) .build() ) @@ -154,7 +158,7 @@ class SafetySourceTestData(private val context: Context) { "Unspecified summary", SEVERITY_LEVEL_UNSPECIFIED ) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .addIssue(informationIssue) @@ -172,7 +176,7 @@ class SafetySourceTestData(private val context: Context) { "Unspecified summary", SEVERITY_LEVEL_UNSPECIFIED ) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .addIssue(informationIssue) @@ -183,7 +187,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceData.Builder() .setStatus( SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .build() @@ -209,8 +213,10 @@ class SafetySourceTestData(private val context: Context) { SafetySourceData.Builder() .setStatus( SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION) - .setPendingIntent(testActivityRedirectPendingIntent) - .setIconAction(IconAction(ICON_TYPE_INFO, testActivityRedirectPendingIntent)) + .setPendingIntent(createTestActivityRedirectPendingIntent()) + .setIconAction( + IconAction(ICON_TYPE_INFO, createTestActivityRedirectPendingIntent()) + ) .build() ) .build() @@ -223,8 +229,10 @@ class SafetySourceTestData(private val context: Context) { SafetySourceData.Builder() .setStatus( SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION) - .setPendingIntent(testActivityRedirectPendingIntent) - .setIconAction(IconAction(ICON_TYPE_GEAR, testActivityRedirectPendingIntent)) + .setPendingIntent(createTestActivityRedirectPendingIntent()) + .setIconAction( + IconAction(ICON_TYPE_GEAR, createTestActivityRedirectPendingIntent()) + ) .build() ) .build() @@ -237,7 +245,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceData.Builder() .setStatus( SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .addIssue(informationIssue) @@ -253,7 +261,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceData.Builder() .setStatus( SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .addIssue( @@ -275,7 +283,7 @@ class SafetySourceTestData(private val context: Context) { "Ok summary", SEVERITY_LEVEL_INFORMATION ) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .addIssue(informationIssue) @@ -289,7 +297,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceData.Builder() .setStatus( SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .addIssue(informationIssueWithSubtitle) @@ -315,7 +323,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( RECOMMENDATION_ISSUE_ACTION_ID, "See issue", - testActivityRedirectPendingIntent + createTestActivityRedirectPendingIntent() ) .apply { if (confirmationDialog && SdkLevel.isAtLeastU()) { @@ -383,7 +391,7 @@ class SafetySourceTestData(private val context: Context) { "Recommendation summary", SEVERITY_LEVEL_RECOMMENDATION ) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) @@ -467,7 +475,11 @@ class SafetySourceTestData(private val context: Context) { /** An action that redirects to [TestActivity] */ val testActivityRedirectAction = - Action.Builder(CRITICAL_ISSUE_ACTION_ID, "Redirect", testActivityRedirectPendingIntent) + Action.Builder( + CRITICAL_ISSUE_ACTION_ID, + "Redirect", + createTestActivityRedirectPendingIntent() + ) .build() /** A resolving Critical [Action] that declares a success message */ @@ -505,7 +517,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( CRITICAL_ISSUE_ACTION_ID, "Go solve issue", - testActivityRedirectPendingIntent + createTestActivityRedirectPendingIntent() ) .build() ) @@ -584,7 +596,7 @@ class SafetySourceTestData(private val context: Context) { "Critical summary", SEVERITY_LEVEL_CRITICAL_WARNING ) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) @@ -698,7 +710,7 @@ class SafetySourceTestData(private val context: Context) { "Critical summary", SEVERITY_LEVEL_CRITICAL_WARNING ) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .addIssue(criticalResolvingIssueWithSuccessMessage) @@ -723,7 +735,7 @@ class SafetySourceTestData(private val context: Context) { "Critical summary 2", SEVERITY_LEVEL_CRITICAL_WARNING ) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .addIssue(criticalRedirectingIssue) @@ -742,7 +754,7 @@ class SafetySourceTestData(private val context: Context) { SafetySourceData.Builder() .setStatus( SafetySourceStatus.Builder(entryTitle, entrySummary, severityLevel) - .setPendingIntent(testActivityRedirectPendingIntent) + .setPendingIntent(createTestActivityRedirectPendingIntent()) .build() ) .apply { @@ -759,7 +771,7 @@ class SafetySourceTestData(private val context: Context) { Action.Builder( "action_id", "Action", - testActivityRedirectPendingIntent + createTestActivityRedirectPendingIntent() ) .build() ) @@ -824,41 +836,28 @@ class SafetySourceTestData(private val context: Context) { return builder.build() } - /** Returns a [PendingIntent] that redirects to [intent]. */ - fun createRedirectPendingIntent( - context: Context, - intent: Intent, - inQuietMode: Boolean = false - ): PendingIntent { - val explicitIntent = Intent(intent).setPackage(context.packageName) - val redirectIntent = - if (intentResolves(context, explicitIntent)) { - explicitIntent - } else if (intentResolves(context, intent)) { - // We have seen some flakiness where implicit intents find multiple receivers - // and the ResolveActivity pops up. A test cannot handle this, so crash. Most - // likely the cause is other test's APKs being left hanging around by flaky - // test infrastructure. - val intentWithFlag = Intent(intent) - intentWithFlag.flags = - intentWithFlag.flags or Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT - intentWithFlag - } else if (inQuietMode) { - explicitIntent - } else { - throw IllegalStateException("Intent doesn't resolve") - } + /** Returns an [Intent] that redirects to the [TestActivity] page. */ + fun createTestActivityIntent(context: Context, explicit: Boolean = true): Intent = + if (explicit) { + Intent(ACTION_TEST_ACTIVITY).setPackage(context.packageName) + } else { + val intent = Intent(ACTION_TEST_ACTIVITY_EXPORTED) + // We have seen some flakiness where implicit intents find multiple receivers + // and the ResolveActivity pops up. A test cannot handle this, so crash. Most + // likely the cause is other test's APKs being left hanging around by flaky + // test infrastructure. + intent.flags = intent.flags or Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT + intent + } + + /** Returns a [PendingIntent] that redirects to the given [Intent]. */ + fun createRedirectPendingIntent(context: Context, intent: Intent): PendingIntent { return PendingIntent.getActivity( context, 0 /* requestCode */, - redirectIntent, + intent, PendingIntent.FLAG_IMMUTABLE ) } - - private fun intentResolves(context: Context, intent: Intent): Boolean = - context.packageManager - .queryIntentActivities(intent, ResolveInfoFlags.of(0)) - .isNotEmpty() } } |