diff options
author | 2022-03-15 08:40:33 +0000 | |
---|---|---|
committer | 2022-03-15 08:40:33 +0000 | |
commit | e409677c8f5a5b63e96d7716d5830ec488eaec34 (patch) | |
tree | b40f4fcad9261a7f78392ac4d1cb6c3b48e72015 | |
parent | 7b3fde52decf298517bf5aaa3fb5e089b7c0caab (diff) | |
parent | a0478e178790f26705ddb4db9c1300f45f9c091a (diff) |
Merge "Disable SafetyCenter APIs if flag is off." into tm-dev
19 files changed, 1038 insertions, 492 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java index 4b947aa26..64c52d1f0 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java @@ -15,7 +15,13 @@ */ package com.android.permissioncontroller.safetycenter.ui; +import static android.content.Intent.FLAG_ACTIVITY_FORWARD_RESULT; + +import android.content.Intent; import android.os.Bundle; +import android.provider.Settings; +import android.safetycenter.SafetyCenterManager; +import android.util.Log; import androidx.annotation.Keep; @@ -28,9 +34,21 @@ import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; @Keep public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity { + private static final String TAG = SafetyCenterActivity.class.getSimpleName(); + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + SafetyCenterManager safetyCenterManager = getSystemService(SafetyCenterManager.class); + + if (safetyCenterManager == null || !safetyCenterManager.isSafetyCenterEnabled()) { + Log.w(TAG, "Safety Center disabled, redirecting to security settings page"); + startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS).addFlags( + FLAG_ACTIVITY_FORWARD_RESULT)); + finish(); + return; + } + setTitle(getString(R.string.safety_center_dashboard_page_title)); getSupportFragmentManager() .beginTransaction() diff --git a/framework-s/java/android/safetycenter/SafetyCenterManager.java b/framework-s/java/android/safetycenter/SafetyCenterManager.java index fece3d543..11fc87403 100644 --- a/framework-s/java/android/safetycenter/SafetyCenterManager.java +++ b/framework-s/java/android/safetycenter/SafetyCenterManager.java @@ -133,7 +133,6 @@ public final class SafetyCenterManager { /** * Used as an {@code String} extra field in {@link #ACTION_REFRESH_SAFETY_SOURCES} intents to * specify a string identifier for the broadcast. - * */ public static final String EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID = "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID"; @@ -225,7 +224,8 @@ public final class SafetyCenterManager { * * @param errorDetails details of an error that should be displayed to the user */ - default void onError(@NonNull SafetyCenterErrorDetails errorDetails) {} + default void onError(@NonNull SafetyCenterErrorDetails errorDetails) { + } } private final Object mListenersLock = new Object(); @@ -249,7 +249,12 @@ public final class SafetyCenterManager { this.mService = service; } - /** Returns whether the Safety Center feature is enabled. */ + /** + * Returns whether the Safety Center feature is enabled. + * + * <p>If this returns {@code false}, all the other methods in this class will no-op and/or + * return default values. + */ @RequiresPermission(anyOf = { READ_SAFETY_CENTER_STATUS, SEND_SAFETY_CENTER_UPDATE @@ -272,13 +277,13 @@ public final class SafetyCenterManager { * <p>This call will rewrite any existing {@link SafetySourceData} already set for the given * {@code safetySourceId} for the calling user. * - * @param safetySourceId the unique identifier for a safety source in the calling user + * @param safetySourceId the unique identifier for a safety source in the calling user * @param safetySourceData the latest safety data for the safety source in the calling user. If - * a safety source does not have any data to set, it can set its - * {@link SafetySourceData} to {@code null}, in which case Safety Center - * will fall back to any placeholder data specified in the safety source - * xml configuration. - * @param safetyEvent the event that triggered the safety source to set safety data + * a safety source does not have any data to set, it can set its + * {@link SafetySourceData} to {@code null}, in which case Safety Center + * will fall back to any placeholder data specified in the safety source + * xml configuration. + * @param safetyEvent the event that triggered the safety source to set safety data */ @RequiresPermission(SEND_SAFETY_CENTER_UPDATE) public void setSafetySourceData( @@ -328,7 +333,7 @@ public final class SafetyCenterManager { * <p>Safety sources should use this API to notify Safety Center when Safety Center requested or * expected them to perform an action or provide data, but they were unable to do so. * - * @param safetySourceId the id of the safety source that provided the issue + * @param safetySourceId the id of the safety source that provided the issue * @param safetySourceErrorDetails details of the error that occurred */ @RequiresPermission(SEND_SAFETY_CENTER_UPDATE) @@ -453,9 +458,10 @@ public final class SafetyCenterManager { /** * Executes the specified Safety Center issue action on the specified Safety Center issue. * - * @param safetyCenterIssueId the target issue ID returned by {@link SafetyCenterIssue#getId()} + * @param safetyCenterIssueId the target issue ID returned by + * {@link SafetyCenterIssue#getId()} * @param safetyCenterIssueActionId the target action ID returned by {@link - * SafetyCenterIssue.Action#getId()} + * SafetyCenterIssue.Action#getId()} */ @RequiresPermission(MANAGE_SAFETY_CENTER) public void executeSafetyCenterIssueAction( diff --git a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java index e119ce6bd..b05246356 100644 --- a/service/java/com/android/safetycenter/SafetyCenterDataTracker.java +++ b/service/java/com/android/safetycenter/SafetyCenterDataTracker.java @@ -91,7 +91,7 @@ final class SafetyCenterDataTracker { @Nullable SafetyCenterData setSafetySourceData( @NonNull String safetySourceId, - @NonNull SafetySourceData safetySourceData, + @Nullable SafetySourceData safetySourceData, @NonNull String packageName, @UserIdInt int userId) { if (!configContains(safetySourceId, packageName)) { @@ -101,7 +101,7 @@ final class SafetyCenterDataTracker { Key key = Key.of(safetySourceId, packageName, userId); SafetySourceData existingSafetySourceData = mSafetySourceDataForKey.get(key); - if (safetySourceData.equals(existingSafetySourceData)) { + if (Objects.equals(safetySourceData, existingSafetySourceData)) { return null; } @@ -152,6 +152,25 @@ final class SafetyCenterDataTracker { return getSafetyCenterData(safetyCenterConfig, userId); } + /** + * Returns a default {@link SafetyCenterData} object to be returned when the API is disabled. + */ + @NonNull + static SafetyCenterData getDefaultSafetyCenterData() { + return new SafetyCenterData( + new SafetyCenterStatus.Builder() + .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN) + .setTitle(getSafetyCenterStatusTitle( + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN)) + .setSummary(getSafetyCenterStatusSummary( + SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN)) + .build(), + emptyList(), + emptyList(), + emptyList() + ); + } + // TODO(b/219702252): Create a more efficient data structure for this, and update it when the // config changes. private boolean configContains( @@ -479,22 +498,6 @@ final class SafetyCenterDataTracker { } } - @NonNull - private static SafetyCenterData getDefaultSafetyCenterData() { - return new SafetyCenterData( - new SafetyCenterStatus.Builder() - .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN) - .setTitle(getSafetyCenterStatusTitle( - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN)) - .setSummary(getSafetyCenterStatusSummary( - SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN)) - .build(), - emptyList(), - emptyList(), - emptyList() - ); - } - @Nullable private static SafetySourceStatus getSafetySourceStatus( @Nullable SafetySourceData safetySourceData) { diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java index 3f6ff7be2..cea0ae404 100644 --- a/service/java/com/android/safetycenter/SafetyCenterService.java +++ b/service/java/com/android/safetycenter/SafetyCenterService.java @@ -45,6 +45,7 @@ import android.safetycenter.SafetySourceErrorDetails; import android.safetycenter.config.SafetyCenterConfig; import android.safetycenter.config.SafetySource; import android.safetycenter.config.SafetySourcesGroup; +import android.util.Log; import androidx.annotation.Keep; import androidx.annotation.RequiresApi; @@ -111,17 +112,7 @@ public final class SafetyCenterService extends SystemService { // TODO(b/214568975): Decide if we should disable safety center if there is a problem // reading the config. - // We don't require the caller to have READ_DEVICE_CONFIG permission. - final long callingId = Binder.clearCallingIdentity(); - try { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_SAFETY_CENTER_ENABLED, - /* defaultValue = */ false) - && getSafetyCenterConfigValue(); - } finally { - Binder.restoreCallingIdentity(callingId); - } + return isApiEnabled(); } @Override @@ -138,6 +129,9 @@ public final class SafetyCenterService extends SystemService { // TODO(b/205706756): Security: check certs? getContext().enforceCallingOrSelfPermission(SEND_SAFETY_CENTER_UPDATE, "setSafetySourceData"); + if (!checkApiEnabled("setSafetySourceData")) { + return; + } // TODO(b/218812582): Validate the SafetySourceData. SafetyCenterData safetyCenterData; @@ -175,6 +169,9 @@ public final class SafetyCenterService extends SystemService { // TODO(b/205706756): Security: check certs? getContext().enforceCallingOrSelfPermission( SEND_SAFETY_CENTER_UPDATE, "getSafetySourceData"); + if (!checkApiEnabled("getSafetySourceData")) { + return null; + } synchronized (mApiLock) { return mSafetyCenterDataTracker.getSafetySourceData(safetySourceId, packageName, @@ -194,6 +191,9 @@ public final class SafetyCenterService extends SystemService { userId, false, "reportSafetySourceError", getContext()); getContext().enforceCallingOrSelfPermission( SEND_SAFETY_CENTER_UPDATE, "reportSafetySourceError"); + if (!checkApiEnabled("reportSafetySourceError")) { + return; + } // TODO(b/218379298): Add implementation } @@ -206,6 +206,9 @@ public final class SafetyCenterService extends SystemService { userId, false, "refreshSafetySources", getContext()); getContext().enforceCallingPermission( MANAGE_SAFETY_CENTER, "refreshSafetySources"); + if (!checkApiEnabled("refreshSafetySources")) { + return; + } // We don't require the caller to have INTERACT_ACROSS_USERS and // START_FOREGROUND_SERVICES_FROM_BACKGROUND permissions. @@ -227,6 +230,9 @@ public final class SafetyCenterService extends SystemService { userId, false, "getSafetyCenterData", getContext()); getContext().enforceCallingOrSelfPermission( MANAGE_SAFETY_CENTER, "getSafetyCenterData"); + if (!checkApiEnabled("getSafetyCenterData")) { + return SafetyCenterDataTracker.getDefaultSafetyCenterData(); + } synchronized (mApiLock) { return mSafetyCenterDataTracker.getSafetyCenterData(userId); @@ -242,6 +248,9 @@ public final class SafetyCenterService extends SystemService { userId, false, "addOnSafetyCenterDataChangedListener", getContext()); getContext().enforceCallingOrSelfPermission( MANAGE_SAFETY_CENTER, "addOnSafetyCenterDataChangedListener"); + if (!checkApiEnabled("addOnSafetyCenterDataChangedListener")) { + return; + } SafetyCenterData safetyCenterData; synchronized (mApiLock) { @@ -263,6 +272,9 @@ public final class SafetyCenterService extends SystemService { userId, false, "removeOnSafetyCenterDataChangedListener", getContext()); getContext().enforceCallingOrSelfPermission( MANAGE_SAFETY_CENTER, "removeOnSafetyCenterDataChangedListener"); + if (!checkApiEnabled("removeOnSafetyCenterDataChangedListener")) { + return; + } synchronized (mApiLock) { mSafetyCenterListeners.removeListener(listener, userId); @@ -276,6 +288,9 @@ public final class SafetyCenterService extends SystemService { userId, false, "dismissSafetyCenterIssue", getContext()); getContext().enforceCallingOrSelfPermission( MANAGE_SAFETY_CENTER, "dismissSafetyCenterIssue"); + if (!checkApiEnabled("dismissSafetyCenterIssue")) { + return; + } // TODO(b/202387059): Implement issue dismissal. } @@ -290,6 +305,9 @@ public final class SafetyCenterService extends SystemService { userId, false, "executeSafetyCenterIssueAction", getContext()); getContext().enforceCallingOrSelfPermission(MANAGE_SAFETY_CENTER, "executeSafetyCenterIssueAction"); + if (!checkApiEnabled("executeSafetyCenterIssueAction")) { + return; + } // TODO(b/218379298): Add implementation } @@ -297,6 +315,9 @@ public final class SafetyCenterService extends SystemService { public void clearAllSafetySourceData() { getContext().enforceCallingOrSelfPermission( MANAGE_SAFETY_CENTER, "clearAllSafetySourceData"); + if (!checkApiEnabled("clearAllSafetySourceData")) { + return; + } synchronized (mApiLock) { mSafetyCenterDataTracker.clear(); @@ -308,6 +329,9 @@ public final class SafetyCenterService extends SystemService { @NonNull SafetyCenterConfig safetyCenterConfig) { getContext().enforceCallingOrSelfPermission(MANAGE_SAFETY_CENTER, "setSafetyCenterConfigOverride"); + if (!checkApiEnabled("setSafetyCenterConfigOverride")) { + return; + } synchronized (mRefreshLock) { // TODO(b/217944317): Implement properly by overriding config in @@ -335,6 +359,9 @@ public final class SafetyCenterService extends SystemService { public void clearSafetyCenterConfigOverride() { getContext().enforceCallingOrSelfPermission( MANAGE_SAFETY_CENTER, "clearSafetyCenterConfigOverride"); + if (!checkApiEnabled("clearSafetyCenterConfigOverride")) { + return; + } synchronized (mRefreshLock) { mSafetyCenterRefreshManager @@ -342,6 +369,23 @@ public final class SafetyCenterService extends SystemService { } } + private boolean isApiEnabled() { + return getSafetyCenterConfigValue() && getDeviceConfigSafetyCenterEnabledProperty(); + } + + private boolean getDeviceConfigSafetyCenterEnabledProperty() { + // This call requires the READ_DEVICE_CONFIG permission. + final long callingId = Binder.clearCallingIdentity(); + try { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_SAFETY_CENTER_ENABLED, + /* defaultValue = */ false); + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + private boolean getSafetyCenterConfigValue() { return getContext().getResources().getBoolean(Resources.getSystem().getIdentifier( "config_enableSafetyCenter", @@ -363,5 +407,13 @@ public final class SafetyCenterService extends SystemService { throw new SecurityException(message + " requires any of: " + Arrays.toString(permissions) + ", but none were granted"); } + + private boolean checkApiEnabled(@NonNull String message) { + if (!isApiEnabled()) { + Log.w(TAG, String.format("Called %s, but Safety Center is disabled", message)); + return false; + } + return true; + } } } diff --git a/tests/cts/safetycenter/AndroidManifest.xml b/tests/cts/safetycenter/AndroidManifest.xml index cfa22f72b..4a784753b 100644 --- a/tests/cts/safetycenter/AndroidManifest.xml +++ b/tests/cts/safetycenter/AndroidManifest.xml @@ -18,13 +18,14 @@ package="android.safetycenter.cts"> <application> - <uses-library android:name="android.test.runner"/> - - <receiver android:name=".SafetySourceBroadcastReceiver" android:exported="false"> + <receiver android:name="android.safetycenter.testing.SafetySourceBroadcastReceiver" + android:exported="false"> <intent-filter> <action android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES"/> </intent-filter> </receiver> + + <uses-library android:name="android.test.runner"/> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:label="CTS tests for SafetyCenter" diff --git a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetyCenterConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetyCenterConfigTest.kt index 4c5723343..c42192ed8 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetyCenterConfigTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetyCenterConfigTest.kt @@ -18,8 +18,8 @@ package android.safetycenter.config.cts import android.os.Build.VERSION_CODES.TIRAMISU import android.safetycenter.config.SafetyCenterConfig -import android.safetycenter.testers.AnyTester -import android.safetycenter.testers.ParcelableTester +import android.safetycenter.testing.AnyTester +import android.safetycenter.testing.ParcelableTester import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.google.common.truth.Truth.assertThat diff --git a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourceTest.kt b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourceTest.kt index f2a96df7d..9970dddf6 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourceTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourceTest.kt @@ -19,8 +19,8 @@ package android.safetycenter.config.cts import android.content.res.Resources import android.os.Build.VERSION_CODES.TIRAMISU import android.safetycenter.config.SafetySource -import android.safetycenter.testers.AnyTester -import android.safetycenter.testers.ParcelableTester +import android.safetycenter.testing.AnyTester +import android.safetycenter.testing.ParcelableTester import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.google.common.truth.Truth.assertThat diff --git a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourcesGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourcesGroupTest.kt index 2e61411bc..09c0de91f 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourcesGroupTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourcesGroupTest.kt @@ -19,8 +19,8 @@ package android.safetycenter.config.cts import android.content.res.Resources import android.os.Build.VERSION_CODES.TIRAMISU import android.safetycenter.config.SafetySourcesGroup -import android.safetycenter.testers.AnyTester -import android.safetycenter.testers.ParcelableTester +import android.safetycenter.testing.AnyTester +import android.safetycenter.testing.ParcelableTester import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.google.common.truth.Truth.assertThat diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt index 0adf45852..57a1c2c71 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt @@ -20,11 +20,15 @@ import android.content.Context import android.content.Intent import android.content.Intent.ACTION_SAFETY_CENTER import android.os.Build.VERSION_CODES.TIRAMISU +import android.safetycenter.testing.SafetyCenterFlags +import android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter import android.support.test.uiautomator.By 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.UiAutomatorUtils.waitFindObject +import org.junit.Assume.assumeTrue +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -33,13 +37,36 @@ import org.junit.runner.RunWith class SafetyCenterActivityTest { private val context: Context = getApplicationContext() + @Before + fun assumeDeviceSupportsSafetyCenterToRunTests() { + assumeTrue(context.deviceSupportsSafetyCenter()) + } + @Test - fun launchActivity_showsSecurityAndPrivacyTitle() { - context.startActivity( - Intent(ACTION_SAFETY_CENTER).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - ) + fun launchActivity_withFlagEnabled_showsSecurityAndPrivacyTitle() { + SafetyCenterFlags.setSafetyCenterEnabled(true) + + startSafetyCenterActivity() // CollapsingToolbar title can't be found by text, so using description instead. waitFindObject(By.desc("Security & Privacy")) } + + @Test + fun launchActivity_withFlagDisabled_showsSecurityTitle() { + SafetyCenterFlags.setSafetyCenterEnabled(false) + + startSafetyCenterActivity() + + // CollapsingToolbar title can't be found by text, so using description instead. + waitFindObject(By.desc("Security")) + } + + private fun startSafetyCenterActivity() { + context.startActivity( + Intent(ACTION_SAFETY_CENTER) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + ) + } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterApisWithShellPermissions.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterApisWithShellPermissions.kt deleted file mode 100644 index c3c7e7266..000000000 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterApisWithShellPermissions.kt +++ /dev/null @@ -1,109 +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.cts - -import android.Manifest.permission.MANAGE_SAFETY_CENTER -import android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE -import android.safetycenter.SafetyCenterData -import android.safetycenter.SafetyCenterManager -import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener -import android.safetycenter.SafetySourceData -import android.safetycenter.SafetyEvent -import android.safetycenter.config.SafetyCenterConfig -import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity -import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity -import java.util.concurrent.Executor - -/** - * Call {@link SafetyCenterManager#setSafetySourceData} adopting Shell's - * {@link SEND_SAFETY_CENTER_UPDATE} permission. - */ -fun SafetyCenterManager.setSafetySourceDataWithPermission( - safetySourceId: String, - safetySourceData: SafetySourceData, - safetyEvent: SafetyEvent -) = - runWithShellPermissionIdentity({ - setSafetySourceData(safetySourceId, safetySourceData, safetyEvent) - }, SEND_SAFETY_CENTER_UPDATE) - -/** - * Call {@link SafetyCenterManager#getSafetySourceData} adopting Shell's - * {@link SEND_SAFETY_CENTER_UPDATE} permission. - */ -fun SafetyCenterManager.getSafetySourceDataWithPermission(id: String): SafetySourceData? = - callWithShellPermissionIdentity({ - getSafetySourceData(id) - }, SEND_SAFETY_CENTER_UPDATE) - -/** - * Call {@link SafetyCenterManager#isSafetyCenterEnabled} adopting Shell's - * {@link SEND_SAFETY_CENTER_UPDATE} permission. - */ -fun SafetyCenterManager.isSafetyCenterEnabledWithPermission(): Boolean = - callWithShellPermissionIdentity({ - isSafetyCenterEnabled - }, SEND_SAFETY_CENTER_UPDATE) - -/** - * Call {@link SafetyCenterManager#refreshSafetySources} adopting Shell's - * {@link MANAGE_SAFETY_CENTER} permission (required for - * {@link SafetyCenterManager#refreshSafetySources}). - */ -fun SafetyCenterManager.refreshSafetySourcesWithPermission(refreshReason: Int) = - runWithShellPermissionIdentity({ - refreshSafetySources(refreshReason) - }, MANAGE_SAFETY_CENTER) - -fun SafetyCenterManager.getSafetyCenterDataWithPermission(): SafetyCenterData = - runWithShellPermissionIdentity(::getSafetyCenterData, MANAGE_SAFETY_CENTER) - -fun SafetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( - executor: Executor, - listener: OnSafetyCenterDataChangedListener -) = - runWithShellPermissionIdentity({ - addOnSafetyCenterDataChangedListener(executor, listener) - }, MANAGE_SAFETY_CENTER) - -fun SafetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission( - listener: OnSafetyCenterDataChangedListener -) = - runWithShellPermissionIdentity({ - removeOnSafetyCenterDataChangedListener(listener) - }, MANAGE_SAFETY_CENTER) - -/** - * Call {@link SafetyCenterManager#clearAllSafetySourceData} adopting Shell's - * {@link MANAGE_SAFETY_CENTER} permission. - */ -fun SafetyCenterManager.clearAllSafetySourceDataWithPermission() = - runWithShellPermissionIdentity({ - clearAllSafetySourceData() - }, MANAGE_SAFETY_CENTER) - -fun SafetyCenterManager.setSafetyCenterConfigOverrideWithPermission( - safetyCenterConfig: SafetyCenterConfig -) = - runWithShellPermissionIdentity({ - setSafetyCenterConfigOverride(safetyCenterConfig) - }, MANAGE_SAFETY_CENTER) - -fun SafetyCenterManager.clearSafetyCenterConfigOverrideWithPermission() = - runWithShellPermissionIdentity({ - clearSafetyCenterConfigOverride() - }, MANAGE_SAFETY_CENTER) diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt index 0f29e9c46..a574a7892 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt @@ -23,29 +23,45 @@ import android.app.PendingIntent.FLAG_IMMUTABLE import android.content.Context import android.content.Intent import android.content.Intent.ACTION_SAFETY_CENTER -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import android.content.res.Resources import android.os.Build.VERSION_CODES.TIRAMISU -import android.provider.DeviceConfig import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterManager +import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN import android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK -import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener -import android.safetycenter.SafetySourceData -import android.safetycenter.SafetySourceIssue +import android.safetycenter.SafetyCenterStatus +import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN import android.safetycenter.SafetyEvent import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED +import android.safetycenter.SafetySourceData +import android.safetycenter.SafetySourceErrorDetails +import android.safetycenter.SafetySourceIssue import android.safetycenter.SafetySourceIssue.SEVERITY_LEVEL_CRITICAL_WARNING import android.safetycenter.SafetySourceStatus import android.safetycenter.SafetySourceStatus.STATUS_LEVEL_CRITICAL_WARNING +import android.safetycenter.SafetySourceStatus.STATUS_LEVEL_NONE +import android.safetycenter.SafetySourceStatus.STATUS_LEVEL_OK import android.safetycenter.config.SafetyCenterConfig import android.safetycenter.config.SafetySource +import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC import android.safetycenter.config.SafetySourcesGroup +import android.safetycenter.testing.SafetyCenterFlags +import android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter +import android.safetycenter.testing.SafetySourceBroadcastReceiver +import android.safetycenter.testing.addOnSafetyCenterDataChangedListenerWithPermission +import android.safetycenter.testing.clearAllSafetySourceDataWithPermission +import android.safetycenter.testing.clearSafetyCenterConfigOverrideWithPermission +import android.safetycenter.testing.getSafetyCenterDataWithPermission +import android.safetycenter.testing.getSafetySourceDataWithPermission +import android.safetycenter.testing.isSafetyCenterEnabledWithPermission +import android.safetycenter.testing.refreshSafetySourcesWithPermission +import android.safetycenter.testing.removeOnSafetyCenterDataChangedListenerWithPermission +import android.safetycenter.testing.setSafetyCenterConfigOverrideWithPermission +import android.safetycenter.testing.setSafetySourceDataWithPermission 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.SystemUtil.runWithShellPermissionIdentity +import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity import com.google.common.truth.Truth.assertThat import com.google.common.util.concurrent.MoreExecutors.directExecutor import kotlinx.coroutines.TimeoutCancellationException @@ -53,6 +69,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout import org.junit.After +import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -64,293 +81,372 @@ import kotlin.test.assertFailsWith class SafetyCenterManagerTest { private val context: Context = getApplicationContext() private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!! - private val somePendingIntent = PendingIntent.getActivity( - context, 0 /* requestCode */, - Intent(ACTION_SAFETY_CENTER).addFlags(FLAG_ACTIVITY_NEW_TASK), - FLAG_IMMUTABLE - ) - private val safetySourceDataOnPageOpen = SafetySourceData.Builder() - .setStatus( - SafetySourceStatus.Builder( - "safetySourceDataOnPageOpen status title", - "safetySourceDataOnPageOpen status summary", - SafetySourceStatus.STATUS_LEVEL_NONE, - somePendingIntent - ) - .build() + private val somePendingIntent = + PendingIntent.getActivity( + context, + 0 /* requestCode */, + Intent(ACTION_SAFETY_CENTER), + FLAG_IMMUTABLE ) - .build() - private val safetySourceDataOnRescanClick = SafetySourceData.Builder() - .setStatus( - SafetySourceStatus.Builder( - "safetySourceDataOnRescanClick status title", - "safetySourceDataOnRescanClick status summary", - SafetySourceStatus.STATUS_LEVEL_RECOMMENDATION, - somePendingIntent + private val safetySourceDataNone = + SafetySourceData.Builder() + .setStatus( + SafetySourceStatus.Builder( + "None title", + "None summary", + STATUS_LEVEL_NONE, + somePendingIntent + ) + .build() ) - .build() - ).build() + .build() + private val safetySourceDataOk = + SafetySourceData.Builder() + .setStatus( + SafetySourceStatus.Builder("Ok title", "Ok summary", STATUS_LEVEL_OK, + somePendingIntent) + .build() + ) + .build() + private val safetySourceDataCritical = + SafetySourceData.Builder() + .setStatus( + SafetySourceStatus.Builder( + "Critical title", + "Critical summary", + STATUS_LEVEL_CRITICAL_WARNING, + somePendingIntent + ) + .build() + ) + .addIssue( + SafetySourceIssue.Builder( + "critical_issue_id", + "Critical issue title", + "Critical issue summary", + SEVERITY_LEVEL_CRITICAL_WARNING, + "issue_type_id" + ) + .addAction( + SafetySourceIssue.Action.Builder("critical_action_id", "Solve issue", + somePendingIntent) + .build() + ) + .build() + ) + .build() private val listenerChannel = Channel<SafetyCenterData>() + // The lambda has to be wrapped to the right type because kotlin wraps lambdas in a new Java // functional interface implementation each time they are referenced/cast to a Java interface: // b/215569072. private val listener = OnSafetyCenterDataChangedListener { - runBlockingWithTimeout { - listenerChannel.send(it) - } + runBlockingWithTimeout { listenerChannel.send(it) } + } + + @Before + fun assumeDeviceSupportsSafetyCenterToRunTests() { + assumeTrue(context.deviceSupportsSafetyCenter()) } @Before @After fun clearDataBetweenTest() { + SafetyCenterFlags.setSafetyCenterEnabled(true) safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener) safetyCenterManager.clearAllSafetySourceDataWithPermission() + safetyCenterManager.clearSafetyCenterConfigOverrideWithPermission() SafetySourceBroadcastReceiver.reset() } - @Before - fun setSafetyCenterConfigOverrideBeforeTest() { - safetyCenterManager.clearSafetyCenterConfigOverrideWithPermission() - // TODO(b/217944317): When the test API impl is finalized to override XML config, only - // override config in select test cases that require it. This is to ensure that we do have - // some test cases running with the XML config. - safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_ONLY_CONFIG) + @After + fun cancelChannelAfterTest() { + listenerChannel.cancel() } - @After - fun clearSafetyCenterConfigOverrideAfterTest() { - safetyCenterManager.clearSafetyCenterConfigOverrideWithPermission() + @Test + fun isSafetyCenterEnabled_withFlagEnabled_returnsTrue() { + val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission() + + assertThat(isSafetyCenterEnabled).isTrue() } @Test - fun getSafetySourceData_notSet_returnsNull() { - val safetySourceData = - safetyCenterManager.getSafetySourceDataWithPermission("some_unknown_id") + fun isSafetyCenterEnabled_withFlagDisabled_returnsFalse() { + SafetyCenterFlags.setSafetyCenterEnabled(false) + + val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission() - assertThat(safetySourceData).isNull() + assertThat(isSafetyCenterEnabled).isFalse() + } + + @Test + fun isSafetyCenterEnabled_withoutPermission_throwsSecurityException() { + assertFailsWith(SecurityException::class) { safetyCenterManager.isSafetyCenterEnabled } } @Test - fun setSafetySourceData_getSafetySourceDataReturnsNewValue() { - val safetySourceData = SafetySourceData.Builder().build() + fun setSafetySourceData_validId_setsValue() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + safetyCenterManager.setSafetySourceDataWithPermission( - CTS_SOURCE_ID, - safetySourceData, - EVENT_SOURCE_STATE_CHANGED + CTS_SOURCE_ID, + safetySourceDataNone, + EVENT_SOURCE_STATE_CHANGED ) - val safetySourceDataResult = - safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) - - assertThat(safetySourceDataResult).isEqualTo(safetySourceData) + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) + assertThat(apiSafetySourceData).isEqualTo(safetySourceDataNone) } @Test - fun setSafetySourceData_withSameId_replacesValue() { - val firstSafetySourceData = SafetySourceData.Builder().build() + fun setSafetySourceData_twice_replacesValue() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) safetyCenterManager.setSafetySourceDataWithPermission( - CTS_SOURCE_ID, - firstSafetySourceData, - EVENT_SOURCE_STATE_CHANGED + CTS_SOURCE_ID, + safetySourceDataNone, + EVENT_SOURCE_STATE_CHANGED ) - val secondSafetySourceData = SafetySourceData.Builder().setStatus( - SafetySourceStatus.Builder( - "Status title", "Summary of the status", STATUS_LEVEL_CRITICAL_WARNING, - somePendingIntent - ).build() - ).addIssue( - SafetySourceIssue.Builder( - "Issue id", "Issue title", "Summary of the issue", - SEVERITY_LEVEL_CRITICAL_WARNING, - "issue_type_id" - ) - .addAction( - SafetySourceIssue.Action.Builder( - "action_id", - "Solve issue", - somePendingIntent - ).build() - ).build() - ).build() safetyCenterManager.setSafetySourceDataWithPermission( - CTS_SOURCE_ID, - secondSafetySourceData, - EVENT_SOURCE_STATE_CHANGED + CTS_SOURCE_ID, + safetySourceDataCritical, + EVENT_SOURCE_STATE_CHANGED ) - val safetySourceData = safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) - - assertThat(safetySourceData).isEqualTo(secondSafetySourceData) + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) + assertThat(apiSafetySourceData).isEqualTo(safetySourceDataCritical) } @Test - fun isSafetyCenterEnabled_whenConfigEnabled_andFlagEnabled_returnsTrue() { - if (!deviceSupportsSafetyCenter()) { - return - } - - runWithShellPermissionIdentity { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_SAFETY_CENTER_ENABLED, - /* value = */ true.toString(), - /* makeDefault = */ false - ) - } + fun setSafetySourceData_null_clearsValue() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataNone, + EVENT_SOURCE_STATE_CHANGED + ) - val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission() + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceData = null, + EVENT_SOURCE_STATE_CHANGED + ) - assertThat(isSafetyCenterEnabled).isTrue() + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) + assertThat(apiSafetySourceData).isNull() } @Test - fun isSafetyCenterEnabled_whenConfigEnabled_andFlagDisabled_returnsFalse() { - if (!deviceSupportsSafetyCenter()) { - return - } + fun setSafetySourceData_withFlagDisabled_noOp() { + SafetyCenterFlags.setSafetyCenterEnabled(false) - runWithShellPermissionIdentity { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_SAFETY_CENTER_ENABLED, - /* value = */ false.toString(), - /* makeDefault = */ false - ) - } - - val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission() + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataNone, + EVENT_SOURCE_STATE_CHANGED + ) - assertThat(isSafetyCenterEnabled).isFalse() + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) + assertThat(apiSafetySourceData).isNull() } @Test - fun isSafetyCenterEnabled_whenConfigDisabled_andFlagEnabled_returnsFalse() { - if (deviceSupportsSafetyCenter()) { - return - } - - runWithShellPermissionIdentity { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_SAFETY_CENTER_ENABLED, - /* value = */ true.toString(), - /* makeDefault = */ false + fun setSafetySourceData_withoutPermission_throwsSecurityException() { + assertFailsWith(SecurityException::class) { + safetyCenterManager.setSafetySourceData( + CTS_SOURCE_ID, + safetySourceDataNone, + EVENT_SOURCE_STATE_CHANGED ) } - - val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission() - - assertThat(isSafetyCenterEnabled).isFalse() } @Test - fun isSafetyCenterEnabled_whenConfigDisabled_andFlagDisabled_returnsFalse() { - if (deviceSupportsSafetyCenter()) { - return - } - - runWithShellPermissionIdentity { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_SAFETY_CENTER_ENABLED, - /* value = */ false.toString(), - /* makeDefault = */ false - ) - } + fun getSafetySourceData_validId_noData_returnsNull() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) - val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission() + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) - assertThat(isSafetyCenterEnabled).isFalse() + assertThat(apiSafetySourceData).isNull() } @Test - fun isSafetyCenterEnabled_whenAppDoesNotHoldPermission_methodThrows() { + fun getSafetySourceData_withoutPermission_throwsSecurityException() { assertFailsWith(SecurityException::class) { - safetyCenterManager.isSafetyCenterEnabled + safetyCenterManager.getSafetySourceData(CTS_SOURCE_ID) } } @Test - fun refreshSafetySources_withoutManageSafetyCenterPermission_throwsSecurityException() { + fun reportSafetySourceError_withoutPermission_throwsSecurityException() { assertFailsWith(SecurityException::class) { - safetyCenterManager.refreshSafetySources(REFRESH_REASON_RESCAN_BUTTON_CLICK) + safetyCenterManager.reportSafetySourceError( + CTS_SOURCE_ID, + SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED) + ) } } @Test - fun refreshSafetySources_whenReceiverDoesNotHaveSendingPermission_sourceDoesNotSendData() { + fun refreshSafetySources_withRefreshReasonRescanButtonClick_sourceSendsRescanData() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID - SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataOnRescanClick + SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataCritical - safetyCenterManager.refreshSafetySourcesWithPermission( + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( REFRESH_REASON_RESCAN_BUTTON_CLICK ) - assertFailsWith(TimeoutCancellationException::class) { - SafetySourceBroadcastReceiver.waitTillOnReceiveComplete(TIMEOUT_SHORT) - } - val safetySourceData = + val apiSafetySourceData = safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) - assertThat(safetySourceData).isNull() + assertThat(apiSafetySourceData).isEqualTo(safetySourceDataCritical) } @Test - fun refreshSafetySources_withRefreshReasonRescanButtonClick_sourceSendsRescanData() { + fun refreshSafetySources_withRefreshReasonPageOpen_sourceSendsPageOpenData() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID - SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataOnRescanClick + SafetySourceBroadcastReceiver.safetySourceDataOnPageOpen = safetySourceDataOk safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( - REFRESH_REASON_RESCAN_BUTTON_CLICK - ) + REFRESH_REASON_PAGE_OPEN) - val safetySourceData = safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) - assertThat(safetySourceData).isEqualTo(safetySourceDataOnRescanClick) + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) + assertThat(apiSafetySourceData).isEqualTo(safetySourceDataOk) } @Test - fun refreshSafetySources_withRefreshReasonPageOpen_sourceSendsPageOpenData() { + fun refreshSafetySources_whenReceiverDoesNotHaveSendingPermission_sourceDoesNotSendData() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID - SafetySourceBroadcastReceiver.safetySourceDataOnPageOpen = safetySourceDataOnPageOpen + SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataCritical - safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( - REFRESH_REASON_PAGE_OPEN - ) + safetyCenterManager.refreshSafetySourcesWithPermission(REFRESH_REASON_RESCAN_BUTTON_CLICK) - val safetySourceData = safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) - assertThat(safetySourceData).isEqualTo(safetySourceDataOnPageOpen) + assertFailsWith(TimeoutCancellationException::class) { + SafetySourceBroadcastReceiver.waitTillOnReceiveComplete(TIMEOUT_SHORT) + } + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) + assertThat(apiSafetySourceData).isNull() } @Test - fun refreshSafetySources_withInvalidRefreshSeason_throwsIllegalArgumentException() { + fun refreshSafetySources_withFlagDisabled_noOp() { + SafetyCenterFlags.setSafetyCenterEnabled(false) + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID - SafetySourceBroadcastReceiver.safetySourceDataOnPageOpen = safetySourceDataOnPageOpen - SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataOnRescanClick + SafetySourceBroadcastReceiver.safetySourceDataOnPageOpen = safetySourceDataOk - assertFailsWith(IllegalArgumentException::class) { - safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(500) + assertFailsWith(TimeoutCancellationException::class) { + safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( + REFRESH_REASON_PAGE_OPEN, TIMEOUT_SHORT) } + val apiSafetySourceData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) + assertThat(apiSafetySourceData).isNull() } @Test - fun getSafetyCenterData_returnsSafetyCenterData() { - val safetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() - - // TODO(b/218830137): Assert on content. - assertThat(safetyCenterData).isNotNull() + fun refreshSafetySources_withInvalidRefreshSeason_throwsIllegalArgumentException() { + val thrown = + assertFailsWith(IllegalArgumentException::class) { + safetyCenterManager.refreshSafetySourcesWithPermission(500) + } + assertThat(thrown).hasMessageThat().isEqualTo("Invalid refresh reason: 500") } @Test - fun getSafetyCenterData_whenAppDoesNotHoldPermission_methodThrows() { + fun refreshSafetySources_withoutPermission_throwsSecurityException() { assertFailsWith(SecurityException::class) { - safetyCenterManager.safetyCenterData + safetyCenterManager.refreshSafetySources(REFRESH_REASON_RESCAN_BUTTON_CLICK) } } @Test - fun addOnSafetyCenterDataChangedListener_listenerCalledWithSafetyCenterData() { + fun getSafetyCenterData_withoutDataProvided_returnsDataFromConfig() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + // TODO(b/218830137): Assert on content. + assertThat(apiSafetyCenterData).isNotNull() + } + + @Test + fun getSafetyCenterData_withSomeDataProvided_returnsDataProvided() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataNone, + EVENT_SOURCE_STATE_CHANGED + ) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + // TODO(b/218830137): Assert on content. + assertThat(apiSafetyCenterData).isNotNull() + } + + @Test + fun getSafetyCenterData_withUpdatedData_returnsUpdatedData() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataOk, + EVENT_SOURCE_STATE_CHANGED + ) + val previousApiSafetyCenterData = + safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID) + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataCritical, + EVENT_SOURCE_STATE_CHANGED + ) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + // TODO(b/218830137): Assert on content. + assertThat(apiSafetyCenterData).isNotEqualTo(previousApiSafetyCenterData) + } + + @Test + fun getSafetyCenterData_withFlagDisabled_returnsDefaultData() { + SafetyCenterFlags.setSafetyCenterEnabled(false) + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + + val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() + + assertThat(apiSafetyCenterData).isEqualTo(SafetyCenterData( + SafetyCenterStatus.Builder() + .setSeverityLevel(OVERALL_SEVERITY_LEVEL_UNKNOWN) + .setTitle("Unknown") + .setSummary("Unknown safety status") + .build(), + emptyList(), + emptyList(), + emptyList() + )) + } + + @Test + fun getSafetyCenterData_withoutPermission_throwsSecurityException() { + assertFailsWith(SecurityException::class) { safetyCenterManager.safetyCenterData } + } + + @Test + fun addOnSafetyCenterDataChangedListener_listenerCalledWithSafetyCenterDataFromConfig() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( - directExecutor(), listener + directExecutor(), + listener ) val safetyCenterDataFromListener = receiveListenerUpdate() @@ -360,17 +456,18 @@ class SafetyCenterManagerTest { @Test fun addOnSafetyCenterDataChangedListener_listenerCalledOnSafetySourceData() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( - directExecutor(), listener + directExecutor(), + listener ) // Receive initial data. receiveListenerUpdate() - val safetySourceData = SafetySourceData.Builder().build() safetyCenterManager.setSafetySourceDataWithPermission( - CTS_SOURCE_ID, - safetySourceData, - EVENT_SOURCE_STATE_CHANGED + CTS_SOURCE_ID, + safetySourceDataOk, + EVENT_SOURCE_STATE_CHANGED ) val safetyCenterDataFromListener = receiveListenerUpdate() @@ -379,19 +476,75 @@ class SafetyCenterManagerTest { } @Test - fun removeOnSafetyCenterDataChangedListener_listenerNotCalledOnSafetySourceData() { + fun addOnSafetyCenterDataChangedListener_listenerCalledWhenSafetySourceDataChanges() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( - directExecutor(), listener + directExecutor(), + listener ) // Receive initial data. receiveListenerUpdate() + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataOk, + EVENT_SOURCE_STATE_CHANGED + ) + // Receive update from #setSafetySourceData call. + receiveListenerUpdate() - safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener) - val safetySourceData = SafetySourceData.Builder().build() safetyCenterManager.setSafetySourceDataWithPermission( - CTS_SOURCE_ID, - safetySourceData, - EVENT_SOURCE_STATE_CHANGED + CTS_SOURCE_ID, + safetySourceDataCritical, + EVENT_SOURCE_STATE_CHANGED + ) + val safetyCenterDataFromListener = receiveListenerUpdate() + + // TODO(b/218830137): Assert on content. + assertThat(safetyCenterDataFromListener).isNotNull() + } + + @Test + fun addOnSafetyCenterDataChangedListener_listenerCalledWhenSafetySourceDataCleared() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( + directExecutor(), + listener + ) + // Receive initial data. + receiveListenerUpdate() + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataOk, + EVENT_SOURCE_STATE_CHANGED + ) + // Receive update from #setSafetySourceData call. + receiveListenerUpdate() + + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceData = null, + EVENT_SOURCE_STATE_CHANGED + ) + val safetyCenterDataFromListener = receiveListenerUpdate() + + // TODO(b/218830137): Assert on content. + assertThat(safetyCenterDataFromListener).isNotNull() + } + + @Test + fun addOnSafetyCenterDataChangedListener_listenerNotCalledWhenSafetySourceDataStaysNull() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( + directExecutor(), + listener + ) + // Receive initial data. + receiveListenerUpdate() + + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceData = null, + EVENT_SOURCE_STATE_CHANGED ) assertFailsWith(TimeoutCancellationException::class) { @@ -400,18 +553,102 @@ class SafetyCenterManagerTest { } @Test - fun addOnSafetyCenterDataChangedListener_whenAppDoesNotHoldPermission_methodThrows() { + fun addOnSafetyCenterDataChangedListener_listenerNotCalledWhenSafetySourceDataDoesntChange() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( + directExecutor(), + listener + ) + // Receive initial data. + receiveListenerUpdate() + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataOk, + EVENT_SOURCE_STATE_CHANGED + ) + // Receive update from #setSafetySourceData call. + receiveListenerUpdate() + + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataOk, + EVENT_SOURCE_STATE_CHANGED + ) + + assertFailsWith(TimeoutCancellationException::class) { + receiveListenerUpdate(TIMEOUT_SHORT) + } + } + + @Test + fun addOnSafetyCenterDataChangedListener_withFlagDisabled_listenerNotCalled() { + SafetyCenterFlags.setSafetyCenterEnabled(false) + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + + safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( + directExecutor(), + listener + ) + + assertFailsWith(TimeoutCancellationException::class) { + receiveListenerUpdate(TIMEOUT_SHORT) + } + } + + @Test + fun addOnSafetyCenterDataChangedListener_withoutPermission_throwsSecurityException() { assertFailsWith(SecurityException::class) { - safetyCenterManager.addOnSafetyCenterDataChangedListener( - directExecutor(), listener - ) + safetyCenterManager.addOnSafetyCenterDataChangedListener(directExecutor(), listener) + } + } + + @Test + fun addOnSafetyCenterDataChangedListener_oneShot_doesntDeadlock() { + val oneShotListener = + object : OnSafetyCenterDataChangedListener { + override fun onSafetyCenterDataChanged(safetyCenterData: SafetyCenterData) { + safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(this) + runBlockingWithTimeout { listenerChannel.send(safetyCenterData) } + } + } + safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( + directExecutor(), + oneShotListener + ) + + // Check that we don't deadlock when using a one-shot listener: this is because adding the + // listener could call the listener while holding a lock on the binder thread-pool; causing + // a deadlock when attempting to call the `SafetyCenterManager` from that listener. + receiveListenerUpdate() + } + + @Test + fun removeOnSafetyCenterDataChangedListener_listenerNotCalledOnSafetySourceData() { + safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_CONFIG) + safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( + directExecutor(), + listener + ) + // Receive initial data. + receiveListenerUpdate() + + safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener) + safetyCenterManager.setSafetySourceDataWithPermission( + CTS_SOURCE_ID, + safetySourceDataOk, + EVENT_SOURCE_STATE_CHANGED + ) + + assertFailsWith(TimeoutCancellationException::class) { + receiveListenerUpdate(TIMEOUT_SHORT) } } @Test - fun removeOnSafetyCenterDataChangedListener_whenAppDoesNotHoldPermission_methodThrows() { + fun removeOnSafetyCenterDataChangedListener_withoutPermission_throwsSecurityException() { safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( - directExecutor(), listener + directExecutor(), + listener ) assertFailsWith(SecurityException::class) { @@ -420,79 +657,94 @@ class SafetyCenterManagerTest { } @Test - fun dismissSafetyCenterIssue_whenAppDoesNotHoldPermission_methodThrows() { + fun dismissSafetyCenterIssue_withoutPermission_throwsSecurityException() { assertFailsWith(SecurityException::class) { safetyCenterManager.dismissSafetyCenterIssue("bleh") } } - private fun deviceSupportsSafetyCenter() = - context.resources.getBoolean( - Resources.getSystem().getIdentifier( - "config_enableSafetyCenter", - "bool", - "android" - ) - ) + @Test + fun executeSafetyCenterIssueAction_withoutPermission_throwsSecurityException() { + assertFailsWith(SecurityException::class) { + safetyCenterManager.executeSafetyCenterIssueAction("bleh", "blah") + } + } + + @Test + fun clearAllSafetySourceData_withoutPermission_throwsSecurityException() { + assertFailsWith(SecurityException::class) { safetyCenterManager.clearAllSafetySourceData() } + } + + @Test + fun setSafetyCenterConfigOverride_withoutPermission_throwsSecurityException() { + assertFailsWith(SecurityException::class) { + safetyCenterManager.setSafetyCenterConfigOverride(CTS_CONFIG) + } + } + + @Test + fun clearSafetyCenterConfigOverride_withoutPermission_throwsSecurityException() { + assertFailsWith(SecurityException::class) { + safetyCenterManager.clearSafetyCenterConfigOverride() + } + } private fun SafetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( - refreshReason: Int + refreshReason: Int, + timeout: Duration = TIMEOUT_LONG ) { - try { - runWithShellPermissionIdentity({ + callWithShellPermissionIdentity( + { refreshSafetySources(refreshReason) - SafetySourceBroadcastReceiver.waitTillOnReceiveComplete(TIMEOUT_LONG) - }, SEND_SAFETY_CENTER_UPDATE, MANAGE_SAFETY_CENTER) - } catch (e: RuntimeException) { - throw e.cause ?: e - } + SafetySourceBroadcastReceiver.waitTillOnReceiveComplete(timeout) + }, + SEND_SAFETY_CENTER_UPDATE, + MANAGE_SAFETY_CENTER + ) } private fun receiveListenerUpdate(timeout: Duration = TIMEOUT_LONG): SafetyCenterData = - runBlockingWithTimeout(timeout) { - listenerChannel.receive() - } + runBlockingWithTimeout(timeout) { listenerChannel.receive() } private fun <T> runBlockingWithTimeout( timeout: Duration = TIMEOUT_LONG, block: suspend () -> T ) = runBlocking { - withTimeout(timeout.toMillis()) { - block() - } + withTimeout(timeout.toMillis()) { block() } } companion object { - /** Name of the flag that determines whether SafetyCenter is enabled. */ - private const val PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled" private const val CTS_PACKAGE_NAME = "android.safetycenter.cts" private const val CTS_BROADCAST_RECEIVER_NAME = - "android.safetycenter.cts.SafetySourceBroadcastReceiver" + "android.safetycenter.testing.SafetySourceBroadcastReceiver" private val TIMEOUT_LONG: Duration = Duration.ofMillis(5000) private val TIMEOUT_SHORT: Duration = Duration.ofMillis(1000) private val EVENT_SOURCE_STATE_CHANGED = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() private const val CTS_SOURCE_ID = "cts_source_id" + private const val CTS_SOURCE_GROUP_ID = "cts_source_group" + // TODO(b/217944317): Consider moving the following to a file where they can be used by // other tests. - private val CTS_SOURCE = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC) + private val CTS_SOURCE = + SafetySource.Builder(SAFETY_SOURCE_TYPE_DYNAMIC) .setId(CTS_SOURCE_ID) .setPackageName(CTS_PACKAGE_NAME) - .setTitleResId(R.string.reference) - .setSummaryResId(R.string.reference) - .setIntentAction("SafetyCenterManagerTest.INTENT_ACTION") + .setTitleResId(android.R.string.ok) + .setSummaryResId(android.R.string.ok) + .setIntentAction(ACTION_SAFETY_CENTER) .setBroadcastReceiverClassName(CTS_BROADCAST_RECEIVER_NAME) .setProfile(SafetySource.PROFILE_PRIMARY) .build() - private val CTS_SOURCE_GROUP = SafetySourcesGroup.Builder() - .setId("cts_source_group") - .setTitleResId(R.string.reference) - .setSummaryResId(R.string.reference) + private val CTS_SOURCE_GROUP = + SafetySourcesGroup.Builder() + .setId(CTS_SOURCE_GROUP_ID) + .setTitleResId(android.R.string.ok) + .setSummaryResId(android.R.string.ok) .addSafetySource(CTS_SOURCE) .build() - private val CTS_ONLY_CONFIG = SafetyCenterConfig.Builder() - .addSafetySourcesGroup(CTS_SOURCE_GROUP) - .build() + private val CTS_CONFIG = + SafetyCenterConfig.Builder().addSafetySourcesGroup(CTS_SOURCE_GROUP).build() } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt new file mode 100644 index 000000000..5265a4fab --- /dev/null +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021 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.cts + +import android.content.Context +import android.content.Intent +import android.content.Intent.ACTION_SAFETY_CENTER +import android.os.Build.VERSION_CODES.TIRAMISU +import android.safetycenter.SafetyCenterManager +import android.safetycenter.testing.SafetyCenterFlags +import android.safetycenter.testing.SafetyCenterFlags.deviceSupportsSafetyCenter +import android.safetycenter.testing.isSafetyCenterEnabledWithPermission +import android.support.test.uiautomator.By +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.UiAutomatorUtils.waitFindObject +import com.google.common.truth.Truth.assertThat +import org.junit.Assume.assumeFalse +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu") +class SafetyCenterUnsupportedTest { + private val context: Context = getApplicationContext() + private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!! + + @Before + fun assumeDeviceDoesntSupportSafetyCenterToRunTests() { + assumeFalse(context.deviceSupportsSafetyCenter()) + } + + @Test + fun launchActivity_showsSecurityTitle() { + startSafetyCenterActivity() + + // CollapsingToolbar title can't be found by text, so using description instead. + waitFindObject(By.desc("Security")) + } + + @Test + fun isSafetyCenterEnabled_withFlagEnabled_returnsFalse() { + SafetyCenterFlags.setSafetyCenterEnabled(true) + + val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission() + + assertThat(isSafetyCenterEnabled).isFalse() + } + + @Test + fun isSafetyCenterEnabled_withFlagDisabled_returnsFalse() { + SafetyCenterFlags.setSafetyCenterEnabled(false) + + val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission() + + assertThat(isSafetyCenterEnabled).isFalse() + } + + private fun startSafetyCenterActivity() { + context.startActivity( + Intent(ACTION_SAFETY_CENTER) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + ) + } +} diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt index 06f1e26e6..a13b4e425 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt @@ -18,13 +18,13 @@ package android.safetycenter.cts import android.os.Build import android.safetycenter.SafetyEvent -import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED -import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED +import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED -import android.safetycenter.testers.AnyTester.assertThatRepresentationsAreEqual -import android.safetycenter.testers.AnyTester.assertThatRepresentationsAreNotEqual -import android.safetycenter.testers.ParcelableTester.assertThatRoundTripReturnsOriginal +import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED +import android.safetycenter.testing.AnyTester.assertThatRepresentationsAreEqual +import android.safetycenter.testing.AnyTester.assertThatRepresentationsAreNotEqual +import android.safetycenter.testing.ParcelableTester.assertThatRoundTripReturnsOriginal import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.google.common.truth.Truth.assertThat @@ -45,9 +45,9 @@ class SafetyEventTest { @Test fun getRefreshBroadcastId_returnsRefreshBroadcastId() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId(REFRESH_BROADCAST_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) + .setRefreshBroadcastId(REFRESH_BROADCAST_ID) + .build() assertThat(safetyEvent.refreshBroadcastId).isEqualTo(REFRESH_BROADCAST_ID) } @@ -55,9 +55,9 @@ class SafetyEventTest { @Test fun getSafetySourceIssueId_returnsSafetySourceIssueId() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) + .build() assertThat(safetyEvent.safetySourceIssueId).isEqualTo(SAFETY_SOURCE_ISSUE_ID) } @@ -65,9 +65,9 @@ class SafetyEventTest { @Test fun getSafetySourceIssueActionId_returnsSafetySourceIssueActionId() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) + .build() assertThat(safetyEvent.safetySourceIssueActionId).isEqualTo(SAFETY_SOURCE_ISSUE_ACTION_ID) } @@ -75,10 +75,10 @@ class SafetyEventTest { @Test fun describeContents_returns0() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) - .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) + .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) + .build() assertThat(safetyEvent.describeContents()).isEqualTo(0) } @@ -86,10 +86,10 @@ class SafetyEventTest { @Test fun createFromParcel_withWriteToParcel_returnsOriginalSafetySourceData() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) - .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) + .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) + .build() assertThatRoundTripReturnsOriginal(safetyEvent, SafetyEvent.CREATOR) } @@ -98,9 +98,9 @@ class SafetyEventTest { @Test fun hashCode_equals_toString_withEqualByReference_areEqual() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId(REFRESH_BROADCAST_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) + .setRefreshBroadcastId(REFRESH_BROADCAST_ID) + .build() val otherSafetyEvent = safetyEvent assertThatRepresentationsAreEqual(safetyEvent, otherSafetyEvent) @@ -109,15 +109,15 @@ class SafetyEventTest { @Test fun hashCode_equals_toString_withAllFieldsEqual_areEqual() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) - .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) + .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) + .build() val otherSafetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) - .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) + .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) + .build() assertThatRepresentationsAreEqual(safetyEvent, otherSafetyEvent) } @@ -125,11 +125,11 @@ class SafetyEventTest { @Test fun hashCode_equals_toString_withDifferentSafetyEventTypes_areNotEqual() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) + .build() val otherSafetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED) + .build() assertThatRepresentationsAreNotEqual(safetyEvent, otherSafetyEvent) } @@ -137,13 +137,13 @@ class SafetyEventTest { @Test fun hashCode_equals_toString_withDifferentRefreshBroadcastIds_areNotEqual() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId(REFRESH_BROADCAST_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) + .setRefreshBroadcastId(REFRESH_BROADCAST_ID) + .build() val otherSafetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId(OTHER_REFRESH_BROADCAST_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) + .setRefreshBroadcastId(OTHER_REFRESH_BROADCAST_ID) + .build() assertThatRepresentationsAreNotEqual(safetyEvent, otherSafetyEvent) } @@ -151,13 +151,13 @@ class SafetyEventTest { @Test fun hashCode_equals_toString_withDifferentIssueIds_areNotEqual() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) + .build() val otherSafetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(OTHER_SAFETY_SOURCE_ISSUE_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(OTHER_SAFETY_SOURCE_ISSUE_ID) + .build() assertThatRepresentationsAreNotEqual(safetyEvent, otherSafetyEvent) } @@ -165,15 +165,15 @@ class SafetyEventTest { @Test fun hashCode_equals_toString_withDifferentActionIds_areNotEqual() { val safetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) - .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) + .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID) + .build() val otherSafetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) - .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) - .setSafetySourceIssueActionId(OTHER_SAFETY_SOURCE_ISSUE_ACTION_ID) - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED) + .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID) + .setSafetySourceIssueActionId(OTHER_SAFETY_SOURCE_ISSUE_ACTION_ID) + .build() assertThatRepresentationsAreNotEqual(safetyEvent, otherSafetyEvent) } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt index 2a8bff776..ed9136f04 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt @@ -19,9 +19,9 @@ package android.safetycenter.cts import android.os.Build import android.safetycenter.SafetyEvent import android.safetycenter.SafetySourceErrorDetails -import android.safetycenter.testers.AnyTester.assertThatRepresentationsAreEqual -import android.safetycenter.testers.AnyTester.assertThatRepresentationsAreNotEqual -import android.safetycenter.testers.ParcelableTester.assertThatRoundTripReturnsOriginal +import android.safetycenter.testing.AnyTester.assertThatRepresentationsAreEqual +import android.safetycenter.testing.AnyTester.assertThatRepresentationsAreNotEqual +import android.safetycenter.testing.ParcelableTester.assertThatRoundTripReturnsOriginal import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.google.common.truth.Truth.assertThat @@ -64,15 +64,15 @@ class SafetySourceErrorDetailsTest { @Test fun equals_toString_withDifferentSafetyEvents_areNotEqual() { val errorDetails = SafetySourceErrorDetails( - SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()) + SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()) val otherErrorDetails = SafetySourceErrorDetails( - SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build()) + SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build()) assertThatRepresentationsAreNotEqual(errorDetails, otherErrorDetails) } companion object { private val SAFETY_EVENT = - SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() + SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() } }
\ No newline at end of file diff --git a/tests/cts/safetycenter/src/android/safetycenter/testers/AnyTester.kt b/tests/cts/safetycenter/src/android/safetycenter/testing/AnyTester.kt index f3fbf77a8..50c37a39a 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/testers/AnyTester.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/testing/AnyTester.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.safetycenter.testers +package android.safetycenter.testing import com.google.common.truth.Truth.assertThat diff --git a/tests/cts/safetycenter/src/android/safetycenter/testers/ParcelableTester.kt b/tests/cts/safetycenter/src/android/safetycenter/testing/ParcelableTester.kt index f2c2482a1..0ba599993 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/testers/ParcelableTester.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/testing/ParcelableTester.kt @@ -1,4 +1,4 @@ -package android.safetycenter.testers +package android.safetycenter.testing import android.os.Parcel import android.os.Parcelable diff --git a/tests/cts/safetycenter/src/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt b/tests/cts/safetycenter/src/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt new file mode 100644 index 000000000..153390492 --- /dev/null +++ b/tests/cts/safetycenter/src/android/safetycenter/testing/SafetyCenterApisWithShellPermissions.kt @@ -0,0 +1,156 @@ +/* + * 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.testing + +import android.Manifest.permission.MANAGE_SAFETY_CENTER +import android.Manifest.permission.READ_SAFETY_CENTER_STATUS +import android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE +import android.safetycenter.SafetyCenterManager +import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener +import android.safetycenter.SafetyEvent +import android.safetycenter.SafetySourceData +import android.safetycenter.SafetySourceErrorDetails +import android.safetycenter.config.SafetyCenterConfig +import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity +import java.util.concurrent.Executor + +/** + * Calls [SafetyCenterManager.isSafetyCenterEnabled] adopting Shell's [READ_SAFETY_CENTER_STATUS] + * permission. + */ +fun SafetyCenterManager.isSafetyCenterEnabledWithPermission() = + callWithShellPermissionIdentity({ isSafetyCenterEnabled }, READ_SAFETY_CENTER_STATUS) + +/** + * Calls [SafetyCenterManager.setSafetySourceData] adopting Shell's [SEND_SAFETY_CENTER_UPDATE] + * permission. + */ +fun SafetyCenterManager.setSafetySourceDataWithPermission( + safetySourceId: String, + safetySourceData: SafetySourceData?, + safetyEvent: SafetyEvent +) = + callWithShellPermissionIdentity( + { setSafetySourceData(safetySourceId, safetySourceData, safetyEvent) }, + SEND_SAFETY_CENTER_UPDATE + ) + +/** + * Calls [SafetyCenterManager.getSafetySourceData] adopting Shell's [SEND_SAFETY_CENTER_UPDATE] + * permission. + */ +fun SafetyCenterManager.getSafetySourceDataWithPermission(id: String) = + callWithShellPermissionIdentity({ getSafetySourceData(id) }, SEND_SAFETY_CENTER_UPDATE) + +/** + * Calls [SafetyCenterManager.reportSafetySourceError] adopting Shell's [MANAGE_SAFETY_CENTER] + * permission. + */ +fun SafetyCenterManager.reportSafetySourceErrorWithPermission( + safetySourceId: String, + safetySourceErrorDetails: SafetySourceErrorDetails +) = + callWithShellPermissionIdentity( + { reportSafetySourceError(safetySourceId, safetySourceErrorDetails) }, + MANAGE_SAFETY_CENTER + ) + +/** + * Calls [SafetyCenterManager.refreshSafetySources] adopting Shell's [MANAGE_SAFETY_CENTER] + * permission. + */ +fun SafetyCenterManager.refreshSafetySourcesWithPermission(refreshReason: Int) = + callWithShellPermissionIdentity({ refreshSafetySources(refreshReason) }, MANAGE_SAFETY_CENTER) + +/** + * Calls [SafetyCenterManager.getSafetyCenterData] adopting Shell's [MANAGE_SAFETY_CENTER] + * permission. + */ +fun SafetyCenterManager.getSafetyCenterDataWithPermission() = + callWithShellPermissionIdentity(::getSafetyCenterData, MANAGE_SAFETY_CENTER) + +/** + * Calls [SafetyCenterManager.addOnSafetyCenterDataChangedListener] adopting Shell's + * [MANAGE_SAFETY_CENTER] permission. + */ +fun SafetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission( + executor: Executor, + listener: OnSafetyCenterDataChangedListener +) = + callWithShellPermissionIdentity( + { addOnSafetyCenterDataChangedListener(executor, listener) }, + MANAGE_SAFETY_CENTER + ) + +/** + * Calls [SafetyCenterManager.removeOnSafetyCenterDataChangedListener] adopting Shell's + * [MANAGE_SAFETY_CENTER] permission. + */ +fun SafetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission( + listener: OnSafetyCenterDataChangedListener +) = + callWithShellPermissionIdentity( + { removeOnSafetyCenterDataChangedListener(listener) }, + MANAGE_SAFETY_CENTER + ) + +/** + * Calls [SafetyCenterManager.dismissSafetyCenterIssue] adopting Shell's [MANAGE_SAFETY_CENTER] + * permission. + */ +fun SafetyCenterManager.dismissSafetyIssueWithPermission(safetyCenterIssueId: String) = + callWithShellPermissionIdentity({ dismissSafetyCenterIssue(safetyCenterIssueId) }, + MANAGE_SAFETY_CENTER) + +/** + * Calls [SafetyCenterManager.executeSafetyCenterIssueAction] adopting Shell's + * [MANAGE_SAFETY_CENTER] permission. + */ +fun SafetyCenterManager.executeSafetyCenterActionWithPermission( + safetyCenterIssueId: String, + safetyCenterIssueActionId: String +) = + callWithShellPermissionIdentity( + { executeSafetyCenterIssueAction(safetyCenterIssueId, safetyCenterIssueActionId) }, + MANAGE_SAFETY_CENTER + ) + +/** + * Calls [SafetyCenterManager.clearAllSafetySourceData] adopting Shell's [MANAGE_SAFETY_CENTER] + * permission. + */ +fun SafetyCenterManager.clearAllSafetySourceDataWithPermission() = + callWithShellPermissionIdentity({ clearAllSafetySourceData() }, MANAGE_SAFETY_CENTER) + +/** + * Calls [SafetyCenterManager.setSafetyCenterConfigOverride] adopting Shell's [MANAGE_SAFETY_CENTER] + * permission. + */ +fun SafetyCenterManager.setSafetyCenterConfigOverrideWithPermission( + safetyCenterConfig: SafetyCenterConfig +) = + callWithShellPermissionIdentity( + { setSafetyCenterConfigOverride(safetyCenterConfig) }, + MANAGE_SAFETY_CENTER + ) + +/** + * Calls [SafetyCenterManager.clearSafetyCenterConfigOverride] adopting Shell's + * [MANAGE_SAFETY_CENTER] permission. + */ +fun SafetyCenterManager.clearSafetyCenterConfigOverrideWithPermission() = + callWithShellPermissionIdentity({ clearSafetyCenterConfigOverride() }, MANAGE_SAFETY_CENTER) diff --git a/tests/cts/safetycenter/src/android/safetycenter/testing/SafetyCenterFlags.kt b/tests/cts/safetycenter/src/android/safetycenter/testing/SafetyCenterFlags.kt new file mode 100644 index 000000000..8ae335c7c --- /dev/null +++ b/tests/cts/safetycenter/src/android/safetycenter/testing/SafetyCenterFlags.kt @@ -0,0 +1,55 @@ +/* + * 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.testing + +import android.Manifest.permission.WRITE_DEVICE_CONFIG +import android.content.Context +import android.content.res.Resources +import android.provider.DeviceConfig +import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity + +/** A class that facilitates working with Safety Center flags. */ +object SafetyCenterFlags { + + /** Name of the flag that determines whether SafetyCenter is enabled. */ + private const val PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled" + + /** Returns whether the device supports Safety Center. */ + fun Context.deviceSupportsSafetyCenter() = + resources.getBoolean( + Resources.getSystem().getIdentifier("config_enableSafetyCenter", "bool", "android") + ) + + /** Sets the Safety Center device config flag to the given boolean [value]. */ + fun setSafetyCenterEnabled(value: Boolean) { + callWithShellPermissionIdentity( + { + val valueWasSet = + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_SAFETY_CENTER_ENABLED, + /* value = */ value.toString(), + /* makeDefault = */ false + ) + if (!valueWasSet) { + throw IllegalStateException("Could not set Safety Center flag value to: $value") + } + }, + WRITE_DEVICE_CONFIG + ) + } +}
\ No newline at end of file diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceBroadcastReceiver.kt b/tests/cts/safetycenter/src/android/safetycenter/testing/SafetySourceBroadcastReceiver.kt index 5f8e8b890..8097d9dbb 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceBroadcastReceiver.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/testing/SafetySourceBroadcastReceiver.kt @@ -14,19 +14,19 @@ * limitations under the License. */ -package android.safetycenter.cts +package android.safetycenter.testing import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.safetycenter.SafetyCenterManager import android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES -import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA +import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE -import android.safetycenter.SafetyCenterManager -import android.safetycenter.SafetySourceData import android.safetycenter.SafetyEvent import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED +import android.safetycenter.SafetySourceData import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout @@ -48,46 +48,49 @@ class SafetySourceBroadcastReceiver : BroadcastReceiver() { when (intent.getIntExtra(EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE, -1)) { EXTRA_REFRESH_REQUEST_TYPE_GET_DATA -> safetyCenterManager.setSafetySourceDataWithPermission( - safetySourceId!!, - safetySourceDataOnPageOpen!!, - EVENT_REFRESH_REQUESTED + safetySourceId!!, + safetySourceDataOnPageOpen!!, + EVENT_REFRESH_REQUESTED ) EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA -> safetyCenterManager.setSafetySourceDataWithPermission( - safetySourceId!!, - safetySourceDataOnRescanClick!!, - EVENT_REFRESH_REQUESTED + safetySourceId!!, + safetySourceDataOnRescanClick!!, + EVENT_REFRESH_REQUESTED ) } - runBlocking { - updateChannel.send(Unit) - } + runBlocking { updateChannel.send(Unit) } } companion object { - private var updateChannel = Channel<Unit>() private val EVENT_REFRESH_REQUESTED = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId("refresh_id") - .build() + SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) + .setRefreshBroadcastId("refresh_id") + .build() + + @Volatile + private var updateChannel = Channel<Unit>() + + @Volatile var safetySourceId: String? = null + + @Volatile var safetySourceDataOnPageOpen: SafetySourceData? = null + + @Volatile var safetySourceDataOnRescanClick: SafetySourceData? = null fun reset() { safetySourceId = null safetySourceDataOnRescanClick = null safetySourceDataOnPageOpen = null + updateChannel.cancel() updateChannel = Channel() } fun waitTillOnReceiveComplete(duration: Duration) { - runBlocking { - withTimeout(duration.toMillis()) { - updateChannel.receive() - } - } + runBlocking { withTimeout(duration.toMillis()) { updateChannel.receive() } } } } } |