diff options
Diffstat (limited to 'tests/functional/safetycenter/singleuser/src/android/safetycenter')
11 files changed, 1456 insertions, 2508 deletions
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 b8fd17b37..963c593cd 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt @@ -16,10 +16,9 @@ package android.safetycenter.functional -import android.app.PendingIntent +import android.Manifest.permission.MANAGE_SAFETY_CENTER import android.content.Context import android.content.Intent -import android.os.Build import android.os.Build.VERSION_CODES.TIRAMISU import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.os.UserHandle @@ -63,25 +62,25 @@ import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress -import com.android.compatibility.common.preconditions.ScreenLockHelper import com.android.compatibility.common.util.SystemUtil import com.android.modules.utils.build.SdkLevel import com.android.safetycenter.internaldata.SafetyCenterBundles import com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY import com.android.safetycenter.internaldata.SafetyCenterEntryId import com.android.safetycenter.internaldata.SafetyCenterIds -import com.android.safetycenter.resources.SafetyCenterResourcesContext +import com.android.safetycenter.resources.SafetyCenterResourcesApk import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT import com.android.safetycenter.testing.Coroutines.waitForWithTimeout import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.dismissSafetyCenterIssueWithPermission import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetyCenterConfigWithPermission import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetyCenterDataWithPermission +import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetySourceDataWithPermission import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.refreshSafetySourcesWithPermission import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.reportSafetySourceErrorWithPermission import com.android.safetycenter.testing.SafetyCenterFlags -import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter 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 @@ -116,6 +115,7 @@ import com.android.safetycenter.testing.SafetyCenterTestData.Companion.withAttri import com.android.safetycenter.testing.SafetyCenterTestData.Companion.withDismissedIssuesIfAtLeastU import com.android.safetycenter.testing.SafetyCenterTestData.Companion.withoutExtras import com.android.safetycenter.testing.SafetyCenterTestHelper +import com.android.safetycenter.testing.SafetyCenterTestRule import com.android.safetycenter.testing.SafetySourceIntentHandler.Request import com.android.safetycenter.testing.SafetySourceIntentHandler.Response import com.android.safetycenter.testing.SafetySourceReceiver @@ -130,15 +130,14 @@ import com.android.safetycenter.testing.SafetySourceTestData.Companion.INFORMATI import com.android.safetycenter.testing.SafetySourceTestData.Companion.RECOMMENDATION_ISSUE_ID import com.android.safetycenter.testing.SettingsPackage.getSettingsPackageName import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity +import com.android.safetycenter.testing.SupportsSafetyCenterRule import com.google.common.base.Preconditions.checkState import com.google.common.truth.Truth.assertThat import java.time.Duration import kotlin.test.assertFailsWith import kotlinx.coroutines.TimeoutCancellationException -import org.junit.After import org.junit.Assume.assumeFalse -import org.junit.Assume.assumeTrue -import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -146,588 +145,657 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SafetyCenterManagerTest { private val context: Context = getApplicationContext() - private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context) + private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context) private val safetyCenterTestHelper = SafetyCenterTestHelper(context) private val safetySourceTestData = SafetySourceTestData(context) private val safetyCenterTestData = SafetyCenterTestData(context) private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!! - private val safetyCenterStatusOk = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_summary") - ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) - .build() + private val safetyCenterStatusOk: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary") + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) + .build() + + private val safetyCenterStatusUnknownScanning: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName("scanning_title"), + safetyCenterResourcesApk.getStringByName("loading_summary") + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN) + .setRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS) + .build() + + private val safetyCenterStatusOkOneAlert: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), + safetyCenterTestData.getAlertString(1) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) + .build() + + private val safetyCenterStatusOkReviewOneAlert: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_ok_review_title" + ), + safetyCenterTestData.getAlertString(1) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) + .build() + + private val safetyCenterStatusOkReview: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_ok_review_title" + ), + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_ok_review_summary" + ) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) + .build() + + private val safetyCenterStatusGeneralRecommendationOneAlert: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_safety_recommendation_title" + ), + safetyCenterTestData.getAlertString(1) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) + .build() + + private val safetyCenterStatusAccountRecommendationOneAlert: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_account_recommendation_title" + ), + safetyCenterTestData.getAlertString(1) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) + .build() + + private val safetyCenterStatusDeviceRecommendationOneAlert: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_device_recommendation_title" + ), + safetyCenterTestData.getAlertString(1) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) + .build() - private val safetyCenterStatusUnknownScanning = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName("scanning_title"), - safetyCenterResourcesContext.getStringByName("loading_summary") + private val safetyCenterStatusGeneralCriticalOneAlert: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_critical_safety_warning_title" + ), + safetyCenterTestData.getAlertString(1) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) + .build() + + private val safetyCenterStatusGeneralCriticalTwoAlerts: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_critical_safety_warning_title" + ), + safetyCenterTestData.getAlertString(2) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) + .build() + + private val safetyCenterStatusAccountCriticalOneAlert: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_critical_account_warning_title" + ), + safetyCenterTestData.getAlertString(1) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) + .build() + + private val safetyCenterStatusAccountCriticalTwoAlerts: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_critical_account_warning_title" + ), + safetyCenterTestData.getAlertString(2) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) + .build() + + private val safetyCenterStatusDeviceCriticalOneAlert: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_critical_device_warning_title" + ), + safetyCenterTestData.getAlertString(1) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) + .build() + + private val safetyCenterStatusDeviceCriticalTwoAlerts: SafetyCenterStatus + get() = + SafetyCenterStatus.Builder( + safetyCenterResourcesApk.getStringByName( + "overall_severity_level_critical_device_warning_title" + ), + safetyCenterTestData.getAlertString(2) + ) + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) + .build() + + private val safetyCenterEntryOrGroupRecommendation: SafetyCenterEntryOrGroup + get() = + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) + ) + + private val safetyCenterEntryOrGroupCritical: SafetyCenterEntryOrGroup + get() = + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryCritical(SINGLE_SOURCE_ID) + ) + + private val safetyCenterEntryGroupMixedFromComplexConfig: SafetyCenterEntryOrGroup + get() = + SafetyCenterEntryOrGroup( + SafetyCenterEntryGroup.Builder(MIXED_STATEFUL_GROUP_ID, "OK") + .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN) + .setSummary(safetyCenterResourcesApk.getStringByName("group_unknown_summary")) + .setEntries( + listOf( + safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_IN_STATEFUL_ID), + SafetyCenterEntry.Builder( + SafetyCenterTestData.entryId(STATIC_IN_STATEFUL_ID), + "OK" + ) + .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) + .setSummary("OK") + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) + .setSeverityUnspecifiedIconType( + SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON + ) + .build() + ) + ) + .setSeverityUnspecifiedIconType( + SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION + ) + .build() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN) - .setRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS) - .build() - private val safetyCenterStatusOkOneAlert = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"), - safetyCenterTestData.getAlertString(1) + private val safetyCenterStaticEntryGroupFromComplexConfig: SafetyCenterStaticEntryGroup + get() = + SafetyCenterStaticEntryGroup( + "OK", + listOf( + SafetyCenterStaticEntry.Builder("OK") + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) + .build(), + SafetyCenterStaticEntry.Builder("OK") + .setSummary("OK") + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) + .build() + ) ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) - .build() - private val safetyCenterStatusOkReviewOneAlert = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_ok_review_title" - ), - safetyCenterTestData.getAlertString(1) + private val safetyCenterStaticEntryGroupMixedFromComplexConfig: SafetyCenterStaticEntryGroup + get() = + SafetyCenterStaticEntryGroup( + "OK", + listOf( + SafetyCenterStaticEntry.Builder("OK") + .setSummary("OK") + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent() + ) + .build(), + SafetyCenterStaticEntry.Builder("OK") + .setSummary("OK") + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) + .build() + ) ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) - .build() - private val safetyCenterStatusOkReview = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_ok_review_title" - ), - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_ok_review_summary" + private val safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig: + SafetyCenterStaticEntryGroup + get() = + SafetyCenterStaticEntryGroup( + "OK", + listOf( + SafetyCenterStaticEntry.Builder("Unspecified title") + .setSummary("Unspecified summary") + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent() + ) + .build(), + SafetyCenterStaticEntry.Builder("OK") + .setSummary("OK") + .setPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( + explicit = false + ) + ) + .build() ) ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK) - .build() - private val safetyCenterStatusGeneralRecommendationOneAlert = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_safety_recommendation_title" + private val safetyCenterDataFromConfigScanning: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusUnknownScanning, + emptyList(), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID) + ) ), - safetyCenterTestData.getAlertString(1) + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) - .build() - private val safetyCenterStatusAccountRecommendationOneAlert = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_account_recommendation_title" + private val safetyCenterDataFromConfig: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterTestData.safetyCenterStatusUnknown, + emptyList(), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID) + ) ), - safetyCenterTestData.getAlertString(1) + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) - .build() - private val safetyCenterStatusDeviceRecommendationOneAlert = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_device_recommendation_title" + private val safetyCenterDataUnspecified: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusOk, + emptyList(), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryUnspecified(SINGLE_SOURCE_ID) + ) ), - safetyCenterTestData.getAlertString(1) + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION) - .build() - private val safetyCenterStatusGeneralCriticalOneAlert = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_critical_safety_warning_title" + private val safetyCenterDataOk: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusOk, + emptyList(), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID) + ) ), - safetyCenterTestData.getAlertString(1) + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) - .build() - private val safetyCenterStatusGeneralCriticalTwoAlerts = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_critical_safety_warning_title" + private val safetyCenterDataOkWithIconAction: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusOk, + emptyList(), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData + .safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID) + .setIconAction( + ICON_ACTION_TYPE_INFO, + safetySourceTestData.createTestActivityRedirectPendingIntent() + ) + .build() + ) ), - safetyCenterTestData.getAlertString(2) + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) - .build() - private val safetyCenterStatusAccountCriticalOneAlert = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_critical_account_warning_title" + private val safetyCenterDataUnknownScanningWithError: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusUnknownScanning, + emptyList(), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID) + ) ), - safetyCenterTestData.getAlertString(1) + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) - .build() - private val safetyCenterStatusAccountCriticalTwoAlerts = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_critical_account_warning_title" + private val safetyCenterDataUnknownReviewError: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterTestData.safetyCenterStatusUnknown, + emptyList(), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID) + ) ), - safetyCenterTestData.getAlertString(2) + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) - .build() - private val safetyCenterStatusDeviceCriticalOneAlert = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_critical_device_warning_title" + private val safetyCenterDataOkOneAlert: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusOkOneAlert, + listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID) + ) ), - safetyCenterTestData.getAlertString(1) + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) - .build() - private val safetyCenterStatusDeviceCriticalTwoAlerts = - SafetyCenterStatus.Builder( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_critical_device_warning_title" - ), - safetyCenterTestData.getAlertString(2) + private val safetyCenterDataOkReviewCriticalEntry: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusOkReview, + emptyList(), + listOf(safetyCenterEntryOrGroupCritical), + emptyList() ) - .setSeverityLevel(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING) - .build() - private val safetyCenterEntryOrGroupRecommendation = - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) - ) + private val safetyCenterDataOkReviewRecommendationEntry: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusOkReview, + emptyList(), + listOf(safetyCenterEntryOrGroupRecommendation), + emptyList() + ) - private val safetyCenterEntryOrGroupCritical = - SafetyCenterEntryOrGroup(safetyCenterTestData.safetyCenterEntryCritical(SINGLE_SOURCE_ID)) + private val safetyCenterDataOkReviewOneAlert: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusOkReviewOneAlert, + listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)), + listOf(safetyCenterEntryOrGroupCritical), + emptyList() + ) - private val safetyCenterEntryGroupMixedFromComplexConfig = - SafetyCenterEntryOrGroup( - SafetyCenterEntryGroup.Builder(MIXED_STATEFUL_GROUP_ID, "OK") - .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN) - .setSummary(safetyCenterResourcesContext.getStringByName("group_unknown_summary")) - .setEntries( - listOf( - safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_IN_STATEFUL_ID), - SafetyCenterEntry.Builder( - SafetyCenterTestData.entryId(STATIC_IN_STATEFUL_ID), - "OK" - ) - .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED) - .setSummary("OK") - .setPendingIntent( - safetySourceTestData.testActivityRedirectPendingIntent - ) - .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON) - .build() + private val safetyCenterDataGeneralRecommendationOneAlert: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusGeneralRecommendationOneAlert, + listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) ) - ) - .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION) - .build() - ) - - private val safetyCenterStaticEntryGroupFromComplexConfig = - SafetyCenterStaticEntryGroup( - "OK", - listOf( - SafetyCenterStaticEntry.Builder("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) - .build(), - SafetyCenterStaticEntry.Builder("OK") - .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) - .build() + ), + emptyList() ) - ) - private val safetyCenterStaticEntryGroupMixedFromComplexConfig = - SafetyCenterStaticEntryGroup( - "OK", - listOf( - SafetyCenterStaticEntry.Builder("OK") - .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) - .build(), - SafetyCenterStaticEntry.Builder("OK") - .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) - .build() + private val safetyCenterDataGeneralRecommendationAlertWithConfirmation: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusGeneralRecommendationOneAlert, + listOf( + safetyCenterTestData.safetyCenterIssueRecommendation( + SINGLE_SOURCE_ID, + confirmationDialog = true + ) + ), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) + ) + ), + emptyList() ) - ) - private val safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig = - SafetyCenterStaticEntryGroup( - "OK", - listOf( - SafetyCenterStaticEntry.Builder("Unspecified title") - .setSummary("Unspecified summary") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) - .build(), - SafetyCenterStaticEntry.Builder("OK") - .setSummary("OK") - .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent) - .build() + private val safetyCenterDataAccountRecommendationOneAlert: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusAccountRecommendationOneAlert, + listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) + ) + ), + emptyList() ) - ) - - private val safetyCenterDataFromConfigScanning = - SafetyCenterData( - safetyCenterStatusUnknownScanning, - emptyList(), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - - private val safetyCenterDataFromConfig = - SafetyCenterData( - safetyCenterTestData.safetyCenterStatusUnknown, - emptyList(), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - - private val safetyCenterDataUnspecified = - SafetyCenterData( - safetyCenterStatusOk, - emptyList(), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryUnspecified(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - - private val safetyCenterDataOk = - SafetyCenterData( - safetyCenterStatusOk, - emptyList(), - listOf( - SafetyCenterEntryOrGroup(safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID)) - ), - emptyList() - ) - - private val safetyCenterDataOkWithIconAction = - SafetyCenterData( - safetyCenterStatusOk, - emptyList(), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData - .safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID) - .setIconAction( - ICON_ACTION_TYPE_INFO, - safetySourceTestData.testActivityRedirectPendingIntent - ) - .build() - ) - ), - emptyList() - ) - - private val safetyCenterDataUnknownScanningWithError = - SafetyCenterData( - safetyCenterStatusUnknownScanning, - emptyList(), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - private val safetyCenterDataUnknownReviewError = - SafetyCenterData( - safetyCenterTestData.safetyCenterStatusUnknown, - emptyList(), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryError(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - - private val safetyCenterDataOkOneAlert = - SafetyCenterData( - safetyCenterStatusOkOneAlert, - listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)), - listOf( - SafetyCenterEntryOrGroup(safetyCenterTestData.safetyCenterEntryOk(SINGLE_SOURCE_ID)) - ), - emptyList() - ) - - private val safetyCenterDataOkReviewCriticalEntry = - SafetyCenterData( - safetyCenterStatusOkReview, - emptyList(), - listOf(safetyCenterEntryOrGroupCritical), - emptyList() - ) - - private val safetyCenterDataOkReviewRecommendationEntry = - SafetyCenterData( - safetyCenterStatusOkReview, - emptyList(), - listOf(safetyCenterEntryOrGroupRecommendation), - emptyList() - ) - - private val safetyCenterDataOkReviewOneAlert = - SafetyCenterData( - safetyCenterStatusOkReviewOneAlert, - listOf(safetyCenterTestData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)), - listOf(safetyCenterEntryOrGroupCritical), - emptyList() - ) - - private val safetyCenterDataGeneralRecommendationOneAlert = - SafetyCenterData( - safetyCenterStatusGeneralRecommendationOneAlert, - listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - - private val safetyCenterDataGeneralRecommendationAlertWithConfirmation = - SafetyCenterData( - safetyCenterStatusGeneralRecommendationOneAlert, - listOf( - safetyCenterTestData.safetyCenterIssueRecommendation( - SINGLE_SOURCE_ID, - confirmationDialog = true - ) - ), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - - private val safetyCenterDataAccountRecommendationOneAlert = - SafetyCenterData( - safetyCenterStatusAccountRecommendationOneAlert, - listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - - private val safetyCenterDataDeviceRecommendationOneAlert = - SafetyCenterData( - safetyCenterStatusDeviceRecommendationOneAlert, - listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)), - listOf( - SafetyCenterEntryOrGroup( - safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) - ) - ), - emptyList() - ) - - private val safetyCenterDataGeneralCriticalOneAlert = - SafetyCenterData( - safetyCenterStatusGeneralCriticalOneAlert, - listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), - listOf(safetyCenterEntryOrGroupCritical), - emptyList() - ) - - private val safetyCenterDataAccountCriticalOneAlert = - SafetyCenterData( - safetyCenterStatusAccountCriticalOneAlert, - listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), - listOf(safetyCenterEntryOrGroupCritical), - emptyList() - ) + private val safetyCenterDataDeviceRecommendationOneAlert: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusDeviceRecommendationOneAlert, + listOf(safetyCenterTestData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)), + listOf( + SafetyCenterEntryOrGroup( + safetyCenterTestData.safetyCenterEntryRecommendation(SINGLE_SOURCE_ID) + ) + ), + emptyList() + ) - private val safetyCenterDataDeviceCriticalOneAlert = - SafetyCenterData( - safetyCenterStatusDeviceCriticalOneAlert, - listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), - listOf(safetyCenterEntryOrGroupCritical), - emptyList() - ) + private val safetyCenterDataGeneralCriticalOneAlert: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusGeneralCriticalOneAlert, + listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), + listOf(safetyCenterEntryOrGroupCritical), + emptyList() + ) - private val safetyCenterDataCriticalOneAlertInFlight = - SafetyCenterData( - safetyCenterStatusGeneralCriticalOneAlert, - listOf( - safetyCenterTestData.safetyCenterIssueCritical( - SINGLE_SOURCE_ID, - isActionInFlight = true - ) - ), - listOf(safetyCenterEntryOrGroupCritical), - emptyList() - ) + private val safetyCenterDataAccountCriticalOneAlert: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusAccountCriticalOneAlert, + listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), + listOf(safetyCenterEntryOrGroupCritical), + emptyList() + ) - private val safetyCenterDataOkReviewOneDismissedAlertInFlight = - SafetyCenterData( - safetyCenterStatusOkReview, - emptyList(), + private val safetyCenterDataDeviceCriticalOneAlert: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusDeviceCriticalOneAlert, + listOf(safetyCenterTestData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)), listOf(safetyCenterEntryOrGroupCritical), emptyList() ) - .withDismissedIssuesIfAtLeastU( + + private val safetyCenterDataCriticalOneAlertInFlight: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusGeneralCriticalOneAlert, listOf( safetyCenterTestData.safetyCenterIssueCritical( SINGLE_SOURCE_ID, isActionInFlight = true ) - ) + ), + listOf(safetyCenterEntryOrGroupCritical), + emptyList() ) - private val safetyCenterDataFromComplexConfig = - SafetyCenterData( - safetyCenterTestData.safetyCenterStatusUnknown, - emptyList(), - listOf( - SafetyCenterEntryOrGroup( - SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK") - .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN) - .setSummary( - safetyCenterResourcesContext.getStringByName("group_unknown_summary") + private val safetyCenterDataOkReviewOneDismissedAlertInFlight: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterStatusOkReview, + emptyList(), + listOf(safetyCenterEntryOrGroupCritical), + emptyList() + ) + .withDismissedIssuesIfAtLeastU( + listOf( + safetyCenterTestData.safetyCenterIssueCritical( + SINGLE_SOURCE_ID, + isActionInFlight = true ) - .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY) - .setEntries( - listOf( - safetyCenterTestData.safetyCenterEntryDefault(DYNAMIC_BAREBONE_ID), - safetyCenterTestData - .safetyCenterEntryDefaultBuilder(DYNAMIC_ALL_OPTIONAL_ID) - .setEnabled(false) - .build(), - safetyCenterTestData - .safetyCenterEntryDefaultBuilder(DYNAMIC_DISABLED_ID) - .setPendingIntent(null) - .setEnabled(false) - .build(), - safetyCenterTestData - .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID) - .setPendingIntent(null) - .setEnabled(false) - .build() + ) + ) + + private val safetyCenterDataFromComplexConfig: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterTestData.safetyCenterStatusUnknown, + emptyList(), + listOf( + SafetyCenterEntryOrGroup( + SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK") + .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN) + .setSummary( + safetyCenterResourcesApk.getStringByName("group_unknown_summary") ) - ) - .build() + .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY) + .setEntries( + listOf( + safetyCenterTestData.safetyCenterEntryDefault( + DYNAMIC_BAREBONE_ID + ), + safetyCenterTestData + .safetyCenterEntryDefaultBuilder(DYNAMIC_ALL_OPTIONAL_ID) + .setEnabled(false) + .build(), + safetyCenterTestData + .safetyCenterEntryDefaultBuilder(DYNAMIC_DISABLED_ID) + .setPendingIntent(null) + .setEnabled(false) + .build(), + safetyCenterTestData + .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID) + .setPendingIntent(null) + .setEnabled(false) + .build() + ) + ) + .build() + ), + safetyCenterEntryGroupMixedFromComplexConfig ), - safetyCenterEntryGroupMixedFromComplexConfig - ), - listOf( - safetyCenterStaticEntryGroupFromComplexConfig, - safetyCenterStaticEntryGroupMixedFromComplexConfig + listOf( + safetyCenterStaticEntryGroupFromComplexConfig, + safetyCenterStaticEntryGroupMixedFromComplexConfig + ) ) - ) - private val safetyCenterDataFromComplexConfigUpdated = - SafetyCenterData( - safetyCenterTestData.safetyCenterStatusCritical(6), - listOf( - safetyCenterTestData.safetyCenterIssueCritical( - DYNAMIC_BAREBONE_ID, - groupId = DYNAMIC_GROUP_ID - ), - safetyCenterTestData.safetyCenterIssueCritical( - ISSUE_ONLY_BAREBONE_ID, - groupId = ISSUE_ONLY_GROUP_ID - ), - safetyCenterTestData.safetyCenterIssueRecommendation( - DYNAMIC_DISABLED_ID, - groupId = DYNAMIC_GROUP_ID - ), - safetyCenterTestData.safetyCenterIssueRecommendation( - ISSUE_ONLY_ALL_OPTIONAL_ID, - groupId = ISSUE_ONLY_GROUP_ID - ), - safetyCenterTestData.safetyCenterIssueInformation( - DYNAMIC_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID + private val safetyCenterDataFromComplexConfigUpdated: SafetyCenterData + get() = + SafetyCenterData( + safetyCenterTestData.safetyCenterStatusCritical(6), + listOf( + safetyCenterTestData.safetyCenterIssueCritical( + DYNAMIC_BAREBONE_ID, + groupId = DYNAMIC_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueCritical( + ISSUE_ONLY_BAREBONE_ID, + groupId = ISSUE_ONLY_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueRecommendation( + DYNAMIC_DISABLED_ID, + groupId = DYNAMIC_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueRecommendation( + ISSUE_ONLY_ALL_OPTIONAL_ID, + groupId = ISSUE_ONLY_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueInformation( + DYNAMIC_IN_STATELESS_ID, + groupId = MIXED_STATELESS_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueInformation( + ISSUE_ONLY_IN_STATELESS_ID, + groupId = MIXED_STATELESS_GROUP_ID + ) ), - safetyCenterTestData.safetyCenterIssueInformation( - ISSUE_ONLY_IN_STATELESS_ID, - groupId = MIXED_STATELESS_GROUP_ID - ) - ), - listOf( - SafetyCenterEntryOrGroup( - SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK") - .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING) - .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY) - .setSummary("Critical summary") - .setEntries( - listOf( - safetyCenterTestData.safetyCenterEntryCritical(DYNAMIC_BAREBONE_ID), - safetyCenterTestData - .safetyCenterEntryDefaultBuilder(DYNAMIC_ALL_OPTIONAL_ID) - .setEnabled(false) - .build(), - safetyCenterTestData.safetyCenterEntryRecommendation( - DYNAMIC_DISABLED_ID - ), - safetyCenterTestData.safetyCenterEntryUnspecified( - DYNAMIC_HIDDEN_ID, - pendingIntent = null - ), - safetyCenterTestData.safetyCenterEntryOk( - DYNAMIC_HIDDEN_WITH_SEARCH_ID - ), - safetyCenterTestData - .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID) - .setPendingIntent(null) - .setEnabled(false) - .build() + listOf( + SafetyCenterEntryOrGroup( + SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK") + .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING) + .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY) + .setSummary("Critical summary") + .setEntries( + listOf( + safetyCenterTestData.safetyCenterEntryCritical( + DYNAMIC_BAREBONE_ID + ), + safetyCenterTestData + .safetyCenterEntryDefaultBuilder(DYNAMIC_ALL_OPTIONAL_ID) + .setEnabled(false) + .build(), + safetyCenterTestData.safetyCenterEntryRecommendation( + DYNAMIC_DISABLED_ID + ), + safetyCenterTestData.safetyCenterEntryUnspecified( + DYNAMIC_HIDDEN_ID, + pendingIntent = null + ), + safetyCenterTestData.safetyCenterEntryOk( + DYNAMIC_HIDDEN_WITH_SEARCH_ID + ), + safetyCenterTestData + .safetyCenterEntryDefaultBuilder(DYNAMIC_OTHER_PACKAGE_ID) + .setPendingIntent(null) + .setEnabled(false) + .build() + ) ) - ) - .build() + .build() + ), + safetyCenterEntryGroupMixedFromComplexConfig ), - safetyCenterEntryGroupMixedFromComplexConfig - ), - listOf( - safetyCenterStaticEntryGroupFromComplexConfig, - safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig + listOf( + safetyCenterStaticEntryGroupFromComplexConfig, + safetyCenterStaticEntryGroupMixedUpdatedFromComplexConfig + ) ) - ) - // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to - // manually skip the setup and teardown methods. - private val shouldRunTests = context.deviceSupportsSafetyCenter() + @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) + @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper) - @Before - fun assumeDeviceSupportsSafetyCenterToRunTests() { - assumeTrue(shouldRunTests) - } + @Test + fun getSafetySourceData_differentPackageWithManageSafetyCenterPermission_returnsData() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexConfig) - @Before - fun enableSafetyCenterBeforeTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.setup() - } + val data = + callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) { + safetyCenterManager.getSafetySourceData(DYNAMIC_OTHER_PACKAGE_ID) + } - @After - fun clearDataAfterTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.reset() + assertThat(data).isNull() } @Test @@ -808,9 +876,9 @@ class SafetyCenterManagerTest { val status1 = listener.receiveSafetyCenterData().status assertThat(status1.refreshStatus).isEqualTo(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS) assertThat(status1.title.toString()) - .isEqualTo(safetyCenterResourcesContext.getStringByName("scanning_title")) + .isEqualTo(safetyCenterResourcesApk.getStringByName("scanning_title")) assertThat(status1.summary.toString()) - .isEqualTo(safetyCenterResourcesContext.getStringByName("loading_summary")) + .isEqualTo(safetyCenterResourcesApk.getStringByName("loading_summary")) val status2 = listener.receiveSafetyCenterData().status assertThat(status2.refreshStatus).isEqualTo(REFRESH_STATUS_NONE) assertThat(status2).isEqualTo(safetyCenterStatusOk) @@ -832,9 +900,9 @@ class SafetyCenterManagerTest { val status1 = listener.receiveSafetyCenterData().status assertThat(status1.refreshStatus).isEqualTo(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS) assertThat(status1.title.toString()) - .isEqualTo(safetyCenterResourcesContext.getStringByName("scanning_title")) + .isEqualTo(safetyCenterResourcesApk.getStringByName("scanning_title")) assertThat(status1.summary.toString()) - .isEqualTo(safetyCenterResourcesContext.getStringByName("loading_summary")) + .isEqualTo(safetyCenterResourcesApk.getStringByName("loading_summary")) val status2 = listener.receiveSafetyCenterData().status assertThat(status2.refreshStatus).isEqualTo(REFRESH_STATUS_NONE) assertThat(status2).isEqualTo(safetyCenterStatusOk) @@ -858,13 +926,78 @@ class SafetyCenterManagerTest { assertThat(status1.refreshStatus).isEqualTo(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS) assertThat(status1.title.toString()).isEqualTo(safetyCenterStatusOk.title.toString()) assertThat(status1.summary.toString()) - .isEqualTo(safetyCenterResourcesContext.getStringByName("loading_summary")) + .isEqualTo(safetyCenterResourcesApk.getStringByName("loading_summary")) val status2 = listener.receiveSafetyCenterData().status assertThat(status2.refreshStatus).isEqualTo(REFRESH_STATUS_NONE) assertThat(status2).isEqualTo(safetyCenterStatusOk) } @Test + fun refreshSafetySources_reasonPageOpen_allowedByFlag_broadcastSent() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig) + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) + SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID) + SafetySourceReceiver.setResponse( + Request.Refresh(SINGLE_SOURCE_ID), + Response.SetData(safetySourceTestData.informationWithIssue) + ) + + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN + ) + + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + assertThat(apiSafetySourceData).isEqualTo(safetySourceTestData.informationWithIssue) + } + + @Test + fun refreshSafetySources_reasonPageOpen_allowedByFlagLater_broadcastSentLater() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig) + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) + SafetySourceReceiver.setResponse( + Request.Refresh(SINGLE_SOURCE_ID), + Response.SetData(safetySourceTestData.informationWithIssue) + ) + + assertFailsWith(TimeoutCancellationException::class) { + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN, + timeout = TIMEOUT_SHORT + ) + } + val apiSafetySourceDataBeforeSettingFlag = + safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID) + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN + ) + val apiSafetySourceDataAfterSettingFlag = + safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + + assertThat(apiSafetySourceDataBeforeSettingFlag).isEqualTo(safetySourceTestData.information) + assertThat(apiSafetySourceDataAfterSettingFlag) + .isEqualTo(safetySourceTestData.informationWithIssue) + } + + @Test + fun refreshSafetySources_reasonPageOpen_noDataForSource_broadcastSent() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.noPageOpenConfig) + SafetySourceReceiver.setResponse( + Request.Refresh(SINGLE_SOURCE_ID), + Response.SetData(safetySourceTestData.information) + ) + + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN + ) + + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) + assertThat(apiSafetySourceData).isEqualTo(safetySourceTestData.information) + } + + @Test fun getSafetyCenterData_withoutDataProvided_returnsDataFromConfig() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) @@ -874,28 +1007,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) @@ -940,7 +1150,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_attributionTitleProvidedBySource_returnsIssueWithAttributionTitle() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( @@ -956,7 +1166,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_attributionTitleNotProvided_returnsGroupTitleAsAttributionTitle() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) @@ -969,7 +1179,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_attributionNotSetAndGroupTitleNull_returnsNullAttributionTitle() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceNoGroupTitleConfig) safetyCenterTestHelper.setData( @@ -997,9 +1207,6 @@ class SafetyCenterManagerTest { @Test @SdkSuppress(maxSdkVersion = TIRAMISU) fun getSafetyCenterData_attributionNotSetBySourceOnTiramisu_returnsNullAttributionTitle() { - // TODO(b/258228790): Remove after U is no longer in pre-release - assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake") - assumeFalse(Build.VERSION.CODENAME == "VanillaIceCream") safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) @@ -1074,7 +1281,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_withActionConfirmation_returnsRecommendationWithActionConfirmation() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) safetyCenterTestHelper.setData( @@ -1154,7 +1361,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_withRecommendationDataIssue_returnsDataRecommendationStatus() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1179,7 +1386,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_withCriticalDataIssue_returnsDataCriticalStatus() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1204,7 +1411,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_withRecommendationPasswordsIssue_returnsDataRecommendationStatus() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1229,7 +1436,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_withCriticalPasswordsIssue_returnsDataCriticalStatus() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1254,7 +1461,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_withRecommendationPersonalIssue_returnsDataRecommendationStatus() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1279,7 +1486,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_withCriticalPersonalIssue_returnsDataCriticalStatus() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1304,7 +1511,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_infoStatusTipFirstIssueSingleTip_infoStatusWithTipSummary() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1324,7 +1531,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_infoStatusTipFirstIssueMultiTips_infoStatusWithTipsSummary() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1360,7 +1567,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_infoStatusActionFirstIssueSingleAction_infoStatusWithActionSummary() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1380,7 +1587,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_infoStatusActionFirstIssueMultiActions_infoStatusWithActionsSummary() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1416,7 +1623,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_infoStatusManualFirstIssueSingleManual_infoStatusWithAlertSummary() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1442,7 +1649,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_infoStatusManualFirstIssueMultiManual_infoStatusWithAlertsSummary() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.issueOnlySourceConfig) safetyCenterTestHelper.setData( @@ -1480,7 +1687,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_withStaticEntryGroups_hasStaticEntriesToIdsMapping() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.staticSourcesConfig) @@ -1560,7 +1767,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesOfSameSeverities_issueOfFirstSourceInConfigShown() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1592,7 +1799,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesInDifferentSourceGroups_topIssueRelevantForBothGroups() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1622,7 +1829,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesInSameSourceGroups_topIssueRelevantForThatGroup() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1652,7 +1859,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_noDuplicateIssues_noGroupBelongingSpecified() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1680,7 +1887,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_differentDuplicationId_bothIssuesShown() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1717,7 +1924,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_differentDuplicationGroup_bothIssuesShown() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1754,7 +1961,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_threeDuplicateIssues_onlyOneIssueShown() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1793,7 +2000,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesOfDifferentSeverities_moreSevereIssueShown() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1825,7 +2032,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_multipleDuplicationsOfIssues_correctlyDeduplicated() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1901,7 +2108,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesBothDismissed_topOneShownAsDismissed() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1942,7 +2149,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesLowerSeverityOneDismissed_topOneShown() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -1980,7 +2187,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesHigherSeverityOneDismissed_topOneShownAsDismissed() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -2018,7 +2225,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_dupIssuesLowerPrioritySameSeverityOneDismissed_topShownAsDismissed() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -2056,7 +2263,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_dupIssuesTopOneDismissedThenDisappears_bottomOneReemergesTimely() { SafetyCenterFlags.tempHiddenIssueResurfaceDelay = Duration.ZERO SafetyCenterFlags.resurfaceIssueMaxCounts = mapOf(SEVERITY_LEVEL_CRITICAL_WARNING to 99L) @@ -2110,7 +2317,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_dupsOfDiffSeveritiesTopOneDismissedThenGone_bottomOneReemergesTimely() { SafetyCenterFlags.tempHiddenIssueResurfaceDelay = Duration.ZERO SafetyCenterFlags.resurfaceIssueMaxCounts = @@ -2173,7 +2380,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesLowerOneResurfaces_lowerOneStillFilteredOut() { SafetyCenterFlags.resurfaceIssueMaxCounts = mapOf( @@ -2236,7 +2443,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_duplicateIssuesTopOneResurfaces_topOneShown() { SafetyCenterFlags.resurfaceIssueMaxCounts = mapOf( @@ -2297,7 +2504,7 @@ class SafetyCenterManagerTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun getSafetyCenterData_dupIssuesTopOneResolved_bottomOneReemergesAfterTemporaryHiddenPeriod() { SafetyCenterFlags.tempHiddenIssueResurfaceDelay = RESURFACE_DELAY safetyCenterTestHelper.setConfig( @@ -2831,7 +3038,7 @@ class SafetyCenterManagerTest { safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID) assertThat(group.summary) - .isEqualTo(safetyCenterResourcesContext.getStringByName("group_unknown_summary")) + .isEqualTo(safetyCenterResourcesApk.getStringByName("group_unknown_summary")) assertThat(group.severityLevel).isEqualTo(ENTRY_SEVERITY_LEVEL_UNKNOWN) } @@ -2845,7 +3052,7 @@ class SafetyCenterManagerTest { .getGroup(ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID) assertThat(initialGroup.summary) - .isEqualTo(safetyCenterResourcesContext.getStringByName("group_unknown_summary")) + .isEqualTo(safetyCenterResourcesApk.getStringByName("group_unknown_summary")) assertThat(initialGroup.severityLevel).isEqualTo(ENTRY_SEVERITY_LEVEL_UNKNOWN) safetyCenterTestHelper.setData(DYNAMIC_BAREBONE_ID, safetySourceTestData.unspecified) @@ -3340,7 +3547,7 @@ class SafetyCenterManagerTest { assertThat(error) .isEqualTo( SafetyCenterErrorDetails( - safetyCenterResourcesContext.getStringByName("resolving_action_error") + safetyCenterResourcesApk.getStringByName("resolving_action_error") ) ) } @@ -3374,7 +3581,7 @@ class SafetyCenterManagerTest { assertThat(error) .isEqualTo( SafetyCenterErrorDetails( - safetyCenterResourcesContext.getStringByName("resolving_action_error") + safetyCenterResourcesApk.getStringByName("resolving_action_error") ) ) } @@ -3538,46 +3745,6 @@ class SafetyCenterManagerTest { } @Test - fun lockScreenSource_withoutReplaceLockScreenIconActionFlag_doesntReplace() { - // Must have a screen lock for the icon action to be set - assumeTrue(ScreenLockHelper.isDeviceSecure(context)) - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.settingsLockScreenSourceConfig) - val listener = safetyCenterTestHelper.addListener() - SafetyCenterFlags.replaceLockScreenIconAction = false - - safetyCenterManager.refreshSafetySourcesWithPermission(REFRESH_REASON_PAGE_OPEN) - // Skip loading data. - listener.receiveSafetyCenterData() - - val lockScreenSafetyCenterData = listener.receiveSafetyCenterData() - val lockScreenEntry = lockScreenSafetyCenterData.entriesOrGroups.first().entry!! - val entryPendingIntent = lockScreenEntry.pendingIntent!! - val iconActionPendingIntent = lockScreenEntry.iconAction!!.pendingIntent - // This test passes for now but will eventually start failing once we introduce the fix in - // the Settings app. This will warn if the assumption is failed rather than fail, at which - // point we can remove this test (and potentially even this magnificent hack). - assumeTrue(iconActionPendingIntent == entryPendingIntent) - } - - @Test - fun lockScreenSource_withReplaceLockScreenIconActionFlag_replaces() { - // Must have a screen lock for the icon action to be set - assumeTrue(ScreenLockHelper.isDeviceSecure(context)) - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.settingsLockScreenSourceConfig) - val listener = safetyCenterTestHelper.addListener() - - safetyCenterManager.refreshSafetySourcesWithPermission(REFRESH_REASON_PAGE_OPEN) - // Skip loading data. - listener.receiveSafetyCenterData() - - val lockScreenSafetyCenterData = listener.receiveSafetyCenterData() - val lockScreenEntry = lockScreenSafetyCenterData.entriesOrGroups.first().entry!! - val entryPendingIntent = lockScreenEntry.pendingIntent!! - val iconActionPendingIntent = lockScreenEntry.iconAction!!.pendingIntent - assertThat(iconActionPendingIntent).isNotEqualTo(entryPendingIntent) - } - - @Test fun beforeAnyDataSet_noLastUpdatedTimestamps() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) 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 70d6468fa..9c9e9b009 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterNotificationTest.kt @@ -28,19 +28,21 @@ import android.safetycenter.SafetyCenterStatus import android.safetycenter.SafetyEvent import android.safetycenter.SafetySourceErrorDetails import android.safetycenter.SafetySourceIssue -import android.safetycenter.functional.testing.NotificationCharacteristics -import android.safetycenter.functional.testing.TestNotificationListener +import android.service.notification.StatusBarNotification import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress +import com.android.compatibility.common.util.DisableAnimationRule +import com.android.compatibility.common.util.FreezeRotationRule import com.android.safetycenter.pendingintents.PendingIntentSender +import com.android.safetycenter.testing.Coroutines import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT +import com.android.safetycenter.testing.NotificationCharacteristics import com.android.safetycenter.testing.SafetyCenterActivityLauncher.executeBlockAndExit import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.clearAllSafetySourceDataForTestsWithPermission import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.dismissSafetyCenterIssueWithPermission import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.reportSafetySourceErrorWithPermission import com.android.safetycenter.testing.SafetyCenterFlags -import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter import com.android.safetycenter.testing.SafetyCenterTestConfigs import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_1 @@ -48,20 +50,23 @@ import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_5 import com.android.safetycenter.testing.SafetyCenterTestData import com.android.safetycenter.testing.SafetyCenterTestHelper +import com.android.safetycenter.testing.SafetyCenterTestRule import com.android.safetycenter.testing.SafetySourceIntentHandler.Request import com.android.safetycenter.testing.SafetySourceIntentHandler.Response import com.android.safetycenter.testing.SafetySourceReceiver import com.android.safetycenter.testing.SafetySourceTestData import com.android.safetycenter.testing.SafetySourceTestData.Companion.ISSUE_TYPE_ID import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity +import com.android.safetycenter.testing.StatusBarNotificationWithChannel +import com.android.safetycenter.testing.SupportsSafetyCenterRule +import com.android.safetycenter.testing.TestNotificationListener import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed import com.google.common.truth.Truth.assertThat import java.time.Duration import kotlin.test.assertFailsWith import kotlinx.coroutines.TimeoutCancellationException -import org.junit.After -import org.junit.Assume.assumeTrue import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -74,41 +79,23 @@ class SafetyCenterNotificationTest { private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) private val safetyCenterManager = requireNotNull(context.getSystemService(SafetyCenterManager::class.java)) { - "Could not get system service" + "Could not get SafetyCenterManager" } - // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to - // manually skip the setup and teardown methods. - private val shouldRunTests = context.deviceSupportsSafetyCenter() + @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) + @get:Rule(order = 2) + val safetyCenterTestRule = + SafetyCenterTestRule(safetyCenterTestHelper, withNotifications = true) + @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule() + @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule() @Before - fun assumeDeviceSupportsSafetyCenterToRunTests() { - assumeTrue(shouldRunTests) - } - - @Before - fun setUp() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.setup() - TestNotificationListener.setup() + fun enableNotificationsForTestSourceBeforeTest() { SafetyCenterFlags.notificationsEnabled = true setFlagsForImmediateNotifications(SINGLE_SOURCE_ID) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) } - @After - fun tearDown() { - if (!shouldRunTests) { - return - } - // It is important to reset the notification listener last because it waits/ensures that - // all notifications have been removed before returning. - safetyCenterTestHelper.reset() - TestNotificationListener.reset() - } - @Test fun setSafetySourceData_withNoIssue_noNotification() { safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) @@ -153,7 +140,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_withNotificationBehaviorNever_noNotification() { val data = safetySourceTestData @@ -172,7 +159,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_withNotificationBehaviorDelay_noImmediateNotification() { SafetyCenterFlags.notificationsMinDelay = Duration.ofDays(1) val data = @@ -192,7 +179,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_withNotificationBehaviorDelay_sendsNotificationAfterDelay() { SafetyCenterFlags.notificationsMinDelay = Duration.ofDays(1) val delayedNotificationIssue = @@ -237,7 +224,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_withNotificationBehaviorDelayOfZero_sendsNotificationImmediately() { SafetyCenterFlags.immediateNotificationBehaviorIssues = emptySet() SafetyCenterFlags.notificationsMinDelay = Duration.ofSeconds(0) @@ -264,7 +251,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_withNotificationBehaviorImmediately_sendsNotification() { SafetyCenterFlags.immediateNotificationBehaviorIssues = emptySet() val data = @@ -309,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 @@ -341,7 +328,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_withNotificationsAllowedForSourceByConfig_sendsNotification() { SafetyCenterFlags.notificationsAllowedSources = emptySet() SafetyCenterFlags.immediateNotificationBehaviorIssues = emptySet() @@ -372,10 +359,10 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @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") @@ -397,7 +384,7 @@ class SafetyCenterNotificationTest { SafetySourceIssue.Action.Builder( "default_action", "Default action", - safetySourceTestData.testActivityRedirectPendingIntent + safetySourceTestData.createTestActivityRedirectPendingIntent() ) .build() ) @@ -418,7 +405,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_withEmptyCustomActions_notificationHasNoActions() { val notification = SafetySourceIssue.Notification.Builder("Custom title", "Custom text") @@ -435,7 +422,7 @@ class SafetyCenterNotificationTest { SafetySourceIssue.Action.Builder( "default_action", "Default action", - safetySourceTestData.testActivityRedirectPendingIntent + safetySourceTestData.createTestActivityRedirectPendingIntent() ) .build() ) @@ -488,7 +475,7 @@ class SafetyCenterNotificationTest { SafetySourceIssue.Action.Builder( "new_action", "New action", - safetySourceTestData.testActivityRedirectPendingIntent( + safetySourceTestData.createTestActivityRedirectPendingIntent( identifier = "new_action" ) ) @@ -539,6 +526,22 @@ class SafetyCenterNotificationTest { TestNotificationListener.waitForZeroNotifications() } + // TODO(b/284271124): Decide what to do with existing notifications when flag flipped off + @Test + fun setSafetySourceData_removingAnIssue_afterFlagTurnedOff_noNotificationChanges() { + val data1 = safetySourceTestData.recommendationWithAccountIssue + val data2 = safetySourceTestData.information + + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data1) + + TestNotificationListener.waitForSingleNotification() + + SafetyCenterFlags.notificationsEnabled = false + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data2) + + TestNotificationListener.waitForZeroNotificationEvents() + } + @Test fun reportSafetySourceError_sourceWithNotification_cancelsNotification() { val data = safetySourceTestData.recommendationWithAccountIssue @@ -630,7 +633,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_duplicateIssues_sendsOneNotification() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -660,7 +663,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun setSafetySourceData_duplicateIssueOfLowerSeverityDismissed_sendsNotification() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -784,7 +787,7 @@ class SafetyCenterNotificationTest { } @Test - @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) fun dismissingNotification_withDuplicateIssues_allDismissed() { safetyCenterTestHelper.setConfig( safetyCenterTestConfigs.multipleSourcesWithDeduplicationInfoConfig @@ -898,13 +901,135 @@ class SafetyCenterNotificationTest { sendActionPendingIntentAndWaitWithPermission(action) - TestNotificationListener.waitForSingleNotificationMatching( - NotificationCharacteristics( - "Issue solved", - "", - actions = emptyList(), + TestNotificationListener.waitForSuccessNotification("Issue solved") + } + + @Test + fun successNotification_notificationHasAutoCancel() { + safetyCenterTestHelper.setData( + SINGLE_SOURCE_ID, + safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage + ) + val notificationWithChannel = TestNotificationListener.waitForSingleNotification() + val action = + notificationWithChannel.statusBarNotification.notification.actions.firstOrNull() + checkNotNull(action) { "Notification action unexpectedly null" } + SafetySourceReceiver.setResponse( + Request.ResolveAction(SINGLE_SOURCE_ID), + Response.SetData(safetySourceTestData.information) + ) + sendActionPendingIntentAndWaitWithPermission(action) + + TestNotificationListener.waitForSuccessNotification("Issue solved") { + assertThat(it.hasAutoCancel()).isTrue() + } + } + + // TODO(b/284271124): Decide what to do with existing notifications when flag flipped off + @Test + fun sendActionPendingIntent_flagDisabled_pendingIntentNotSentToSource() { + safetyCenterTestHelper.setData( + SINGLE_SOURCE_ID, + safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage + ) + val notificationWithChannel = TestNotificationListener.waitForSingleNotification() + val action = + notificationWithChannel.statusBarNotification.notification.actions.firstOrNull() + checkNotNull(action) { "Notification action unexpectedly null" } + SafetySourceReceiver.setResponse( + Request.ResolveAction(SINGLE_SOURCE_ID), + Response.SetData(safetySourceTestData.information) + ) + SafetyCenterFlags.notificationsEnabled = false + + assertFailsWith(TimeoutCancellationException::class) { + sendActionPendingIntentAndWaitWithPermission(action, timeout = TIMEOUT_SHORT) + } + } + + @Test + fun sendActionPendingIntent_sourceStateChangedSafetyEvent_successNotification() { + safetyCenterTestHelper.setData( + SINGLE_SOURCE_ID, + safetySourceTestData.criticalWithResolvingIssueWithSuccessMessage + ) + val notificationWithChannel = TestNotificationListener.waitForSingleNotification() + val action = + notificationWithChannel.statusBarNotification.notification.actions.firstOrNull() + checkNotNull(action) { "Notification action unexpectedly null" } + SafetySourceReceiver.setResponse( + Request.ResolveAction(SINGLE_SOURCE_ID), + Response.SetData( + safetySourceTestData.information, + overrideSafetyEvent = + SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() ) ) + + sendActionPendingIntentAndWaitWithPermission(action) + + TestNotificationListener.waitForSuccessNotification("Issue solved") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) + fun sendActionPendingIntent_actionIdDiffersFromIssueActionId_successNotification() { + val notification = + SafetySourceIssue.Notification.Builder("Custom title", "Custom text") + .addAction( + SafetySourceIssue.Action.Builder( + "notification_action_id", + "Solve now!", + safetySourceTestData.resolvingActionPendingIntent( + sourceIssueActionId = "notification_action_id" + ) + ) + .setWillResolve(true) + .setSuccessMessage("Solved via notification action :)") + .build() + ) + .build() + val data = + safetySourceTestData + .defaultCriticalDataBuilder() + .clearIssues() + .addIssue( + safetySourceTestData + .defaultCriticalResolvingIssueBuilder() + .clearActions() + .addAction( + SafetySourceIssue.Action.Builder( + "issue_action_id", + "Default action", + safetySourceTestData.resolvingActionPendingIntent( + sourceIssueActionId = "issue_action_id" + ) + ) + .setWillResolve(true) + .setSuccessMessage("Solved via issue action :(") + .build() + ) + .setCustomNotification(notification) + .setNotificationBehavior( + SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY + ) + .build() + ) + .build() + + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, data) + val notificationWithChannel = TestNotificationListener.waitForSingleNotification() + val action = + notificationWithChannel.statusBarNotification.notification.actions.firstOrNull() + checkNotNull(action) { "Notification action unexpectedly null" } + SafetySourceReceiver.setResponse( + Request.ResolveAction(SINGLE_SOURCE_ID), + Response.SetData(safetySourceTestData.information) + ) + + sendActionPendingIntentAndWaitWithPermission(action) + + TestNotificationListener.waitForSuccessNotification("Solved via notification action :)") } @Test @@ -941,12 +1066,10 @@ class SafetyCenterNotificationTest { safetySourceTestData.recommendationWithDeviceIssue ) val notificationWithChannel = TestNotificationListener.waitForSingleNotification() - val contentIntent = notificationWithChannel.statusBarNotification.notification.contentIntent - executeBlockAndExit( - launchActivity = { PendingIntentSender.send(contentIntent) }, - block = { waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue) } - ) + sendContentPendingIntent(notificationWithChannel) { + waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue) + } } @Test @@ -962,34 +1085,69 @@ class SafetyCenterNotificationTest { safetySourceTestData.criticalWithResolvingGeneralIssue ) val notificationWithChannel = TestNotificationListener.waitForSingleNotification() - val contentIntent = notificationWithChannel.statusBarNotification.notification.contentIntent - executeBlockAndExit( - launchActivity = { PendingIntentSender.send(contentIntent) }, - block = { - waitSourceIssueDisplayed(safetySourceTestData.criticalResolvingGeneralIssue) - waitSourceIssueDisplayed(safetySourceTestData.recommendationGeneralIssue) - } + sendContentPendingIntent(notificationWithChannel) { + waitSourceIssueDisplayed(safetySourceTestData.criticalResolvingGeneralIssue) + waitSourceIssueDisplayed(safetySourceTestData.recommendationDeviceIssue) + } + } + + @Test + fun whenGreenIssue_notificationHasAutoCancel() { + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) + val notificationWithChannel = TestNotificationListener.waitForSingleNotification() + + assertThat(notificationWithChannel.statusBarNotification.hasAutoCancel()).isTrue() + } + + @Test + fun whenNotGreenIssue_notificationDoesntHaveAutoCancel() { + safetyCenterTestHelper.setData( + SINGLE_SOURCE_ID, + safetySourceTestData.recommendationWithDeviceIssue ) + val notificationWithChannel = TestNotificationListener.waitForSingleNotification() + + assertThat(notificationWithChannel.statusBarNotification.hasAutoCancel()).isFalse() } - companion object { - private val SafetyCenterData.inFlightActions: List<SafetyCenterIssue.Action> + private companion object { + val SafetyCenterData.inFlightActions: List<SafetyCenterIssue.Action> get() = issues.flatMap { it.actions }.filter { it.isInFlight } - private fun sendActionPendingIntentAndWaitWithPermission(action: Notification.Action) { + fun sendActionPendingIntentAndWaitWithPermission( + action: Notification.Action, + timeout: Duration = Coroutines.TIMEOUT_LONG + ) { callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) { PendingIntentSender.send(action.actionIntent) // Sending the action's PendingIntent above is asynchronous and we need to wait for // it to be received by the fake receiver below. - SafetySourceReceiver.receiveResolveAction() + SafetySourceReceiver.receiveResolveAction(timeout) } } - private fun setFlagsForImmediateNotifications(vararg sourceIds: String) { + fun setFlagsForImmediateNotifications(vararg sourceIds: String) { SafetyCenterFlags.notificationsAllowedSources = sourceIds.toSet() SafetyCenterFlags.immediateNotificationBehaviorIssues = sourceIds.map { "$it/$ISSUE_TYPE_ID" }.toSet() } + + fun StatusBarNotification.hasAutoCancel(): Boolean { + val autoCancelMask = notification.flags and Notification.FLAG_AUTO_CANCEL + return autoCancelMask != 0 + } + + fun sendContentPendingIntent( + statusBarNotificationWithChannel: StatusBarNotificationWithChannel, + andExecuteBlock: () -> Unit = {} + ) { + val contentIntent = + statusBarNotificationWithChannel.statusBarNotification.notification.contentIntent + executeBlockAndExit( + launchActivity = { PendingIntentSender.send(contentIntent) }, + block = andExecuteBlock + ) + } } } diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterShellCommandsTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterShellCommandsTest.kt new file mode 100644 index 000000000..7c5b41944 --- /dev/null +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterShellCommandsTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 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.safetycenter.functional + +import android.Manifest.permission.INTERACT_ACROSS_USERS +import android.app.ActivityManager +import android.content.Context +import android.safetycenter.SafetyCenterManager +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compatibility.common.util.SystemUtil +import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.isSafetyCenterEnabledWithPermission +import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity +import com.android.safetycenter.testing.deviceSupportsSafetyCenter +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests for Safety Center's shell commands. */ +@RunWith(AndroidJUnit4::class) +class SafetyCenterShellCommandsTest { + private val context: Context = getApplicationContext() + + @Test + fun enabled_printsEnabledValue() { + val enabled = executeShellCommand("cmd safety_center enabled").toBoolean() + + val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!! + assertThat(enabled).isEqualTo(safetyCenterManager.isSafetyCenterEnabledWithPermission()) + } + + @Test + fun supported_printsSupportedValue() { + val supported = executeShellCommand("cmd safety_center supported").toBoolean() + + assertThat(supported).isEqualTo(context.deviceSupportsSafetyCenter()) + } + + @Test + fun packageName_printsPackageName() { + val packageName = executeShellCommand("cmd safety_center package-name") + + assertThat(packageName).isEqualTo(context.packageManager.permissionControllerPackageName) + } + + @Test + fun clearData_executesSuccessfully() { + executeShellCommand("cmd safety_center clear-data") + } + + @Test + fun refresh_executesSuccessfully() { + val currentUser = + callWithShellPermissionIdentity(INTERACT_ACROSS_USERS) { + ActivityManager.getCurrentUser() + } + executeShellCommand("cmd safety_center refresh --reason OTHER --user $currentUser") + } + + @Test + fun help_containsAllCommands() { + val help = executeShellCommand("cmd safety_center help") + + assertThat(help).contains("help") + assertThat(help).contains("enabled") + assertThat(help).contains("supported") + assertThat(help).contains("package-name") + assertThat(help).contains("clear-data") + assertThat(help).contains("refresh") + } + + @Test + fun dump_containsSafetyCenterService() { + val dump = executeShellCommand("dumpsys safety_center") + + assertThat(dump).contains("SafetyCenterService") + } + + private fun executeShellCommand(command: String): String = + SystemUtil.runShellCommandOrThrow(command).trim() +} diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt new file mode 100644 index 000000000..4ba293eb9 --- /dev/null +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetySourceDataFixesTest.kt @@ -0,0 +1,298 @@ +package android.safetycenter.functional + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE +import android.safetycenter.SafetyCenterManager +import android.safetycenter.SafetySourceData +import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION +import android.safetycenter.SafetySourceIssue +import android.safetycenter.SafetySourceStatus +import androidx.annotation.RequiresApi +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress +import com.android.compatibility.common.preconditions.ScreenLockHelper +import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.getSafetySourceDataWithPermission +import com.android.safetycenter.testing.SafetyCenterApisWithShellPermissions.refreshSafetySourcesWithPermission +import com.android.safetycenter.testing.SafetyCenterFlags +import com.android.safetycenter.testing.SafetyCenterTestConfigs +import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID +import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_1 +import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_2 +import com.android.safetycenter.testing.SafetyCenterTestHelper +import com.android.safetycenter.testing.SafetyCenterTestRule +import com.android.safetycenter.testing.SafetySourceTestData +import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity +import com.android.safetycenter.testing.SupportsSafetyCenterRule +import com.google.common.truth.Truth.assertThat +import org.junit.Assume.assumeTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** Functional tests for "fixes" applied to Safety Source data received by [SafetyCenterManager]. */ +@RunWith(AndroidJUnit4::class) +class SafetySourceDataFixesTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + private val safetyCenterTestHelper = SafetyCenterTestHelper(context) + private val safetySourceTestData = SafetySourceTestData(context) + private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) + private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!! + + @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) + @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper) + + @Test + fun lockScreenSource_withoutReplaceLockScreenIconActionFlag_doesntReplace() { + // Must have a screen lock for the icon action to be set + assumeTrue(ScreenLockHelper.isDeviceSecure(context)) + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.settingsLockScreenSourceConfig) + val listener = safetyCenterTestHelper.addListener() + SafetyCenterFlags.replaceLockScreenIconAction = false + + safetyCenterManager.refreshSafetySourcesWithPermission( + SafetyCenterManager.REFRESH_REASON_PAGE_OPEN + ) + // Skip loading data. + listener.receiveSafetyCenterData() + + val lockScreenSafetyCenterData = listener.receiveSafetyCenterData() + val lockScreenEntry = lockScreenSafetyCenterData.entriesOrGroups.first().entry!! + val entryPendingIntent = lockScreenEntry.pendingIntent!! + val iconActionPendingIntent = lockScreenEntry.iconAction!!.pendingIntent + // This test passes for now but will eventually start failing once we introduce the fix in + // the Settings app. This will warn if the assumption is failed rather than fail, at which + // point we can remove this test (and potentially even this magnificent hack). + assumeTrue(iconActionPendingIntent == entryPendingIntent) + } + + @Test + fun lockScreenSource_withReplaceLockScreenIconActionFlag_replaces() { + // Must have a screen lock for the icon action to be set + assumeTrue(ScreenLockHelper.isDeviceSecure(context)) + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.settingsLockScreenSourceConfig) + val listener = safetyCenterTestHelper.addListener() + + safetyCenterManager.refreshSafetySourcesWithPermission( + SafetyCenterManager.REFRESH_REASON_PAGE_OPEN + ) + // Skip loading data. + listener.receiveSafetyCenterData() + + val lockScreenSafetyCenterData = listener.receiveSafetyCenterData() + val lockScreenEntry = lockScreenSafetyCenterData.entriesOrGroups.first().entry!! + val entryPendingIntent = lockScreenEntry.pendingIntent!! + val iconActionPendingIntent = lockScreenEntry.iconAction!!.pendingIntent + assertThat(iconActionPendingIntent).isNotEqualTo(entryPendingIntent) + } + + @Test + fun defaultActionOverride_issue_overridesMatchingActions() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + val targetActionId = "TargetActionId" + SafetyCenterFlags.actionsToOverrideWithDefaultIntent = + mapOf(SINGLE_SOURCE_ID to setOf(targetActionId, "AdditionalActionId")) + + val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT")) + val dataWithActionToOverride = + sourceDataBuilder() + .addIssue( + issueBuilder() + .clearActions() + .addAction( + safetySourceTestData.action( + id = targetActionId, + pendingIntent = originalPendingIntent + ) + ) + .build() + ) + .build() + + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataWithActionToOverride) + + val overriddenPendingIntent = + safetyCenterManager + .getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)!! + .issues[0] + .actions[0] + .pendingIntent + val expectedPendingIntent = + pendingIntent( + Intent(SafetyCenterTestConfigs.ACTION_TEST_ACTIVITY).setPackage(context.packageName) + ) + assertThat(intentsFilterEqual(overriddenPendingIntent, expectedPendingIntent)).isTrue() + } + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE) + fun defaultActionOverride_notification_overridesMatchingActions() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + val targetActionId = "TargetActionId" + SafetyCenterFlags.actionsToOverrideWithDefaultIntent = + mapOf(SINGLE_SOURCE_ID to setOf(targetActionId, "AdditionalActionId")) + + val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT")) + val dataWithNotificationActionToOverride = + sourceDataBuilder() + .addIssue( + issueBuilder() + .setCustomNotification( + notification( + safetySourceTestData.action( + id = targetActionId, + pendingIntent = originalPendingIntent + ) + ) + ) + .build() + ) + .build() + + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataWithNotificationActionToOverride) + + val overriddenPendingIntent = + safetyCenterManager + .getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)!! + .issues[0] + .customNotification!! + .actions[0] + .pendingIntent + val expectedPendingIntent = + pendingIntent( + Intent(SafetyCenterTestConfigs.ACTION_TEST_ACTIVITY).setPackage(context.packageName) + ) + assertThat(intentsFilterEqual(overriddenPendingIntent, expectedPendingIntent)).isTrue() + } + + @Test + fun defaultActionOverride_sameActionIdDifferentSource_doesNotOverride() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) + val targetActionId = "TargetActionId" + SafetyCenterFlags.actionsToOverrideWithDefaultIntent = + mapOf(SOURCE_ID_1 to setOf(targetActionId, "AdditionalActionId")) + + val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT")) + val dataWithoutActionToOverride = + sourceDataBuilder() + .addIssue( + issueBuilder() + .clearActions() + .addAction( + safetySourceTestData.action( + id = targetActionId, + pendingIntent = originalPendingIntent + ) + ) + .build() + ) + .build() + + safetyCenterTestHelper.setData( + SOURCE_ID_2, // Different source ID + dataWithoutActionToOverride + ) + + val actualPendingIntent = + safetyCenterManager + .getSafetySourceDataWithPermission(SOURCE_ID_2)!! + .issues[0] + .actions[0] + .pendingIntent + assertThat(intentsFilterEqual(actualPendingIntent, originalPendingIntent)).isTrue() + } + + @Test + fun defaultActionOverride_sameSourceDifferentActionId_doesNotOverride() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) + SafetyCenterFlags.actionsToOverrideWithDefaultIntent = + mapOf(SOURCE_ID_1 to setOf("TargetActionId")) + + val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT")) + val dataWithoutActionToOverride = + sourceDataBuilder() + .addIssue( + issueBuilder() + .clearActions() + .addAction( + safetySourceTestData.action( + id = "DifferentActionId", + pendingIntent = originalPendingIntent + ) + ) + .build() + ) + .build() + + safetyCenterTestHelper.setData(SOURCE_ID_1, dataWithoutActionToOverride) + + val actualPendingIntent = + safetyCenterManager + .getSafetySourceDataWithPermission(SOURCE_ID_1)!! + .issues[0] + .actions[0] + .pendingIntent + assertThat(intentsFilterEqual(actualPendingIntent, originalPendingIntent)).isTrue() + } + + @Test + fun defaultActionOverride_noDefaultIntent_doesNotOverride() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceInvalidIntentConfig) + val targetActionId = "TargetActionId" + SafetyCenterFlags.actionsToOverrideWithDefaultIntent = + mapOf(SINGLE_SOURCE_ID to setOf(targetActionId, "AdditionalActionId")) + + val originalPendingIntent = pendingIntent(Intent("blah.wrong.INTENT")) + val dataWithActionToOverride = + sourceDataBuilder() + .addIssue( + issueBuilder() + .clearActions() + .addAction( + safetySourceTestData.action( + id = targetActionId, + pendingIntent = originalPendingIntent + ) + ) + .build() + ) + .build() + + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, dataWithActionToOverride) + + val actualPendingIntent = + safetyCenterManager + .getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)!! + .issues[0] + .actions[0] + .pendingIntent + assertThat(intentsFilterEqual(actualPendingIntent, originalPendingIntent)).isTrue() + } + + private fun issueBuilder() = safetySourceTestData.defaultInformationIssueBuilder() + + private fun pendingIntent(intent: Intent) = + PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) + + companion object { + private fun sourceDataBuilder() = + SafetySourceData.Builder() + .setStatus( + SafetySourceStatus.Builder("OK", "Blah", SEVERITY_LEVEL_INFORMATION).build() + ) + + @RequiresApi(UPSIDE_DOWN_CAKE) + private fun notification(action: SafetySourceIssue.Action) = + SafetySourceIssue.Notification.Builder("Blah", "Bleh").addAction(action).build() + + private fun intentsFilterEqual( + actualPendingIntent: PendingIntent, + expectedPendingIntent: PendingIntent? + ) = + callWithShellPermissionIdentity("android.permission.GET_INTENT_SENDER_INTENT") { + actualPendingIntent.intentFilterEquals(expectedPendingIntent) + } + } +} diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/NotificationCharacteristics.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/NotificationCharacteristics.kt deleted file mode 100644 index b91c72422..000000000 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/NotificationCharacteristics.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2022 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.safetycenter.functional.testing - -import android.app.Notification - -/** The characteristic properties of a notification. */ -data class NotificationCharacteristics( - val title: String, - val text: String, - val actions: List<CharSequence> = emptyList(), - val importance: Int = IMPORTANCE_ANY, - val blockable: Boolean? = null -) { - companion object { - const val IMPORTANCE_ANY = -1 - - private fun importanceMatches( - statusBarNotificationWithChannel: StatusBarNotificationWithChannel, - characteristicImportance: Int - ): Boolean { - return characteristicImportance == IMPORTANCE_ANY || - statusBarNotificationWithChannel.channel.importance == characteristicImportance - } - - private fun blockableMatches( - statusBarNotificationWithChannel: StatusBarNotificationWithChannel, - characteristicBlockable: Boolean? - ): Boolean { - return characteristicBlockable == null || - statusBarNotificationWithChannel.channel.isBlockable == characteristicBlockable - } - - private fun isMatch( - statusBarNotificationWithChannel: StatusBarNotificationWithChannel, - characteristic: NotificationCharacteristics - ): Boolean { - val notif = statusBarNotificationWithChannel.statusBarNotification.notification - return notif != null && - notif.extras.getString(Notification.EXTRA_TITLE) == characteristic.title && - notif.extras.getString(Notification.EXTRA_TEXT).orEmpty() == characteristic.text && - notif.actions.orEmpty().map { it.title } == characteristic.actions && - importanceMatches(statusBarNotificationWithChannel, characteristic.importance) && - blockableMatches(statusBarNotificationWithChannel, characteristic.blockable) - } - - fun areMatching( - statusBarNotifications: List<StatusBarNotificationWithChannel>, - characteristics: List<NotificationCharacteristics> - ): Boolean { - if (statusBarNotifications.size != characteristics.size) { - return false - } - return statusBarNotifications.zip(characteristics).all { isMatch(it.first, it.second) } - } - } -} diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/StatusBarNotificationWithChannel.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/StatusBarNotificationWithChannel.kt deleted file mode 100644 index d9a998c3e..000000000 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/StatusBarNotificationWithChannel.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2023 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.safetycenter.functional.testing - -import android.app.NotificationChannel -import android.service.notification.StatusBarNotification - -/** Tuple of [StatusBarNotification] and the [NotificationChannel] it was posted to. */ -data class StatusBarNotificationWithChannel( - val statusBarNotification: StatusBarNotification, - val channel: NotificationChannel -) diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/TestNotificationListener.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/TestNotificationListener.kt deleted file mode 100644 index 113ad3b23..000000000 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/testing/TestNotificationListener.kt +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2022 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.safetycenter.functional.testing - -import android.app.NotificationChannel -import android.content.ComponentName -import android.os.ConditionVariable -import android.service.notification.NotificationListenerService -import android.service.notification.StatusBarNotification -import android.util.Log -import com.android.compatibility.common.util.SystemUtil -import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG -import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT -import com.android.safetycenter.testing.Coroutines.runBlockingWithTimeout -import com.android.safetycenter.testing.Coroutines.runBlockingWithTimeoutOrNull -import com.android.safetycenter.testing.Coroutines.waitForWithTimeout -import com.google.common.truth.Truth.assertThat -import java.time.Duration -import java.util.concurrent.TimeoutException -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.channels.Channel - -/** Used in tests to check whether expected notifications are present in the status bar. */ -class TestNotificationListener : NotificationListenerService() { - - private sealed class NotificationEvent(val statusBarNotification: StatusBarNotification) - - private class NotificationPosted(statusBarNotification: StatusBarNotification) : - NotificationEvent(statusBarNotification) { - override fun toString(): String = "Posted $statusBarNotification" - } - - private class NotificationRemoved(statusBarNotification: StatusBarNotification) : - NotificationEvent(statusBarNotification) { - override fun toString(): String = "Removed $statusBarNotification" - } - - override fun onNotificationPosted(statusBarNotification: StatusBarNotification) { - super.onNotificationPosted(statusBarNotification) - if (statusBarNotification.isSafetyCenterNotification()) { - runBlockingWithTimeout { - safetyCenterNotificationEvents.send(NotificationPosted(statusBarNotification)) - } - } - } - - override fun onNotificationRemoved(statusBarNotification: StatusBarNotification) { - super.onNotificationRemoved(statusBarNotification) - if (statusBarNotification.isSafetyCenterNotification()) { - runBlockingWithTimeout { - safetyCenterNotificationEvents.send(NotificationRemoved(statusBarNotification)) - } - } - } - - override fun onListenerConnected() { - Log.d(TAG, "onListenerConnected") - super.onListenerConnected() - disconnected.close() - instance = this - connected.open() - } - - override fun onListenerDisconnected() { - Log.d(TAG, "onListenerDisconnected") - super.onListenerDisconnected() - connected.close() - instance = null - disconnected.open() - } - - companion object { - private const val TAG = "TestNotificationListene" - - private val id: String = - "android.safetycenter.functional/" + TestNotificationListener::class.java.name - private val componentName = - ComponentName( - "android.safetycenter.functional", - TestNotificationListener::class.java.name - ) - - private val connected = ConditionVariable(false) - private val disconnected = ConditionVariable(true) - private var instance: TestNotificationListener? = null - - @Volatile - private var safetyCenterNotificationEvents = - Channel<NotificationEvent>(capacity = Channel.UNLIMITED) - - /** - * Blocks until there are zero Safety Center notifications and there remain zero for a short - * duration. Throws an [AssertionError] if a this condition is not met within [timeout], or - * if it is met and then violated. - */ - fun waitForZeroNotifications(timeout: Duration = TIMEOUT_LONG) { - waitForNotificationCount(0, timeout) - } - - /** - * Blocks until there is exactly one Safety Center notification and ensures that remains - * true for a short duration. Returns that notification, or throws an [AssertionError] if a - * this condition is not met within [timeout], or if it is met and then violated. - */ - fun waitForSingleNotification( - timeout: Duration = TIMEOUT_LONG - ): StatusBarNotificationWithChannel { - return waitForNotificationCount(1, timeout).first() - } - - /** - * Blocks until there are exactly [count] Safety Center notifications and ensures that - * remains true for a short duration. Returns those notifications, or throws an - * [AssertionError] if a this condition is not met within [timeout], or if it is met and - * then violated. - */ - private fun waitForNotificationCount( - count: Int, - timeout: Duration = TIMEOUT_LONG - ): List<StatusBarNotificationWithChannel> { - return waitForNotificationsToSatisfy(timeout, description = "$count notifications") { - it.size == count - } - } - - /** - * Blocks until there is a single Safety Center notification, which matches the given - * [characteristics] and ensures that remains true for a short duration. Returns that - * notification, or throws an [AssertionError] if a this condition is not met within - * [timeout], or if it is met and then violated. - */ - fun waitForSingleNotificationMatching( - characteristics: NotificationCharacteristics, - timeout: Duration = TIMEOUT_LONG - ): StatusBarNotificationWithChannel { - return waitForNotificationsMatching(characteristics, timeout = timeout).first() - } - - /** - * Blocks until the Safety Center notifications match the given [characteristics] and - * ensures that remains true for a short duration. Returns those notifications, or throws an - * [AssertionError] if a this condition is not met within [timeout], or if it is met and - * then violated. - */ - fun waitForNotificationsMatching( - vararg characteristics: NotificationCharacteristics, - timeout: Duration = TIMEOUT_LONG - ): List<StatusBarNotificationWithChannel> { - val charsList = characteristics.toList() - return waitForNotificationsToSatisfy( - timeout, - description = "notification(s) matching characteristics $charsList" - ) { NotificationCharacteristics.areMatching(it, charsList) } - } - - /** - * Blocks until [forAtLeast] has elapsed, or throw an [AssertionError] if any notification - * is posted or removed before then. - */ - fun waitForZeroNotificationEvents(forAtLeast: Duration = TIMEOUT_SHORT) { - val event = - runBlockingWithTimeoutOrNull(forAtLeast) { - safetyCenterNotificationEvents.receive() - } - assertThat(event).isNull() - } - - private fun waitForNotificationsToSatisfy( - timeout: Duration = TIMEOUT_LONG, - forAtLeast: Duration = TIMEOUT_SHORT, - description: String, - predicate: (List<StatusBarNotificationWithChannel>) -> Boolean - ): List<StatusBarNotificationWithChannel> { - fun formatError(notifs: List<StatusBarNotificationWithChannel>): String { - return "Expected: $description, but the actual notifications were: $notifs" - } - - // First we wait at most timeout for the active notifications to satisfy the given - // predicate or otherwise we throw: - val satisfyingNotifications = - try { - runBlockingWithTimeout(timeout) { - waitForNotificationsToSatisfyAsync(predicate) - } - } catch (e: TimeoutCancellationException) { - throw AssertionError(formatError(getSafetyCenterNotifications()), e) - } - - // Assuming the predicate was satisfied, now we ensure it is not violated for the - // forAtLeast duration as well: - val nonSatisfyingNotifications = - runBlockingWithTimeoutOrNull(forAtLeast) { - waitForNotificationsToSatisfyAsync { !predicate(it) } - } - if (nonSatisfyingNotifications != null) { - // In this case the negated-predicate was satisfied before forAtLeast had elapsed - throw AssertionError(formatError(nonSatisfyingNotifications)) - } - - return satisfyingNotifications - } - - private suspend fun waitForNotificationsToSatisfyAsync( - predicate: (List<StatusBarNotificationWithChannel>) -> Boolean - ): List<StatusBarNotificationWithChannel> { - var currentNotifications = getSafetyCenterNotifications() - while (!predicate(currentNotifications)) { - val event = safetyCenterNotificationEvents.receive() - Log.d(TAG, "Received notification event: $event") - currentNotifications = getSafetyCenterNotifications() - } - return currentNotifications - } - - private fun getSafetyCenterNotifications(): List<StatusBarNotificationWithChannel> { - return with(getInstanceOrThrow()) { - val notificationsSnapshot = - checkNotNull(getActiveNotifications()) { - "getActiveNotifications() returned null" - } - val rankingSnapshot = - checkNotNull(getCurrentRanking()) { "getCurrentRanking() returned null" } - - fun getChannel(key: String): NotificationChannel? { - // This API uses a result parameter: - val rankingOut = Ranking() - val success = rankingSnapshot.getRanking(key, rankingOut) - return if (success) { - rankingOut.channel - } else { - null - } - } - - notificationsSnapshot - .filter { it.isSafetyCenterNotification() } - .mapNotNull { statusBarNotification -> - val channel = getChannel(statusBarNotification.key) - if (channel != null) { - StatusBarNotificationWithChannel(statusBarNotification, channel) - } else { - null - } - } - } - } - - private fun getInstanceOrThrow(): TestNotificationListener { - // We want to check the current values of the connected and disconnected - // ConditionVariables, but importantly block(0) actually does not timeout immediately! - val isConnected = connected.block(1) - val isDisconnected = disconnected.block(1) - check(isConnected == !isDisconnected) { - "Notification listener condition variables are inconsistent" - } - check(isConnected && !isDisconnected) { - "Notification listener was unexpectedly disconnected" - } - return checkNotNull(instance) { "Notification listener was unexpectedly null" } - } - - /** - * Cancels a specific notification and then waits for it to be removed by the notification - * manager and marked as dismissed in Safety Center, or throws if it has not been removed - * within [timeout]. - */ - fun cancelAndWait(key: String, timeout: Duration = TIMEOUT_LONG) { - getInstanceOrThrow().cancelNotification(key) - waitForNotificationsToSatisfy( - timeout, - description = "no notification with the key $key" - ) { notifications -> notifications.none { it.statusBarNotification.key == key } } - - waitForIssueCacheToContainAnyDismissedNotification() - } - - private fun waitForIssueCacheToContainAnyDismissedNotification() { - // Here we wait for an issue to be recorded as dismissed according to the dumpsys - // output. The cancelAndWait helper above first "waits" for the notification to - // be dismissed, but this additional wait is needed to ensure the notification's delete - // PendingIntent is handled. Without this wait there is a race condition between - // SafetyCenterNotificationReceiver#onReceive and subsequent calls that set source data - // and that race makes tests flaky because the dismissal status of the previous - // notification is not well defined. - fun dumpIssueDismissalsRepositoryState(): String = - SystemUtil.runShellCommand("dumpsys safety_center data") - try { - waitForWithTimeout { - dumpIssueDismissalsRepositoryState() - .contains(Regex("""mNotificationDismissedAt=\d+""")) - } - } catch (e: TimeoutCancellationException) { - throw IllegalStateException( - "Notification dismissal was not recorded in the issue cache: " + - dumpIssueDismissalsRepositoryState(), - e - ) - } - } - - /** Runs a shell command to allow or disallow the listener. Use before and after test. */ - private fun toggleListenerAccess(allowed: Boolean) { - // TODO(b/260335646): Try to do this using the AndroidTest.xml instead of in code - val verb = if (allowed) "allow" else "disallow" - SystemUtil.runShellCommand("cmd notification ${verb}_listener $id") - if (allowed) { - requestRebind(componentName) - if (!connected.block(TIMEOUT_LONG.toMillis())) { - throw TimeoutException("Notification listener did not connect in $TIMEOUT_LONG") - } - } else { - if (!disconnected.block(TIMEOUT_LONG.toMillis())) { - throw TimeoutException( - "Notification listener did not disconnect in $TIMEOUT_LONG" - ) - } - } - } - - /** Prepare the [TestNotificationListener] for a notification test */ - fun setup() { - toggleListenerAccess(true) - } - - /** Clean up the [TestNotificationListener] after executing a notification test. */ - fun reset() { - waitForNotificationsToSatisfy( - forAtLeast = Duration.ZERO, - description = "all Safety Center notifications removed in tear down" - ) { it.isEmpty() } - toggleListenerAccess(false) - safetyCenterNotificationEvents.cancel() - safetyCenterNotificationEvents = Channel(capacity = Channel.UNLIMITED) - } - - private fun StatusBarNotification.isSafetyCenterNotification(): Boolean = - packageName == "android" && notification.channelId.startsWith("safety_center") - } -} diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt deleted file mode 100644 index 0c90029e0..000000000 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/PrivacySubpageTest.kt +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2023 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.safetycenter.functional.ui - -import android.content.Context -import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE -import android.os.Bundle -import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID -import android.safetycenter.config.SafetySource -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import androidx.test.uiautomator.By -import com.android.compatibility.common.util.DisableAnimationRule -import com.android.compatibility.common.util.FreezeRotationRule -import com.android.compatibility.common.util.UiAutomatorUtils2 -import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity -import com.android.safetycenter.testing.SafetyCenterActivityLauncher.openPageAndExit -import com.android.safetycenter.testing.SafetyCenterFlags -import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter -import com.android.safetycenter.testing.SafetyCenterTestConfigs -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.PRIVACY_SOURCE_ID_1 -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_1 -import com.android.safetycenter.testing.SafetyCenterTestHelper -import com.android.safetycenter.testing.SafetySourceTestData -import com.android.safetycenter.testing.UiTestHelper.MORE_ISSUES_LABEL -import com.android.safetycenter.testing.UiTestHelper.clickMoreIssuesCard -import com.android.safetycenter.testing.UiTestHelper.resetRotation -import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitButtonDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueNotDisplayed -import org.junit.After -import org.junit.Assume.assumeTrue -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -/** Functional tests for the Privacy subpage in Safety Center. */ -@RunWith(AndroidJUnit4::class) -@SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") -class PrivacySubpageTest { - - @get:Rule val disableAnimationRule = DisableAnimationRule() - - @get:Rule val freezeRotationRule = FreezeRotationRule() - - private val context: Context = getApplicationContext() - private val safetyCenterTestHelper = SafetyCenterTestHelper(context) - private val safetySourceTestData = SafetySourceTestData(context) - private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) - - // JUnit's Assume is not supported in @BeforeClass by the tests runner, so this is used to - // manually skip the setup and teardown methods. - private val shouldRunTests = context.deviceSupportsSafetyCenter() - - @Before - fun assumeDeviceSupportsSafetyCenterToRunTests() { - assumeTrue(shouldRunTests) - } - - @Before - fun enableSafetyCenterBeforeTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.setup() - SafetyCenterFlags.showSubpages = true - } - - @After - fun clearDataAfterTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.reset() - UiAutomatorUtils2.getUiDevice().resetRotation() - } - - @Test - fun privacySubpage_openWithIntentExtra_showsSubpageData() { - val config = safetyCenterTestConfigs.privacySubpageConfig - safetyCenterTestHelper.setConfig(config) - val sourcesGroup = config.safetySourcesGroups.first() - val firstSource: SafetySource = sourcesGroup.safetySources.first() - val lastSource: SafetySource = sourcesGroup.safetySources.last() - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, sourcesGroup.id) - - context.launchSafetyCenterActivity(extras) { - waitAllTextDisplayed( - context.getString(firstSource.titleResId), - context.getString(firstSource.summaryResId), - "Controls", - context.getString(lastSource.titleResId), - context.getString(lastSource.summaryResId) - ) - } - } - - @Test - fun privacySubpage_clickingOnEntry_redirectsToDifferentScreen() { - val config = safetyCenterTestConfigs.privacySubpageConfig - safetyCenterTestHelper.setConfig(config) - val sourcesGroup = config.safetySourcesGroups.first() - val source: SafetySource = sourcesGroup.safetySources.first() - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, sourcesGroup.id) - - context.launchSafetyCenterActivity(extras) { - waitDisplayed(By.text(context.getString(source.titleResId))) { it.click() } - waitButtonDisplayed("Exit test activity") { it.click() } - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId) - ) - } - } - - @Test - fun privacySubpage_withMultipleIssues_displaysExpectedWarningCards() { - val config = safetyCenterTestConfigs.privacySubpageConfig - safetyCenterTestHelper.setConfig(config) - val firstSourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - val secondSourceData = safetySourceTestData.informationWithIssueWithAttributionTitle - safetyCenterTestHelper.setData(PRIVACY_SOURCE_ID_1, firstSourceData) - safetyCenterTestHelper.setData(SOURCE_ID_1, secondSourceData) - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, config.safetySourcesGroups.first().id) - - context.launchSafetyCenterActivity(extras) { - waitSourceIssueDisplayed(firstSourceData.issues[0]) - waitAllTextDisplayed(MORE_ISSUES_LABEL) - waitSourceIssueNotDisplayed(secondSourceData.issues[0]) - - clickMoreIssuesCard() - - waitSourceIssueDisplayed(firstSourceData.issues[0]) - waitAllTextDisplayed(MORE_ISSUES_LABEL) - waitSourceIssueDisplayed(secondSourceData.issues[0]) - } - } - - @Test - fun privacySubpage_openWithIntentExtra_showsPrivacyControls() { - val config = safetyCenterTestConfigs.privacySubpageConfig - safetyCenterTestHelper.setConfig(config) - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, config.safetySourcesGroups.first().id) - - context.launchSafetyCenterActivity(extras) { - waitAllTextDisplayed( - "Camera access", - "Microphone access", - "Show clipboard access", - "Show passwords", - "Location access" - ) - } - } - - @Test - fun privacySubpage_clickingOnLocationEntry_redirectsToLocationScreen() { - val config = safetyCenterTestConfigs.privacySubpageConfig - safetyCenterTestHelper.setConfig(config) - val sourcesGroup = config.safetySourcesGroups.first() - val source: SafetySource = sourcesGroup.safetySources.first() - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, sourcesGroup.id) - - context.launchSafetyCenterActivity(extras) { - openPageAndExit("Location access") { - waitPageTitleDisplayed("Location") - waitAllTextDisplayed("Use location") - } - - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId) - ) - } - } - - @Test - fun settingsSearch_openWithPrivacyIntentExtra_showsPrivacySubpage() { - val config = safetyCenterTestConfigs.privacySubpageConfig - val sourcesGroup = config.safetySourcesGroups.first() - val source: SafetySource = sourcesGroup.safetySources.first() - safetyCenterTestHelper.setConfig(config) - val extras = Bundle() - extras.putString(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY, "privacy_camera_toggle") - - context.launchSafetyCenterActivity(extras) { - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId), - "Controls", - ) - } - } - - companion object { - private const val EXTRA_SETTINGS_FRAGMENT_ARGS_KEY = ":settings:fragment_args_key" - } -} diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterQsActivityTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterQsActivityTest.kt index 9c4d720d3..73d6a0737 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterQsActivityTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterQsActivityTest.kt @@ -16,29 +16,23 @@ package android.safetycenter.functional.ui -import android.Manifest.permission.MANAGE_SENSOR_PRIVACY -import android.Manifest.permission.OBSERVE_SENSOR_PRIVACY import android.content.Context -import android.hardware.SensorPrivacyManager import android.hardware.SensorPrivacyManager.Sensors.CAMERA import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE -import android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE -import android.platform.test.rule.ScreenRecordRule import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.uiautomator.By import com.android.compatibility.common.util.DisableAnimationRule import com.android.compatibility.common.util.FreezeRotationRule +import com.android.safetycenter.testing.EnableSensorRule import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterQsActivity -import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter import com.android.safetycenter.testing.SafetyCenterTestConfigs import com.android.safetycenter.testing.SafetyCenterTestHelper -import com.android.safetycenter.testing.ShellPermissions.callWithShellPermissionIdentity +import com.android.safetycenter.testing.SafetyCenterTestRule +import com.android.safetycenter.testing.SupportsSafetyCenterRule import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed import com.android.safetycenter.testing.UiTestHelper.waitDisplayed import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed -import org.junit.After -import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -48,66 +42,22 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SafetyCenterQsActivityTest { - @get:Rule val disableAnimationRule = DisableAnimationRule() - - @get:Rule val freezeRotationRule = FreezeRotationRule() - - @get:Rule val screenRecordRule = ScreenRecordRule() - private val context: Context = getApplicationContext() private val safetyCenterTestHelper = SafetyCenterTestHelper(context) private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) - private val sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!! - private var shouldRunTests = - context.deviceSupportsSafetyCenter() && - deviceSupportsSensorToggle(CAMERA) && - deviceSupportsSensorToggle(MICROPHONE) - private var oldCameraState: Boolean = false - private var oldMicrophoneState: Boolean = false - @Before - fun assumeDeviceSupportsSafetyCenterToRunTests() { - assumeTrue(shouldRunTests) - } + @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) + @get:Rule(order = 2) val enableCameraRule = EnableSensorRule(context, CAMERA) + @get:Rule(order = 3) val enableMicrophoneRule = EnableSensorRule(context, MICROPHONE) + @get:Rule(order = 4) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper) + @get:Rule(order = 5) val disableAnimationRule = DisableAnimationRule() + @get:Rule(order = 6) val freezeRotationRule = FreezeRotationRule() @Before - fun enableSafetyCenterBeforeTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.setup() + fun setTestConfigBeforeTest() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) } - @After - fun clearDataAfterTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.reset() - } - - @Before - fun enablePrivacyControlsBeforeTest() { - if (!shouldRunTests) { - return - } - oldCameraState = isSensorEnabled(CAMERA) - setSensorState(CAMERA, true) - - oldMicrophoneState = isSensorEnabled(MICROPHONE) - setSensorState(MICROPHONE, true) - } - - @After - fun restorePrivacyControlsAfterTest() { - if (!shouldRunTests) { - return - } - setSensorState(CAMERA, oldCameraState) - setSensorState(MICROPHONE, oldMicrophoneState) - } - @Test fun launchActivity_fromQuickSettings_hasContentDescriptions() { context.launchSafetyCenterQsActivity { @@ -122,7 +72,6 @@ class SafetyCenterQsActivityTest { } @Test - @ScreenRecordRule.ScreenRecord fun launchActivity_togglePrivacyControls_hasUpdatedDescriptions() { context.launchSafetyCenterQsActivity { // Toggle privacy controls @@ -134,25 +83,4 @@ class SafetyCenterQsActivityTest { waitDisplayed(By.desc("Switch. Mic access. Blocked")) } } - - private fun deviceSupportsSensorToggle(sensor: Int): Boolean { - return sensorPrivacyManager.supportsSensorToggle(sensor) && - sensorPrivacyManager.supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor) - } - - private fun isSensorEnabled(sensor: Int): Boolean { - val isSensorDisabled = - callWithShellPermissionIdentity(OBSERVE_SENSOR_PRIVACY) { - sensorPrivacyManager.isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor) - } - return !isSensorDisabled - } - - private fun setSensorState(sensor: Int, enabled: Boolean) { - val disableSensor = !enabled - // The sensor is enabled iff the privacy control is disabled. - callWithShellPermissionIdentity(MANAGE_SENSOR_PRIVACY, OBSERVE_SENSOR_PRIVACY) { - sensorPrivacyManager.setSensorPrivacy(sensor, disableSensor) - } - } } diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt index f4ed328ae..f76a52256 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterStatusCardTest.kt @@ -17,31 +17,31 @@ package android.safetycenter.functional.ui import android.content.Context +import android.os.Build +import android.safetycenter.SafetySourceData import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.uiautomator.By import com.android.compatibility.common.util.DisableAnimationRule import com.android.compatibility.common.util.FreezeRotationRule -import com.android.safetycenter.resources.SafetyCenterResourcesContext +import com.android.safetycenter.resources.SafetyCenterResourcesApk import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity -import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter import com.android.safetycenter.testing.SafetyCenterTestConfigs import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID import com.android.safetycenter.testing.SafetyCenterTestData import com.android.safetycenter.testing.SafetyCenterTestHelper +import com.android.safetycenter.testing.SafetyCenterTestRule import com.android.safetycenter.testing.SafetySourceIntentHandler.Request import com.android.safetycenter.testing.SafetySourceIntentHandler.Response import com.android.safetycenter.testing.SafetySourceReceiver import com.android.safetycenter.testing.SafetySourceTestData +import com.android.safetycenter.testing.SupportsSafetyCenterRule import com.android.safetycenter.testing.UiTestHelper.RESCAN_BUTTON_LABEL import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed import com.android.safetycenter.testing.UiTestHelper.waitButtonDisplayed import com.android.safetycenter.testing.UiTestHelper.waitButtonNotDisplayed import com.android.safetycenter.testing.UiTestHelper.waitDisplayed import com.android.safetycenter.testing.UiTestHelper.waitNotDisplayed -import org.junit.After -import org.junit.Assume.assumeTrue -import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -50,42 +50,17 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SafetyCenterStatusCardTest { - @get:Rule val disableAnimationRule = DisableAnimationRule() - - @get:Rule val freezeRotationRule = FreezeRotationRule() - private val context: Context = getApplicationContext() - - private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context) + private val safetyCenterResourcesApk = SafetyCenterResourcesApk.forTests(context) private val safetyCenterTestHelper = SafetyCenterTestHelper(context) private val safetySourceTestData = SafetySourceTestData(context) private val safetyCenterTestData = SafetyCenterTestData(context) private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) - // JUnit's Assume is not supported in @BeforeClass by the tests runner, so this is used to - // manually skip the setup and teardown methods. - private val shouldRunTests = context.deviceSupportsSafetyCenter() - - @Before - fun assumeDeviceSupportsSafetyCenterToRunTests() { - assumeTrue(shouldRunTests) - } - - @Before - fun enableSafetyCenterBeforeTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.setup() - } - - @After - fun clearDataAfterTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.reset() - } + @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context) + @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper) + @get:Rule(order = 3) val disableAnimationRule = DisableAnimationRule() + @get:Rule(order = 4) val freezeRotationRule = FreezeRotationRule() @Test fun withUnknownStatus_displaysScanningOnLoad() { @@ -93,8 +68,8 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName("scanning_title"), - safetyCenterResourcesContext.getStringByName("loading_summary") + safetyCenterResourcesApk.getStringByName("scanning_title"), + safetyCenterResourcesApk.getStringByName("loading_summary") ) } } @@ -109,8 +84,8 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesContext.getStringByName("loading_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), + safetyCenterResourcesApk.getStringByName("loading_summary") ) } } @@ -122,12 +97,8 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_ok_review_title" - ), - safetyCenterResourcesContext.getStringByName( - "overall_severity_level_ok_review_summary" - ) + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_title"), + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_review_summary") ) waitButtonDisplayed(RESCAN_BUTTON_LABEL) } @@ -136,6 +107,7 @@ class SafetyCenterStatusCardTest { @Test fun withInformationAndNoIssues_hasRescanButton() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceTestData.information) @@ -143,8 +115,8 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary") ) waitButtonDisplayed(RESCAN_BUTTON_LABEL) } @@ -153,6 +125,7 @@ class SafetyCenterStatusCardTest { @Test fun withInformationAndNoIssues_hasContentDescriptions() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceTestData.information) @@ -167,6 +140,7 @@ class SafetyCenterStatusCardTest { @Test fun withInformationIssue_doesNotHaveRescanButton() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceTestData.informationWithIssue) @@ -174,7 +148,7 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"), + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), safetyCenterTestData.getAlertString(1) ) waitButtonNotDisplayed(RESCAN_BUTTON_LABEL) @@ -184,6 +158,10 @@ class SafetyCenterStatusCardTest { @Test fun withRecommendationIssue_doesNotHaveRescanButton() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + safetyCenterTestHelper.setData( + SINGLE_SOURCE_ID, + safetySourceTestData.recommendationWithGeneralIssue + ) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceTestData.recommendationWithGeneralIssue) @@ -191,7 +169,7 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName( + safetyCenterResourcesApk.getStringByName( "overall_severity_level_safety_recommendation_title" ), safetyCenterTestData.getAlertString(1) @@ -203,6 +181,10 @@ class SafetyCenterStatusCardTest { @Test fun withCriticalWarningIssue_doesNotHaveRescanButton() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + safetyCenterTestHelper.setData( + SINGLE_SOURCE_ID, + safetySourceTestData.criticalWithResolvingGeneralIssue + ) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceTestData.criticalWithResolvingGeneralIssue) @@ -210,7 +192,7 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName( + safetyCenterResourcesApk.getStringByName( "overall_severity_level_critical_safety_warning_title" ), safetyCenterTestData.getAlertString(1) @@ -222,6 +204,7 @@ class SafetyCenterStatusCardTest { @Test fun withKnownStatus_displaysScanningOnRescan() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceTestData.information) @@ -229,15 +212,15 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary") ) waitButtonDisplayed(RESCAN_BUTTON_LABEL) { it.click() } waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName("scanning_title"), - safetyCenterResourcesContext.getStringByName("loading_summary") + safetyCenterResourcesApk.getStringByName("scanning_title"), + safetyCenterResourcesApk.getStringByName("loading_summary") ) } } @@ -245,6 +228,7 @@ class SafetyCenterStatusCardTest { @Test fun rescan_updatesDataAfterScanCompletes() { safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + preSetDataOnT(SINGLE_SOURCE_ID, safetySourceTestData.information) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceTestData.information) @@ -256,18 +240,28 @@ class SafetyCenterStatusCardTest { context.launchSafetyCenterActivity(withReceiverPermission = true) { waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"), - safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_summary") + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"), + safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_summary") ) waitButtonDisplayed(RESCAN_BUTTON_LABEL) { it.click() } waitAllTextDisplayed( - safetyCenterResourcesContext.getStringByName( + safetyCenterResourcesApk.getStringByName( "overall_severity_level_safety_recommendation_title" ), safetyCenterTestData.getAlertString(1) ) } } + + /** + * Sets the given data for the given source ID if this test is running on T builds. This is a + * mitigation for b/301234118 which seems to only fail consistently on T. + */ + private fun preSetDataOnT(sourceId: String, safetySourceData: SafetySourceData) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) { + safetyCenterTestHelper.setData(sourceId, safetySourceData) + } + } } diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt deleted file mode 100644 index eeb512037..000000000 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/ui/SafetyCenterSubpagesTest.kt +++ /dev/null @@ -1,1020 +0,0 @@ -/* - * Copyright (C) 2022 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.safetycenter.functional.ui - -import android.content.Context -import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE -import android.os.Bundle -import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID -import android.safetycenter.SafetySourceData -import android.safetycenter.SafetySourceIssue -import android.safetycenter.config.SafetySource -import android.safetycenter.config.SafetySourcesGroup -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.By -import com.android.compatibility.common.util.DisableAnimationRule -import com.android.compatibility.common.util.FreezeRotationRule -import com.android.compatibility.common.util.RetryRule -import com.android.compatibility.common.util.UiAutomatorUtils2 -import com.android.safetycenter.resources.SafetyCenterResourcesContext -import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG -import com.android.safetycenter.testing.Coroutines.TIMEOUT_SHORT -import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity -import com.android.safetycenter.testing.SafetyCenterActivityLauncher.openPageAndExit -import com.android.safetycenter.testing.SafetyCenterActivityLauncher.openPageAndExitAllowingRetries -import com.android.safetycenter.testing.SafetyCenterFlags -import com.android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter -import com.android.safetycenter.testing.SafetyCenterTestConfigs -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.MULTIPLE_SOURCES_GROUP_ID_1 -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SINGLE_SOURCE_ID -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_1 -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_2 -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_3 -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_4 -import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_5 -import com.android.safetycenter.testing.SafetyCenterTestData -import com.android.safetycenter.testing.SafetyCenterTestHelper -import com.android.safetycenter.testing.SafetySourceIntentHandler.Request -import com.android.safetycenter.testing.SafetySourceIntentHandler.Response -import com.android.safetycenter.testing.SafetySourceReceiver -import com.android.safetycenter.testing.SafetySourceTestData -import com.android.safetycenter.testing.UiTestHelper.MORE_ISSUES_LABEL -import com.android.safetycenter.testing.UiTestHelper.clickConfirmDismissal -import com.android.safetycenter.testing.UiTestHelper.clickDismissIssueCard -import com.android.safetycenter.testing.UiTestHelper.clickMoreIssuesCard -import com.android.safetycenter.testing.UiTestHelper.clickOpenSubpage -import com.android.safetycenter.testing.UiTestHelper.clickSubpageBrandChip -import com.android.safetycenter.testing.UiTestHelper.resetRotation -import com.android.safetycenter.testing.UiTestHelper.rotate -import com.android.safetycenter.testing.UiTestHelper.setAnimationsEnabled -import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitAllTextNotDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitButtonDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitCollapsedIssuesDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitExpandedIssuesDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitGroupShownOnHomepage -import com.android.safetycenter.testing.UiTestHelper.waitPageTitleDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitPageTitleNotDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueDisplayed -import com.android.safetycenter.testing.UiTestHelper.waitSourceIssueNotDisplayed -import java.util.concurrent.TimeUnit -import org.junit.After -import org.junit.Assume.assumeTrue -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.Timeout -import org.junit.runner.RunWith - -/** Functional tests for generic subpages in Safety Center. */ -@RunWith(AndroidJUnit4::class) -@SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") -class SafetyCenterSubpagesTest { - - @get:Rule val disableAnimationRule = DisableAnimationRule() - - @get:Rule val freezeRotationRule = FreezeRotationRule() - - // It is necessery to couple RetryRule and Timeout to ensure that all the retries together are - // restricted with the test timeout - @get:Rule val retryRule = RetryRule(/* retries= */ 3) - @get:Rule - val timeoutRule = - Timeout( - InstrumentationRegistry.getArguments().getString("timeout_msec", "60000").toLong(), - TimeUnit.MILLISECONDS - ) - - private val context: Context = getApplicationContext() - private val safetyCenterTestHelper = SafetyCenterTestHelper(context) - private val safetySourceTestData = SafetySourceTestData(context) - private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) - private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context) - - // JUnit's Assume is not supported in @BeforeClass by the tests runner, so this is used to - // manually skip the setup and teardown methods. - private val shouldRunTests = context.deviceSupportsSafetyCenter() - - @Before - fun assumeDeviceSupportsSafetyCenterToRunTests() { - assumeTrue(shouldRunTests) - } - - @Before - fun enableSafetyCenterBeforeTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.setup() - SafetyCenterFlags.showSubpages = true - } - - @After - fun clearDataAfterTest() { - if (!shouldRunTests) { - return - } - safetyCenterTestHelper.reset() - UiAutomatorUtils2.getUiDevice().resetRotation() - } - - @Test - fun launchSafetyCenter_withSubpagesIntentExtra_showsSubpageTitle() { - val config = safetyCenterTestConfigs.multipleSourceGroupsConfig - val sourceGroup = config.safetySourcesGroups.first()!! - safetyCenterTestHelper.setConfig(config) - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, MULTIPLE_SOURCES_GROUP_ID_1) - - context.launchSafetyCenterActivity(extras) { - waitPageTitleDisplayed(context.getString(sourceGroup.titleResId)) - } - } - - @Test - fun launchSafetyCenter_withSubpagesIntentExtraButFlagDisabled_showsHomepageTitle() { - SafetyCenterFlags.showSubpages = false - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig) - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, MULTIPLE_SOURCES_GROUP_ID_1) - - context.launchSafetyCenterActivity(extras) { waitPageTitleDisplayed("Security & privacy") } - } - - @Test - fun launchSafetyCenter_withNonExistingGroupID_opensHomepageAsFallback() { - val config = safetyCenterTestConfigs.multipleSourceGroupsConfig - val sourceGroup = config.safetySourcesGroups.first()!! - safetyCenterTestHelper.setConfig(config) - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, "non_existing_group_id") - - context.launchSafetyCenterActivity(extras) { - waitPageTitleNotDisplayed(context.getString(sourceGroup.titleResId)) - waitPageTitleDisplayed("Security & privacy") - } - } - - @Test - fun launchSafetyCenter_withMultipleGroups_showsHomepageEntries() { - val sourceTestData = safetySourceTestData.information - with(safetyCenterTestHelper) { - setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig) - setData(SOURCE_ID_1, sourceTestData) - setData(SOURCE_ID_2, sourceTestData) - setData(SOURCE_ID_3, sourceTestData) - setData(SOURCE_ID_4, sourceTestData) - setData(SOURCE_ID_5, sourceTestData) - } - val firstGroup = - safetyCenterTestConfigs.multipleSourceGroupsConfig.safetySourcesGroups.first() - val lastGroup = - safetyCenterTestConfigs.multipleSourceGroupsConfig.safetySourcesGroups.last() - - context.launchSafetyCenterActivity { - waitAllTextDisplayed( - context.getString(firstGroup.titleResId), - context.getString(firstGroup.summaryResId), - context.getString(lastGroup.titleResId), - context.getString(lastGroup.summaryResId) - ) - - openPageAndExit(context.getString(lastGroup.titleResId)) { - waitPageTitleDisplayed(context.getString(lastGroup.titleResId)) - waitAllTextNotDisplayed(context.getString(lastGroup.summaryResId)) - } - } - } - - @Test - fun launchSafetyCenter_withMultipleGroupsButFlagDisabled_showsExpandAndCollapseEntries() { - SafetyCenterFlags.showSubpages = false - val sourceTestData = safetySourceTestData.information - with(safetyCenterTestHelper) { - setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig) - setData(SOURCE_ID_1, sourceTestData) - setData(SOURCE_ID_2, sourceTestData) - setData(SOURCE_ID_3, sourceTestData) - setData(SOURCE_ID_4, sourceTestData) - setData(SOURCE_ID_5, sourceTestData) - } - val firstGroup = - safetyCenterTestConfigs.multipleSourceGroupsConfig.safetySourcesGroups.first() - val lastGroup = - safetyCenterTestConfigs.multipleSourceGroupsConfig.safetySourcesGroups.last() - - context.launchSafetyCenterActivity { - waitAllTextDisplayed( - context.getString(firstGroup.titleResId), - context.getString(firstGroup.summaryResId), - context.getString(lastGroup.titleResId) - ) - waitDisplayed(By.text(context.getString(lastGroup.summaryResId))) { it.click() } - - // Verifying that the group is expanded and sources are displayed - waitAllTextDisplayed(sourceTestData.status!!.title, sourceTestData.status!!.summary) - waitAllTextNotDisplayed(context.getString(lastGroup.summaryResId)) - } - } - - @Test - fun launchSafetyCenter_redirectBackFromSubpage_showsHomepageEntries() { - with(safetyCenterTestHelper) { - setConfig(safetyCenterTestConfigs.multipleSourceGroupsConfig) - setData(SOURCE_ID_1, safetySourceTestData.information) - setData(SOURCE_ID_2, safetySourceTestData.information) - } - val firstGroup = - safetyCenterTestConfigs.multipleSourceGroupsConfig.safetySourcesGroups.first() - - context.launchSafetyCenterActivity { - waitGroupShownOnHomepage(context, firstGroup) - - openPageAndExit(context.getString(firstGroup.titleResId)) { - waitPageTitleDisplayed(context.getString(firstGroup.titleResId)) - waitAllTextNotDisplayed(context.getString(firstGroup.summaryResId)) - } - - waitGroupShownOnHomepage(context, firstGroup) - } - } - - @Test - fun entryListWithMultipleSources_clickingOnHomepageEntry_showsSubpageEntries() { - with(safetyCenterTestHelper) { - setConfig(safetyCenterTestConfigs.multipleSourcesConfig) - setData( - SOURCE_ID_1, - safetySourceTestData.buildSafetySourceDataWithSummary( - severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION, - entryTitle = SAFETY_SOURCE_1_TITLE, - entrySummary = SAFETY_SOURCE_1_SUMMARY - ) - ) - setData( - SOURCE_ID_2, - safetySourceTestData.buildSafetySourceDataWithSummary( - severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION, - entryTitle = SAFETY_SOURCE_2_TITLE, - entrySummary = SAFETY_SOURCE_2_SUMMARY - ) - ) - setData( - SOURCE_ID_3, - safetySourceTestData.buildSafetySourceDataWithSummary( - severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION, - entryTitle = SAFETY_SOURCE_3_TITLE, - entrySummary = SAFETY_SOURCE_3_SUMMARY - ) - ) - } - val firstGroup = safetyCenterTestConfigs.multipleSourcesConfig.safetySourcesGroups[0] - val secondGroup = safetyCenterTestConfigs.multipleSourcesConfig.safetySourcesGroups[1] - - context.launchSafetyCenterActivity { - // Verifying that subpage entries of the first group are displayed - openPageAndExit(context.getString(firstGroup.titleResId)) { - waitAllTextNotDisplayed(context.getString(firstGroup.summaryResId)) - waitAllTextDisplayed( - SAFETY_SOURCE_1_TITLE, - SAFETY_SOURCE_1_SUMMARY, - SAFETY_SOURCE_2_TITLE, - SAFETY_SOURCE_2_SUMMARY - ) - } - - // Verifying that subpage entries of the second group are displayed - openPageAndExit(context.getString(secondGroup.titleResId)) { - waitAllTextNotDisplayed(context.getString(secondGroup.summaryResId)) - waitAllTextDisplayed(SAFETY_SOURCE_3_TITLE, SAFETY_SOURCE_3_SUMMARY) - } - } - } - - @Test - fun entryListWithSingleSource_clickingOnSubpageEntry_redirectsToDifferentScreen() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val source: SafetySource = sourcesGroup.safetySources.first() - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitDisplayed(By.text(context.getString(source.titleResId))) { it.click() } - waitButtonDisplayed("Exit test activity") { it.click() } - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId) - ) - } - } - } - - @Test - fun entryListWithSingleSource_clickingTheInfoIcon_redirectsToDifferentScreen() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - val sourceTestData = safetySourceTestData.informationWithIconAction - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceTestData) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitDisplayed(By.desc("Information")) { it.click() } - waitButtonDisplayed("Exit test activity") { it.click() } - waitAllTextDisplayed(sourceTestData.status!!.title, sourceTestData.status!!.summary) - } - } - } - - @Test - fun entryListWithSingleSource_clickingTheGearIcon_redirectsToDifferentScreen() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - val sourceTestData = safetySourceTestData.informationWithGearIconAction - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceTestData) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitDisplayed(By.desc("Settings")) { it.click() } - waitButtonDisplayed("Exit test activity") { it.click() } - waitAllTextDisplayed(sourceTestData.status!!.title, sourceTestData.status!!.summary) - } - } - } - - @Test - fun entryListWithSingleSource_clickingAnUnclickableDisabledEntry_doesNothing() { - val config = safetyCenterTestConfigs.singleSourceInvalidIntentConfig - safetyCenterTestHelper.setConfig(config) - val sourceTestData = safetySourceTestData.informationWithNullIntent - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceTestData) - val sourcesGroup = config.safetySourcesGroups.first() - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitDisplayed(By.text(sourceTestData.status!!.title.toString())) { it.click() } - - // Verifying that clicking on the entry doesn't redirect to any other screen - waitAllTextDisplayed(sourceTestData.status!!.title, sourceTestData.status!!.summary) - } - } - } - - @Test - fun entryListWithSingleSource_clickingAClickableDisabledEntry_redirectsToDifferentScreen() { - val config = safetyCenterTestConfigs.singleSourceConfig - safetyCenterTestHelper.setConfig(config) - val sourceTestData = safetySourceTestData.unspecifiedDisabledWithTestActivityRedirect - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceTestData) - val sourcesGroup = config.safetySourcesGroups.first() - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitDisplayed(By.text(sourceTestData.status!!.title.toString())) { it.click() } - - waitButtonDisplayed("Exit test activity") { it.click() } - } - } - } - - @Test - fun entryListWithSingleSource_updateSafetySourceData_displayedDataIsUpdated() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val source: SafetySource = sourcesGroup.safetySources.first() - - context.launchSafetyCenterActivity(withReceiverPermission = true) { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId) - ) - } - - SafetySourceReceiver.setResponse( - Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData( - safetySourceTestData.buildSafetySourceDataWithSummary( - severityLevel = SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION, - entryTitle = "Updated title", - entrySummary = "Updated summary" - ) - ) - ) - - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitAllTextNotDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId) - ) - waitAllTextDisplayed("Updated title", "Updated summary") - } - } - } - - @Test - fun entryListWithSingleSource_updateSafetySourceDataAndRotate_displayedDataIsNotUpdated() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val source: SafetySource = sourcesGroup.safetySources.first() - - context.launchSafetyCenterActivity(withReceiverPermission = true) { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId) - ) - - SafetySourceReceiver.setResponse( - Request.Refresh(SINGLE_SOURCE_ID), - Response.SetData( - safetySourceTestData.buildSafetySourceDataWithSummary( - severityLevel = SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION, - entryTitle = "Updated title", - entrySummary = "Updated summary" - ) - ) - ) - UiAutomatorUtils2.getUiDevice().rotate() - - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId) - ) - waitAllTextNotDisplayed("Updated title", "Updated summary") - } - } - } - - @Test - fun issueCard_withMultipleGroups_onlyRelevantSubpageHasIssueCard() { - /* The default attribution title for an issue card is same as the entry group title on the - * homepage. This causes test flakiness as UiAutomator is unable to choose from duplicate - * strings. To address that, an issue with a different attribution title is used here. */ - val sourceData = safetySourceTestData.informationWithIssueWithAttributionTitle - val issue = sourceData.issues[0] - - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) - val firstGroup = safetyCenterTestConfigs.multipleSourcesConfig.safetySourcesGroups[0] - val secondGroup = safetyCenterTestConfigs.multipleSourcesConfig.safetySourcesGroups[1] - safetyCenterTestHelper.setData(SOURCE_ID_3, sourceData) - - context.launchSafetyCenterActivity { - // Verify that homepage has the issue card - waitSourceIssueDisplayed(issue) - - // Verify that irrelevant subpage doesn't have the issue card - openPageAndExit(context.getString(firstGroup.titleResId)) { - waitSourceIssueNotDisplayed(issue) - } - // Verify that relevant subpage has the issue card - openPageAndExit(context.getString(secondGroup.titleResId)) { - waitSourceIssueDisplayed(issue) - } - } - } - - @Test - fun issueCard_updateSafetySourceData_subpageDisplaysUpdatedIssue() { - val initialDataToDisplay = safetySourceTestData.informationWithIssueWithAttributionTitle - val updatedDataToDisplay = safetySourceTestData.criticalWithIssueWithAttributionTitle - - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, initialDataToDisplay) - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitSourceIssueDisplayed(initialDataToDisplay.issues[0]) - - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, updatedDataToDisplay) - - waitSourceIssueDisplayed(updatedDataToDisplay.issues[0]) - } - } - } - - @Test - fun issueCard_resolveIssueOnSubpage_issueDismisses() { - val sourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceData) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val issue = sourceData.issues[0] - val action = issue.actions[0] - - // Clear the data when action is triggered to simulate resolution. - SafetySourceReceiver.setResponse( - Request.ResolveAction(SINGLE_SOURCE_ID), - Response.ClearData - ) - - context.launchSafetyCenterActivity(withReceiverPermission = true) { - openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) { - waitSourceIssueDisplayed(issue) - waitButtonDisplayed(action.label) { it.click() } - - // Wait for success message to go away, verify issue no longer displayed - waitAllTextNotDisplayed(action.successMessage) - waitSourceIssueNotDisplayed(issue) - } - } - } - - @Test - fun issueCard_confirmDismissalOnSubpage_dismissesIssue() { - val sourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceData) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val issue = sourceData.issues[0] - - context.launchSafetyCenterActivity { - openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) { - waitSourceIssueDisplayed(issue) - clickDismissIssueCard() - - waitAllTextDisplayed("Dismiss this alert?") - clickConfirmDismissal() - - waitSourceIssueNotDisplayed(issue) - } - } - } - - @Test - fun issueCard_dismissOnSubpageWithRotation_cancellationPersistsIssue() { - val sourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceData) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val issue = sourceData.issues[0] - - context.launchSafetyCenterActivity { - openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) { - waitSourceIssueDisplayed(issue) - clickDismissIssueCard() - waitAllTextDisplayed("Dismiss this alert?") - - UiAutomatorUtils2.getUiDevice().rotate() - - waitAllTextDisplayed("Dismiss this alert?") - waitButtonDisplayed("Cancel") { it.click() } - waitSourceIssueDisplayed(issue) - } - } - } - - @Test - fun moreIssuesCard_expandOnSubpage_showsAdditionalCard() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) - val sourcesGroup = safetyCenterTestConfigs.multipleSourcesConfig.safetySourcesGroups.first() - val firstSourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - val secondSourceData = safetySourceTestData.informationWithIssueWithAttributionTitle - safetyCenterTestHelper.setData(SOURCE_ID_1, firstSourceData) - safetyCenterTestHelper.setData(SOURCE_ID_2, secondSourceData) - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitCollapsedIssuesDisplayed(firstSourceData.issues[0], secondSourceData.issues[0]) - - clickMoreIssuesCard() - - waitExpandedIssuesDisplayed(firstSourceData.issues[0], secondSourceData.issues[0]) - } - } - } - - @Test - fun dismissedIssuesCard_expandWithOnlyDismissedIssues_showsAdditionalCard() { - val sourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceData) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val issue = sourceData.issues[0] - - context.launchSafetyCenterActivity { - openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) { - waitSourceIssueDisplayed(issue) - clickDismissIssueCard() - waitAllTextDisplayed("Dismiss this alert?") - clickConfirmDismissal() - waitSourceIssueNotDisplayed(issue) - - waitDisplayed(By.text("Dismissed alerts")) { it.click() } - - waitAllTextDisplayed("Dismissed alerts") - waitSourceIssueDisplayed(issue) - } - } - } - - @Test - fun dismissedIssuesCard_collapseWithOnlyDismissedIssues_hidesAdditionalCard() { - val sourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceData) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val issue = sourceData.issues[0] - - context.launchSafetyCenterActivity { - openPageAndExitAllowingRetries(context.getString(sourcesGroup.titleResId)) { - waitSourceIssueDisplayed(issue) - clickDismissIssueCard() - waitAllTextDisplayed("Dismiss this alert?") - clickConfirmDismissal() - waitSourceIssueNotDisplayed(issue) - - waitDisplayed(By.text("Dismissed alerts")) { it.click() } - waitSourceIssueDisplayed(issue) - - waitDisplayed(By.text("Dismissed alerts")) { it.click() } - waitSourceIssueNotDisplayed(issue) - } - } - } - - @Test - fun dismissedIssuesCard_resolveIssue_successConfirmationShown() { - SafetyCenterFlags.hideResolvedIssueUiTransitionDelay = TIMEOUT_LONG - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithIssueWithAttributionTitle - ) - prepareActionResponse(Response.ClearData) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { - // Re-enable animations for this test as this is needed to show the success message. - setAnimationsEnabled(true) - it.click() - } - - // Success message should show up if issue marked as resolved - val successMessage = action.successMessage - waitAllTextDisplayed(successMessage) - } - } - - @Test - fun dismissedIssuesCard_resolveIssue_issueDismisses() { - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithIssueWithAttributionTitle - ) - prepareActionResponse(Response.ClearData) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { it.click() } - - // Wait for success message to go away, verify issue no longer displayed - val successMessage = action.successMessage - waitAllTextNotDisplayed(successMessage) - waitSourceIssueNotDisplayed(issue) - } - } - - @Test - fun dismissedIssuesCard_resolveIssue_withDialogClickYes_resolves() { - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithIssueWithConfirmationWithAttributionTitle - ) - prepareActionResponse(Response.ClearData) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { it.click() } - - waitAllTextDisplayed(SafetySourceTestData.CONFIRMATION_TITLE) - waitButtonDisplayed(SafetySourceTestData.CONFIRMATION_YES) { it.click() } - - waitSourceIssueNotDisplayed(issue) - } - } - - @Test - fun dismissedIssuesCard_resolveIssue_withDialog_rotates_clickYes_resolves() { - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithIssueWithConfirmationWithAttributionTitle - ) - prepareActionResponse(Response.ClearData) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { it.click() } - - waitAllTextDisplayed(SafetySourceTestData.CONFIRMATION_TITLE) - - UiAutomatorUtils2.getUiDevice().rotate() - - waitAllTextDisplayed(SafetySourceTestData.CONFIRMATION_TITLE) - waitButtonDisplayed(SafetySourceTestData.CONFIRMATION_YES) { it.click() } - - waitSourceIssueNotDisplayed(issue) - } - } - - @Test - fun dismissedIssuesCard_resolveIssue_withDialogClicksNo_cancels() { - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithIssueWithConfirmationWithAttributionTitle - ) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { it.click() } - - waitAllTextDisplayed(SafetySourceTestData.CONFIRMATION_TITLE) - waitButtonDisplayed(SafetySourceTestData.CONFIRMATION_NO) { it.click() } - - waitSourceIssueDisplayed(issue) - } - } - - @Test - fun dismissedIssuesCard_resolveIssue_noSuccessMessage_noResolutionUiShown_issueDismisses() { - SafetyCenterFlags.hideResolvedIssueUiTransitionDelay = TIMEOUT_LONG - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithIssueWithAttributionTitle - ) - prepareActionResponse(Response.ClearData) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { - // Re-enable animations for this test as this is needed to show the success message. - setAnimationsEnabled(true) - it.click() - } - - waitSourceIssueNotDisplayed(issue) - } - } - - @Test - fun dismissedIssuesCard_resolvingInflightIssueFailed_issueRemains() { - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithIssueWithAttributionTitle - ) - prepareActionResponse(Response.Error) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { it.click() } - - waitSourceIssueDisplayed(issue) - } - } - - @Test - fun dismissedIssuesCard_resolvingInFlightIssueTimesOut_issueRemains() { - SafetyCenterFlags.resolveActionTimeout = TIMEOUT_SHORT - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithIssueWithAttributionTitle - ) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { it.click() } - - waitSourceIssueDisplayed(issue) - } - } - - @Test - fun dismissedIssuesCard_clickingNonResolvingActionButton_redirectsToDifferentScreen() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - val (sourcesGroup, issue) = - prepareSingleSourceGroupWithIssue( - safetySourceTestData.criticalWithTestActivityRedirectWithAttributionTitle - ) - - checkOnDismissedIssue(sourcesGroup, issue) { - val action = issue.actions[0] - waitButtonDisplayed(action.label) { it.click() } - waitButtonDisplayed("Exit test activity") { it.click() } - } - } - - @Test - fun moreIssuesCard_expandWithDismissedIssues_showsAdditionalCards() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesInSingleGroupConfig) - - val firstSourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - val secondSourceData = safetySourceTestData.informationWithIssueWithAttributionTitle - val thirdSourceData = safetySourceTestData.informationWithIssueWithAttributionTitle - - safetyCenterTestHelper.setData(SOURCE_ID_1, firstSourceData) - safetyCenterTestHelper.setData(SOURCE_ID_2, secondSourceData) - safetyCenterTestHelper.setData(SOURCE_ID_3, thirdSourceData) - - val sourcesGroup = - safetyCenterTestConfigs.multipleSourcesInSingleGroupConfig.safetySourcesGroups.first() - val issue = firstSourceData.issues[0] - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitSourceIssueDisplayed(issue) - clickDismissIssueCard() - waitAllTextDisplayed("Dismiss this alert?") - clickConfirmDismissal() - waitSourceIssueNotDisplayed(issue) - - clickMoreIssuesCard() - - waitAllTextDisplayed(MORE_ISSUES_LABEL) - waitAllTextDisplayed("Dismissed alerts") - waitSourceIssueDisplayed(issue) - } - } - } - - @Test - fun moreIssuesCard_collapseWithDismissedIssues_hidesAdditionalCards() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesInSingleGroupConfig) - - val firstSourceData = safetySourceTestData.criticalWithIssueWithAttributionTitle - val secondSourceData = safetySourceTestData.informationWithIssueWithAttributionTitle - val thirdSourceData = safetySourceTestData.informationWithIssueWithAttributionTitle - - safetyCenterTestHelper.setData(SOURCE_ID_1, firstSourceData) - safetyCenterTestHelper.setData(SOURCE_ID_2, secondSourceData) - safetyCenterTestHelper.setData(SOURCE_ID_3, thirdSourceData) - - val sourcesGroup = - safetyCenterTestConfigs.multipleSourcesInSingleGroupConfig.safetySourcesGroups.first() - val issue = firstSourceData.issues[0] - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitSourceIssueDisplayed(issue) - clickDismissIssueCard() - clickConfirmDismissal() - waitSourceIssueNotDisplayed(issue) - - clickMoreIssuesCard() - waitSourceIssueDisplayed(issue) - - clickMoreIssuesCard() - waitAllTextDisplayed(MORE_ISSUES_LABEL) - waitAllTextNotDisplayed("Dismissed alerts") - waitSourceIssueNotDisplayed(issue) - } - } - } - - @Test - fun brandChip_openSubpageFromHomepage_homepageReopensOnClick() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - - context.launchSafetyCenterActivity { - waitGroupShownOnHomepage(context, sourcesGroup) - - clickOpenSubpage(context, sourcesGroup) - waitPageTitleDisplayed(context.getString(sourcesGroup.titleResId)) - waitAllTextNotDisplayed(context.getString(sourcesGroup.summaryResId)) - - clickSubpageBrandChip() - waitGroupShownOnHomepage(context, sourcesGroup) - } - } - - @Test - fun brandChip_openSubpageFromIntent_homepageOpensOnClick() { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.information) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val extras = Bundle() - extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, sourcesGroup.id) - - context.launchSafetyCenterActivity(extras) { - waitPageTitleDisplayed(context.getString(sourcesGroup.titleResId)) - waitAllTextNotDisplayed(context.getString(sourcesGroup.summaryResId)) - - clickSubpageBrandChip() - waitGroupShownOnHomepage(context, sourcesGroup) - } - } - - @Test - fun settingsSearch_openWithGenericIntentExtra_showsGenericSubpage() { - val config = safetyCenterTestConfigs.multipleSourcesConfig - safetyCenterTestHelper.setConfig(config) - val sourcesGroup = config.safetySourcesGroups.first() - val source = sourcesGroup.safetySources.first() - val preferenceKey = "${source.id}_personal" - val extras = Bundle() - extras.putString(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY, preferenceKey) - - context.launchSafetyCenterActivity(extras) { - waitPageTitleDisplayed(context.getString(sourcesGroup.titleResId)) - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId) - ) - } - } - - @Test - fun settingsSearch_openWithInvalidKey_showsHomepage() { - val config = safetyCenterTestConfigs.singleSourceConfig - val sourcesGroup = config.safetySourcesGroups.first() - safetyCenterTestHelper.setConfig(config) - val extras = Bundle() - extras.putString(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY, "invalid_preference_key") - - context.launchSafetyCenterActivity(extras) { - waitPageTitleDisplayed("Security & privacy") - waitGroupShownOnHomepage(context, sourcesGroup) - } - } - - @Test - fun footerSummary_openGenericSubpageHavingFooter_showsExpectedText() { - val config = safetyCenterTestConfigs.singleSourceConfig - val sourcesGroup = config.safetySourcesGroups.first() - val source: SafetySource = sourcesGroup.safetySources.first() - safetyCenterTestHelper.setConfig(config) - - context.launchSafetyCenterActivity { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitAllTextDisplayed( - context.getString(source.titleResId), - context.getString(source.summaryResId), - safetyCenterResourcesContext.getStringByName( - "test_single_source_group_id_footer" - ) - ) - } - } - } - - private fun prepareSingleSourceGroupWithIssue( - sourceData: SafetySourceData - ): Pair<SafetySourcesGroup, SafetySourceIssue> { - safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) - safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, sourceData) - val sourcesGroup = safetyCenterTestConfigs.singleSourceConfig.safetySourcesGroups.first() - val issue = sourceData.issues[0] - return sourcesGroup to issue - } - - private fun prepareActionResponse(actionResponse: Response) { - SafetySourceReceiver.setResponse(Request.ResolveAction(SINGLE_SOURCE_ID), actionResponse) - } - - private fun checkOnDismissedIssue( - sourcesGroup: SafetySourcesGroup, - issue: SafetySourceIssue, - block: () -> Unit - ) { - val safetyCenterIssueId = SafetyCenterTestData.issueId(SINGLE_SOURCE_ID, issue.id) - safetyCenterTestHelper.dismissSafetyCenterIssue(safetyCenterIssueId) - - context.launchSafetyCenterActivity(withReceiverPermission = true) { - openPageAndExit(context.getString(sourcesGroup.titleResId)) { - waitDisplayed(By.text("Dismissed alerts")) { it.click() } - waitSourceIssueDisplayed(issue) - - block() - } - } - } - - companion object { - private const val SAFETY_SOURCE_1_TITLE = "Safety Source 1 Title" - private const val SAFETY_SOURCE_1_SUMMARY = "Safety Source 1 Summary" - private const val SAFETY_SOURCE_2_TITLE = "Safety Source 2 Title" - private const val SAFETY_SOURCE_2_SUMMARY = "Safety Source 2 Summary" - private const val SAFETY_SOURCE_3_TITLE = "Safety Source 3 Title" - private const val SAFETY_SOURCE_3_SUMMARY = "Safety Source 3 Summary" - private const val EXTRA_SETTINGS_FRAGMENT_ARGS_KEY = ":settings:fragment_args_key" - } -} |