diff options
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() } } |