diff options
13 files changed, 247 insertions, 17 deletions
diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml index e4d8f5c54..c018ca094 100644 --- a/PermissionController/res/values-v33/styles.xml +++ b/PermissionController/res/values-v33/styles.xml @@ -596,11 +596,10 @@ <style name="SafetyCenterMoreIssuesCounter" parent="android:Widget.DeviceDefault"> - <item name="android:layout_height">24dp</item> + <item name="android:layout_height">wrap_content</item> <item name="android:layout_width">wrap_content</item> <item name="android:orientation">horizontal</item> - <item name="android:paddingStart">@dimen/sc_spacing_xsmall</item> - <item name="android:paddingEnd">@dimen/sc_spacing_xsmall</item> + <item name="android:paddingHorizontal">@dimen/sc_spacing_xsmall</item> <item name="android:background">@drawable/safety_center_card_widget_background</item> <item name="android:gravity">center_vertical</item> <item name="layout_constraintTop_toTopOf">parent</item> @@ -612,6 +611,7 @@ parent="SafetyCenterBaseTextContainer"> <item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Body</item> <item name="android:lineHeight">@dimen/sc_line_height_medium</item> + <item name="android:paddingVertical">@dimen/sc_spacing_xxxsmall</item> <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_width">wrap_content</item> diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt index 6f146e48c..9feecf5d4 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt @@ -92,7 +92,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() { } val safetyCenterIntent: ParsedSafetyCenterIntent = - requireActivity().getIntent().toSafetyCenterIntent() + requireActivity().intent.toSafetyCenterIntent() val isQsFragment = getArguments()?.getBoolean(QUICK_SETTINGS_SAFETY_CENTER_FRAGMENT, false) ?: false collapsableIssuesCardHelper = @@ -120,7 +120,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() { override fun onStart() { super.onStart() configureInteractionLogger() - safetyCenterViewModel.interactionLogger.record(Action.SAFETY_CENTER_VIEWED) + logSafetyCenterViewedEvent() } override fun onResume() { @@ -162,6 +162,26 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() { abstract fun configureInteractionLogger() + private fun logSafetyCenterViewedEvent() { + // If Safety Center was opened due to an associated notification click (i.e. intent has an + // associated issue), record that issue's metadata on the SAFETY_CENTER_VIEWED event + val maybeIssueKey = requireActivity().intent.toSafetyCenterIntent().safetyCenterIssueKey + val maybeIssue = + maybeIssueKey?.let { + safetyCenterViewModel.getCurrentSafetyCenterDataAsUiData().getMatchingIssue(it) + } + + if (maybeIssue == null) { + safetyCenterViewModel.interactionLogger.record(Action.SAFETY_CENTER_VIEWED) + } else { + safetyCenterViewModel.interactionLogger.recordForIssue( + Action.SAFETY_CENTER_VIEWED, + maybeIssue, + isDismissed = false + ) + } + } + private fun displayErrorDetails(errorDetails: SafetyCenterErrorDetails?) { if (errorDetails == null) return Toast.makeText(requireContext(), errorDetails.errorMessage, Toast.LENGTH_LONG).show() diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt index 239f9a377..69a315f08 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt @@ -16,6 +16,7 @@ package com.android.permissioncontroller.safetycenter.ui.model +import android.os.Build.VERSION_CODES.TIRAMISU import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterEntryGroup @@ -24,12 +25,21 @@ import android.safetycenter.SafetyCenterIssue import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK import androidx.annotation.RequiresApi import com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY +import com.android.safetycenter.internaldata.SafetyCenterIds +import com.android.safetycenter.internaldata.SafetyCenterIssueKey /** UI model representation of Safety Center Data */ data class SafetyCenterUiData( val safetyCenterData: SafetyCenterData, val resolvedIssues: Map<IssueId, ActionId> = emptyMap() ) { + @RequiresApi(TIRAMISU) + fun getMatchingIssue(issueKey: SafetyCenterIssueKey): SafetyCenterIssue? { + return safetyCenterData.issues.find { + SafetyCenterIds.issueIdFromString(it.id).safetyCenterIssueKey == issueKey + } + } + /** Returns the [SafetyCenterEntryGroup] corresponding to the provided ID */ @RequiresApi(UPSIDE_DOWN_CAKE) fun getMatchingGroup(groupId: String): SafetyCenterEntryGroup? { diff --git a/service/java/com/android/safetycenter/SafetyCenterFlags.java b/service/java/com/android/safetycenter/SafetyCenterFlags.java index 67c4d25d6..e51d3a1cf 100644 --- a/service/java/com/android/safetycenter/SafetyCenterFlags.java +++ b/service/java/com/android/safetycenter/SafetyCenterFlags.java @@ -123,6 +123,9 @@ public final class SafetyCenterFlags { private static final String RESURFACE_ISSUE_DELAYS_DEFAULT = ""; private static final Duration RESURFACE_ISSUE_DELAYS_DEFAULT_DURATION = Duration.ofDays(180); + private static final ArraySet<String> sAllowedNotificationSourcesUPlus = + new ArraySet<>(new String[] {"GoogleBackupAndRestore"}); + private static volatile String sUntrackedSourcesDefault = "AndroidAccessibility,AndroidBackgroundLocation," + "AndroidNotificationListener,AndroidPermissionAutoRevoke"; @@ -175,7 +178,10 @@ public final class SafetyCenterFlags { fout.println("FLAGS"); printFlag(fout, PROPERTY_SAFETY_CENTER_ENABLED, getSafetyCenterEnabled()); printFlag(fout, PROPERTY_NOTIFICATIONS_ENABLED, getNotificationsEnabled()); - printFlag(fout, PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES, getNotificationsAllowedSourceIds()); + printFlag( + fout, + PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES, + getNotificationsAllowedSourceIdsFlag()); printFlag(fout, PROPERTY_NOTIFICATIONS_MIN_DELAY, getNotificationsMinDelay()); printFlag( fout, @@ -244,6 +250,20 @@ public final class SafetyCenterFlags { * and therefore this is the only way to enable notifications for sources on Android T. */ public static ArraySet<String> getNotificationsAllowedSourceIds() { + ArraySet<String> sources = getNotificationsAllowedSourceIdsFlag(); + if (SdkLevel.isAtLeastU()) { + // This is a hack to update the flag value via mainline update. Reasons why we can't do + // this via: + // remote flag update - these are generally avoided and considered risky + // XML config - it would break GTS tests for OEMs that have a separate config copy + // default flag value - it would also require a remote flag update + sources.addAll(sAllowedNotificationSourcesUPlus); + } + + return sources; + } + + private static ArraySet<String> getNotificationsAllowedSourceIdsFlag() { return getCommaSeparatedStrings(PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES); } diff --git a/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java b/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java index 8a3151c9f..1ca6cb466 100644 --- a/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java +++ b/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java @@ -106,6 +106,8 @@ final class SafetySourceDataValidator { } if (safetySource.getType() == SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC + && safetySource.getInitialDisplayState() + != SafetySource.INITIAL_DISPLAY_STATE_HIDDEN && safetySourceStatus == null) { throw new IllegalArgumentException( "Missing status for dynamic safety source: " + safetySourceId); 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 d90fbcd1e..ef217a199 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt @@ -3783,6 +3783,17 @@ class SafetyCenterManagerTest { assertThat(lastUpdated[key]).isNotNull() } + @Test + fun setSafetySourceData_dynamicHiddenWithIssueOnlyData_allowed() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.hiddenSourceConfig) + val expectedData = SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue) + + safetyCenterTestHelper.setData(DYNAMIC_HIDDEN_ID, expectedData) + + val actualData = safetyCenterManager.getSafetySourceDataWithPermission(DYNAMIC_HIDDEN_ID) + assertThat(actualData).isEqualTo(expectedData) + } + private fun dumpLastUpdated(): Map<String, String> { val dump = SystemUtil.runShellCommand("dumpsys safety_center data") return dump diff --git a/tests/hostside/safetycenter/helper-app/Android.bp b/tests/hostside/safetycenter/helper-app/Android.bp index cf4372d99..a05f8d2f3 100644 --- a/tests/hostside/safetycenter/helper-app/Android.bp +++ b/tests/hostside/safetycenter/helper-app/Android.bp @@ -30,6 +30,7 @@ android_test_helper_app { static_libs: [ "androidx.test.rules", "androidx.test.ext.junit", + "safety-center-pending-intents", "safety-center-test-util-lib", ], }
\ No newline at end of file diff --git a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt index 784701b8a..0f1356ca4 100644 --- a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt +++ b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt @@ -19,14 +19,21 @@ package android.safetycenter.hostside.device import android.content.Context import android.os.Bundle import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID +import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID +import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity +import com.android.safetycenter.testing.SafetyCenterActivityLauncher.launchSafetyCenterQsActivity import com.android.safetycenter.testing.SafetyCenterActivityLauncher.openPageAndExit 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.SafetyCenterTestHelper import com.android.safetycenter.testing.SafetyCenterTestRule +import com.android.safetycenter.testing.SafetySourceTestData +import com.android.safetycenter.testing.SafetySourceTestData.Companion.INFORMATION_ISSUE_ID +import com.android.safetycenter.testing.UiTestHelper.waitAllTextDisplayed import org.junit.Before import org.junit.Rule import org.junit.Test @@ -47,6 +54,7 @@ class SafetyCenterInteractionLoggingHelperTests { private val context: Context = ApplicationProvider.getApplicationContext() private val safetyCenterTestHelper = SafetyCenterTestHelper(context) private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) + private val safetySourceTestData = SafetySourceTestData(context) @get:Rule val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper) @@ -61,6 +69,34 @@ class SafetyCenterInteractionLoggingHelperTests { } @Test + fun openSafetyCenterFullFromQs() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) + + context.launchSafetyCenterQsActivity { + openPageAndExit("Settings") { waitAllTextDisplayed("OK") } + } + } + + @Test + fun openSafetyCenterWithIssueIntent() { + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) + + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, safetySourceTestData.informationWithIssue) + + val extras = Bundle() + extras.putString(EXTRA_SAFETY_SOURCE_ID, SINGLE_SOURCE_ID) + extras.putString(EXTRA_SAFETY_SOURCE_ISSUE_ID, INFORMATION_ISSUE_ID) + + context.launchSafetyCenterActivity(extras) {} + } + + @Test + fun openSafetyCenterQs() { + context.launchSafetyCenterQsActivity {} + } + + @Test fun openSubpageFromIntentExtra() { val config = safetyCenterTestConfigs.singleSourceConfig safetyCenterTestHelper.setConfig(config) diff --git a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt index 458516379..dc3cb3fc2 100644 --- a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt +++ b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterNotificationLoggingHelperTests.kt @@ -21,12 +21,16 @@ import android.safetycenter.SafetySourceData import android.safetycenter.SafetySourceIssue import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.safetycenter.pendingintents.PendingIntentSender +import com.android.safetycenter.testing.SafetyCenterActivityLauncher 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.SafetyCenterTestHelper import com.android.safetycenter.testing.SafetyCenterTestRule import com.android.safetycenter.testing.SafetySourceTestData +import com.android.safetycenter.testing.StatusBarNotificationWithChannel +import com.android.safetycenter.testing.TestNotificationListener import org.junit.Before import org.junit.Rule import org.junit.Test @@ -49,7 +53,9 @@ class SafetyCenterNotificationLoggingHelperTests { private val safetySourceTestData = SafetySourceTestData(context) private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) - @get:Rule val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper) + @get:Rule + val safetyCenterTestRule = + SafetyCenterTestRule(safetyCenterTestHelper, withNotifications = true) @Before fun setUp() { @@ -64,6 +70,13 @@ class SafetyCenterNotificationLoggingHelperTests { safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, newTestDataWithNotifiableIssue()) } + @Test + fun openSafetyCenterFromNotification() { + safetyCenterTestHelper.setData(SINGLE_SOURCE_ID, newTestDataWithNotifiableIssue()) + + sendContentPendingIntent(TestNotificationListener.waitForSingleNotification()) + } + private fun newTestDataWithNotifiableIssue(): SafetySourceData = safetySourceTestData .defaultCriticalDataBuilder() @@ -74,4 +87,17 @@ class SafetyCenterNotificationLoggingHelperTests { .build() ) .build() + + companion object { + private fun sendContentPendingIntent( + statusBarNotificationWithChannel: StatusBarNotificationWithChannel + ) { + val contentIntent = + statusBarNotificationWithChannel.statusBarNotification.notification.contentIntent + SafetyCenterActivityLauncher.executeBlockAndExit( + launchActivity = { PendingIntentSender.send(contentIntent) }, + block = {} // No action required + ) + } + } } diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt index 5fe9e0a2a..91222d045 100644 --- a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt +++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt @@ -24,10 +24,13 @@ import com.android.compatibility.common.util.ApiLevelUtil import com.android.os.AtomsProto.Atom import com.android.os.AtomsProto.SafetyCenterInteractionReported import com.android.os.AtomsProto.SafetyCenterInteractionReported.Action +import com.android.os.AtomsProto.SafetyCenterInteractionReported.NavigationSource import com.android.os.AtomsProto.SafetyCenterInteractionReported.ViewType import com.android.tradefed.testtype.DeviceJUnit4ClassRunner import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test import com.google.common.truth.Truth.assertThat +import java.math.BigInteger +import java.security.MessageDigest import org.junit.After import org.junit.Assume.assumeTrue import org.junit.Before @@ -67,15 +70,76 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() { val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) - assertThat(safetyCenterViewedAtoms).isNotEmpty() + assertThat(safetyCenterViewedAtoms).hasSize(1) + with(safetyCenterViewedAtoms.first()) { + assertThat(navigationSource).isEqualTo(NavigationSource.SOURCE_UNKNOWN) + assertThat(viewType).isEqualTo(ViewType.FULL) + } } @Test - fun sendNotification_recordsNotificationPostedEvent() { + fun openSafetyCenterQs_recordsSafetyCenterViewedEvent() { + helperAppRule.runTest(TEST_CLASS_NAME, "openSafetyCenterQs") + + val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) + + assertThat(safetyCenterViewedAtoms).hasSize(1) + with(safetyCenterViewedAtoms.first()) { + assertThat(navigationSource).isEqualTo(NavigationSource.QUICK_SETTINGS_TILE) + assertThat(viewType).isEqualTo(ViewType.QUICK_SETTINGS) + } + } + + @Test + fun openSafetyCenterFullFromQs_recordsViewEventWithCorrectSource() { + helperAppRule.runTest(TEST_CLASS_NAME, "openSafetyCenterFullFromQs") + + val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) + + val viewTypesToNavSources = + safetyCenterViewedAtoms.associate { Pair(it.viewType, it.navigationSource) } + assertThat(viewTypesToNavSources) + .containsEntry(ViewType.FULL, NavigationSource.QUICK_SETTINGS_TILE) + } + + @Test + fun openSafetyCenterWithIssueIntent_recordsViewEventWithAssociatedIssueMetadata() { + helperAppRule.runTest(TEST_CLASS_NAME, testMethodName = "openSafetyCenterWithIssueIntent") + + val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) + + assertThat(safetyCenterViewedAtoms).hasSize(1) + with(safetyCenterViewedAtoms.first()) { + assertThat(navigationSource).isEqualTo(NavigationSource.NOTIFICATION) + assertThat(encodedSafetySourceId).isEqualTo(ENCODED_SINGLE_SOURCE_ID) + assertThat(encodedIssueTypeId).isEqualTo(ENCODED_ISSUE_TYPE_ID) + } + } + + @Test + fun openSafetyCenterWithNotification_recordsViewEventWithAssociatedIssueMetadata() { assumeAtLeastUpsideDownCake("Safety Center notification APIs require Android U+") helperAppRule.runTest( testClassName = ".SafetyCenterNotificationLoggingHelperTests", + testMethodName = "openSafetyCenterFromNotification" + ) + + val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) + + assertThat(safetyCenterViewedAtoms).hasSize(1) + with(safetyCenterViewedAtoms.first()) { + assertThat(navigationSource).isEqualTo(NavigationSource.NOTIFICATION) + assertThat(encodedSafetySourceId).isEqualTo(ENCODED_SINGLE_SOURCE_ID) + assertThat(encodedIssueTypeId).isEqualTo(ENCODED_ISSUE_TYPE_ID) + } + } + + @Test + fun sendNotification_recordsNotificationPostedEvent() { + assumeAtLeastUpsideDownCake("Safety Center notification APIs require Android U+") + helperAppRule.runTest( + testClassName = ".SafetyCenterNotificationLoggingHelperTests", testMethodName = "sendNotification" ) @@ -97,8 +161,7 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() { assertThat(safetyCenterViewedAtoms).hasSize(1) with(safetyCenterViewedAtoms.first()) { assertThat(viewType).isEqualTo(ViewType.SUBPAGE) - assertThat(navigationSource) - .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SOURCE_UNKNOWN) + assertThat(navigationSource).isEqualTo(NavigationSource.SOURCE_UNKNOWN) assertThat(sessionId).isNotNull() } } @@ -113,8 +176,7 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() { val subpageViewedEvent = safetyCenterViewedAtoms.find { it.viewType == ViewType.SUBPAGE } assertThat(subpageViewedEvent).isNotNull() - assertThat(subpageViewedEvent!!.navigationSource) - .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SAFETY_CENTER) + assertThat(subpageViewedEvent!!.navigationSource).isEqualTo(NavigationSource.SAFETY_CENTER) assertThat(safetyCenterViewedAtoms.map { it.sessionId }.distinct()).hasSize(1) } @@ -129,8 +191,7 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() { assertThat(safetyCenterViewedAtoms).hasSize(1) with(safetyCenterViewedAtoms.first()) { assertThat(viewType).isEqualTo(ViewType.SUBPAGE) - assertThat(navigationSource) - .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SETTINGS) + assertThat(navigationSource).isEqualTo(NavigationSource.SETTINGS) assertThat(sessionId).isNotNull() } } @@ -148,5 +209,27 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() { private companion object { const val TEST_CLASS_NAME = ".SafetyCenterInteractionLoggingHelperTests" + + // LINT.IfChange(single_source_id) + val ENCODED_SINGLE_SOURCE_ID = encodeId("test_single_source_id") + // LINT.ThenChange(/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt:issue_type_id) + + // LINT.IfChange(issue_type_id) + val ENCODED_ISSUE_TYPE_ID = encodeId("issue_type_id") + // LINT.ThenChange(/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt:issue_type_id) + + /** + * Encodes a string into an long ID. The ID is a SHA-256 of the string, truncated to 64 + * bits. + */ + fun encodeId(id: String?): Long { + if (id == null) return 0 + + val digest = MessageDigest.getInstance("MD5") + digest.update(id.toByteArray()) + + // Truncate to the size of a long + return BigInteger(digest.digest()).toLong() + } } } diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt index edf76e888..fe75a05a2 100644 --- a/tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt +++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/rules/RequireSafetyCenterRule.kt @@ -24,14 +24,23 @@ import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement +/** toBooleanString() doesn't seem available on all Kotlin versions we need to support. */ +private fun String.toBooleanStrictInt(): Boolean = + when (this) { + "true" -> true + "false" -> false + else -> + throw IllegalArgumentException("The string doesn't represent a boolean value: $this") + } + /** JUnit rule for host side tests that requires Safety Center to be supported and enabled. */ class RequireSafetyCenterRule(private val hostTestClass: BaseHostJUnit4Test) : TestRule { private val safetyCenterSupported: Boolean by lazy { - shellCommandStdoutOrThrow("cmd safety_center supported").toBooleanStrict() + shellCommandStdoutOrThrow("cmd safety_center supported").toBooleanStrictInt() } private val safetyCenterEnabled: Boolean by lazy { - shellCommandStdoutOrThrow("cmd safety_center enabled").toBooleanStrict() + shellCommandStdoutOrThrow("cmd safety_center enabled").toBooleanStrictInt() } override fun apply(base: Statement, description: Description): Statement { diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt index fea9b45cc..b0d209fcc 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt @@ -159,6 +159,14 @@ class SafetyCenterTestConfigs(private val context: Context) { /** A [SafetyCenterConfig] with a dynamic source in a different, missing package. */ val singleSourceOtherPackageConfig = singleSourceConfig(dynamicOtherPackageSafetySource) + /** A [SafetyCenterConfig] with a dynamic hidden-by-default source. */ + val hiddenSourceConfig = + singleSourceConfig( + dynamicSafetySourceBuilder(DYNAMIC_HIDDEN_ID) + .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) + .build() + ) + /** A simple [SafetyCenterConfig] with a source supporting all profiles. */ val singleSourceAllProfileConfig = singleSourceConfig( @@ -848,7 +856,9 @@ class SafetyCenterTestConfigs(private val context: Context) { * ID of the only source provided in [singleSourceConfig], [severityZeroConfig] and * [noPageOpenConfig]. */ + // LINT.IfChange(single_source_id) const val SINGLE_SOURCE_ID = "test_single_source_id" + // LINT.ThenChange(/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt:single_source_id) /** ID of the only source provided in [singleSourceAllProfileConfig]. */ const val SINGLE_SOURCE_ALL_PROFILE_ID = "test_single_source_all_profile_id" diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt index 2c4f856bb..66be55fa8 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt @@ -802,7 +802,9 @@ class SafetySourceTestData(private val context: Context) { const val CRITICAL_ISSUE_ACTION_ID = "critical_issue_action_id" /** Issue type ID for all the issues in this file */ + // LINT.IfChange(issue_type_id) const val ISSUE_TYPE_ID = "issue_type_id" + // LINT.ThenChange(/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt:issue_type_id) const val CONFIRMATION_TITLE = "Confirmation title" const val CONFIRMATION_TEXT = "Confirmation text" |