blob: 1ae0e03ddb6fc70dcd767655e2ab8213a2514b55 [file] [log] [blame]
/*
* 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.Manifest.permission.MANAGE_SAFETY_CENTER
import android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
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.SafetyCenterData
import android.safetycenter.SafetyCenterErrorDetails
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.SafetyEvent
import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED
import android.safetycenter.SafetySourceData
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION
import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
import android.safetycenter.SafetySourceErrorDetails
import android.safetycenter.SafetySourceIssue
import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_DEVICE
import android.safetycenter.SafetySourceStatus
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.cts.testing.Coroutines.TIMEOUT_LONG
import android.safetycenter.cts.testing.Coroutines.TIMEOUT_SHORT
import android.safetycenter.cts.testing.Coroutines.runBlockingWithTimeout
import android.safetycenter.cts.testing.FakeExecutor
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.addOnSafetyCenterDataChangedListenerWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.clearAllSafetySourceDataForTestsWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.clearSafetyCenterConfigForTestsWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.getSafetyCenterConfigWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.getSafetyCenterDataWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.getSafetySourceDataWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.isSafetyCenterEnabledWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.refreshSafetySourcesWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.removeOnSafetyCenterDataChangedListenerWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.setSafetyCenterConfigForTestsWithPermission
import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.setSafetySourceDataWithPermission
import android.safetycenter.cts.testing.SafetyCenterFlags
import android.safetycenter.cts.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import android.safetycenter.cts.testing.SafetySourceBroadcastReceiver
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.callWithShellPermissionIdentity
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import java.time.Duration
import kotlin.test.assertFailsWith
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
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), FLAG_IMMUTABLE)
private val safetySourceDataUnspecified =
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder("None title", "None summary", SEVERITY_LEVEL_UNSPECIFIED)
.setEnabled(false)
.build())
.build()
private val safetySourceDataInformation =
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
.setPendingIntent(somePendingIntent)
.build())
.build()
private val safetySourceDataCritical =
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder(
"Critical title", "Critical summary", SEVERITY_LEVEL_CRITICAL_WARNING)
.setPendingIntent(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())
.setIssueCategory(ISSUE_CATEGORY_DEVICE)
.build())
.build()
private val listener =
object : OnSafetyCenterDataChangedListener {
private val dataChannel = Channel<SafetyCenterData>(UNLIMITED)
private val errorChannel = Channel<SafetyCenterErrorDetails>(UNLIMITED)
override fun onSafetyCenterDataChanged(data: SafetyCenterData) {
runBlockingWithTimeout { dataChannel.send(data) }
}
override fun onError(errorDetails: SafetyCenterErrorDetails) {
runBlockingWithTimeout { errorChannel.send(errorDetails) }
}
fun receiveSafetyCenterData(timeout: Duration = TIMEOUT_LONG) =
runBlockingWithTimeout(timeout) { dataChannel.receive() }
fun receiveSafetyCenterErrorDetails(timeout: Duration = TIMEOUT_LONG) =
runBlockingWithTimeout(timeout) { errorChannel.receive() }
fun cancelChannels() {
dataChannel.cancel()
errorChannel.cancel()
}
}
@Before
fun assumeDeviceSupportsSafetyCenterToRunTests() {
assumeTrue(context.deviceSupportsSafetyCenter())
}
@Before
@After
fun clearDataBetweenTest() {
SafetyCenterFlags.setSafetyCenterEnabled(true)
safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener)
safetyCenterManager.clearAllSafetySourceDataForTestsWithPermission()
safetyCenterManager.clearSafetyCenterConfigForTestsWithPermission()
SafetySourceBroadcastReceiver.reset()
}
@After
fun cancelChannelsAfterTest() {
listener.cancelChannels()
}
@Test
fun isSafetyCenterEnabled_withFlagEnabled_returnsTrue() {
val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission()
assertThat(isSafetyCenterEnabled).isTrue()
}
@Test
fun isSafetyCenterEnabled_withFlagDisabled_returnsFalse() {
SafetyCenterFlags.setSafetyCenterEnabled(false)
val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission()
assertThat(isSafetyCenterEnabled).isFalse()
}
@Test
fun isSafetyCenterEnabled_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) { safetyCenterManager.isSafetyCenterEnabled }
}
@Test
fun setSafetySourceData_validId_setsValue() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataUnspecified, EVENT_SOURCE_STATE_CHANGED)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isEqualTo(safetySourceDataUnspecified)
}
@Test
fun setSafetySourceData_twice_replacesValue() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataUnspecified, EVENT_SOURCE_STATE_CHANGED)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataCritical, EVENT_SOURCE_STATE_CHANGED)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isEqualTo(safetySourceDataCritical)
}
@Test
fun setSafetySourceData_null_clearsValue() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataUnspecified, EVENT_SOURCE_STATE_CHANGED)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceData = null, EVENT_SOURCE_STATE_CHANGED)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isNull()
}
@Test
fun setSafetySourceData_withFlagDisabled_noOp() {
SafetyCenterFlags.setSafetyCenterEnabled(false)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataUnspecified, EVENT_SOURCE_STATE_CHANGED)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isNull()
}
@Test
fun setSafetySourceData_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.setSafetySourceData(
CTS_SOURCE_ID, safetySourceDataUnspecified, EVENT_SOURCE_STATE_CHANGED)
}
}
@Test
fun getSafetySourceData_validId_noData_returnsNull() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isNull()
}
@Test
fun getSafetySourceData_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.getSafetySourceData(CTS_SOURCE_ID)
}
}
@Test
fun reportSafetySourceError_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.reportSafetySourceError(
CTS_SOURCE_ID, SafetySourceErrorDetails(EVENT_SOURCE_STATE_CHANGED))
}
}
@Test
fun refreshSafetySources_withRefreshReasonRescanButtonClick_sourceSendsRescanData() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID
SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataCritical
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_RESCAN_BUTTON_CLICK)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isEqualTo(safetySourceDataCritical)
}
@Test
fun refreshSafetySources_withRefreshReasonPageOpen_sourceSendsPageOpenData() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID
SafetySourceBroadcastReceiver.safetySourceDataOnPageOpen = safetySourceDataInformation
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN)
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isEqualTo(safetySourceDataInformation)
}
@Test
fun refreshSafetySources_whenReceiverDoesNotHaveSendingPermission_sourceDoesNotSendData() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID
SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataCritical
safetyCenterManager.refreshSafetySourcesWithPermission(REFRESH_REASON_RESCAN_BUTTON_CLICK)
assertFailsWith(TimeoutCancellationException::class) {
SafetySourceBroadcastReceiver.waitTillOnReceiveComplete(TIMEOUT_SHORT)
}
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isNull()
}
@Test
fun refreshSafetySources_withFlagDisabled_noOp() {
SafetyCenterFlags.setSafetyCenterEnabled(false)
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID
SafetySourceBroadcastReceiver.safetySourceDataOnPageOpen = safetySourceDataInformation
assertFailsWith(TimeoutCancellationException::class) {
safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
REFRESH_REASON_PAGE_OPEN, TIMEOUT_SHORT)
}
val apiSafetySourceData =
safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
assertThat(apiSafetySourceData).isNull()
}
@Test
fun refreshSafetySources_withInvalidRefreshSeason_throwsIllegalArgumentException() {
val thrown =
assertFailsWith(IllegalArgumentException::class) {
safetyCenterManager.refreshSafetySourcesWithPermission(143201)
}
assertThat(thrown).hasMessageThat().endsWith("refresh reason: 143201")
}
@Test
fun refreshSafetySources_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.refreshSafetySources(REFRESH_REASON_RESCAN_BUTTON_CLICK)
}
}
@Test
fun getSafetyCenterConfig_isNotNull() {
val config = safetyCenterManager.getSafetyCenterConfigWithPermission()
// TODO(b/225152057): Assert on content.
assertThat(config).isNotNull()
}
@Test
fun getSafetyCenterConfig_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) { safetyCenterManager.safetyCenterConfig }
}
@Test
fun getSafetyCenterData_withoutDataProvided_returnsDataFromConfig() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
// TODO(b/218830137): Assert on content.
assertThat(apiSafetyCenterData).isNotNull()
}
@Test
fun getSafetyCenterData_withSomeDataProvided_returnsDataProvided() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataUnspecified, EVENT_SOURCE_STATE_CHANGED)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
// TODO(b/218830137): Assert on content.
assertThat(apiSafetyCenterData).isNotNull()
}
@Test
fun getSafetyCenterData_withUpdatedData_returnsUpdatedData() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataInformation, 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.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
assertThat(apiSafetyCenterData).isNotNull()
}
@Test
fun getSafetyCenterData_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) { safetyCenterManager.safetyCenterData }
}
@Test
fun addOnSafetyCenterDataChangedListener_listenerCalledWithSafetyCenterDataFromConfig() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
// TODO(b/218830137): Assert on content.
assertThat(safetyCenterDataFromListener).isNotNull()
}
@Test
fun addOnSafetyCenterDataChangedListener_listenerCalledOnSafetySourceData() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
// Receive initial data.
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataInformation, EVENT_SOURCE_STATE_CHANGED)
val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
// TODO(b/218830137): Assert on content.
assertThat(safetyCenterDataFromListener).isNotNull()
}
@Test
fun addOnSafetyCenterDataChangedListener_listenerCalledWhenSafetySourceDataChanges() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
// Receive initial data.
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataInformation, EVENT_SOURCE_STATE_CHANGED)
// Receive update from #setSafetySourceData call.
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataCritical, EVENT_SOURCE_STATE_CHANGED)
val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
// TODO(b/218830137): Assert on content.
assertThat(safetyCenterDataFromListener).isNotNull()
}
@Test
fun addOnSafetyCenterDataChangedListener_listenerCalledWhenSafetySourceDataCleared() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
// Receive initial data.
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataInformation, EVENT_SOURCE_STATE_CHANGED)
// Receive update from #setSafetySourceData call.
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceData = null, EVENT_SOURCE_STATE_CHANGED)
val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
// TODO(b/218830137): Assert on content.
assertThat(safetyCenterDataFromListener).isNotNull()
}
@Test
fun addOnSafetyCenterDataChangedListener_listenerNotCalledWhenSafetySourceDataStaysNull() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
// Receive initial data.
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceData = null, EVENT_SOURCE_STATE_CHANGED)
assertFailsWith(TimeoutCancellationException::class) {
listener.receiveSafetyCenterData(TIMEOUT_SHORT)
}
}
@Test
fun addOnSafetyCenterDataChangedListener_listenerNotCalledWhenSafetySourceDataDoesntChange() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
// Receive initial data.
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataInformation, EVENT_SOURCE_STATE_CHANGED)
// Receive update from #setSafetySourceData call.
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataInformation, EVENT_SOURCE_STATE_CHANGED)
assertFailsWith(TimeoutCancellationException::class) {
listener.receiveSafetyCenterData(TIMEOUT_SHORT)
}
}
@Test
fun addOnSafetyCenterDataChangedListener_withFlagDisabled_listenerNotCalled() {
SafetyCenterFlags.setSafetyCenterEnabled(false)
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
assertFailsWith(TimeoutCancellationException::class) {
listener.receiveSafetyCenterData(TIMEOUT_SHORT)
}
}
@Test
fun addOnSafetyCenterDataChangedListener_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.addOnSafetyCenterDataChangedListener(directExecutor(), listener)
}
}
@Test
// Permission is held for the entire test to avoid a racy scenario where the shell identity is
// dropped while it's being acquired on another thread.
fun addOnSafetyCenterDataChangedListener_oneShot_doesntDeadlock() {
callWithShellPermissionIdentity(
{
val oneShotListener =
object : OnSafetyCenterDataChangedListener {
override fun onSafetyCenterDataChanged(safetyCenterData: SafetyCenterData) {
safetyCenterManager.removeOnSafetyCenterDataChangedListener(this)
listener.onSafetyCenterDataChanged(safetyCenterData)
}
}
safetyCenterManager.addOnSafetyCenterDataChangedListener(
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.
listener.receiveSafetyCenterData()
},
MANAGE_SAFETY_CENTER)
}
@Test
fun removeOnSafetyCenterDataChangedListener_listenerNotCalledOnSafetySourceData() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
// Receive initial data.
listener.receiveSafetyCenterData()
safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener)
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataInformation, EVENT_SOURCE_STATE_CHANGED)
assertFailsWith(TimeoutCancellationException::class) {
listener.receiveSafetyCenterData(TIMEOUT_SHORT)
}
}
@Test
fun removeOnSafetyCenterDataChangedListener_listenerNeverCalledAfterRemoving() {
safetyCenterManager.setSafetyCenterConfigForTestsWithPermission(CTS_CONFIG)
val fakeExecutor = FakeExecutor()
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
fakeExecutor, listener)
// Receive initial data.
fakeExecutor.getNextTask().run()
listener.receiveSafetyCenterData()
safetyCenterManager.setSafetySourceDataWithPermission(
CTS_SOURCE_ID, safetySourceDataInformation, EVENT_SOURCE_STATE_CHANGED)
val callListenerTask = fakeExecutor.getNextTask()
safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener)
// Simulate the submitted task being run *after* the remove call completes. Our API should
// guard against this raciness, as users of this class likely don't expect their listener to
// be called after calling #removeOnSafetyCenterDataChangedListener.
callListenerTask.run()
assertFailsWith(TimeoutCancellationException::class) {
listener.receiveSafetyCenterData(TIMEOUT_SHORT)
}
}
@Test
fun removeOnSafetyCenterDataChangedListener_withoutPermission_throwsSecurityException() {
safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
directExecutor(), listener)
assertFailsWith(SecurityException::class) {
safetyCenterManager.removeOnSafetyCenterDataChangedListener(listener)
}
}
@Test
fun dismissSafetyCenterIssue_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.dismissSafetyCenterIssue("bleh")
}
}
@Test
fun executeSafetyCenterIssueAction_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.executeSafetyCenterIssueAction("bleh", "blah")
}
}
@Test
fun clearAllSafetySourceDataForTests_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.clearAllSafetySourceDataForTests()
}
}
@Test
fun setSafetyCenterConfigForTests_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.setSafetyCenterConfigForTests(CTS_CONFIG)
}
}
@Test
fun clearSafetyCenterConfigForTests_withoutPermission_throwsSecurityException() {
assertFailsWith(SecurityException::class) {
safetyCenterManager.clearSafetyCenterConfigForTests()
}
}
private fun SafetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
refreshReason: Int,
timeout: Duration = TIMEOUT_LONG
) {
callWithShellPermissionIdentity(
{
refreshSafetySources(refreshReason)
SafetySourceBroadcastReceiver.waitTillOnReceiveComplete(timeout)
},
SEND_SAFETY_CENTER_UPDATE,
MANAGE_SAFETY_CENTER)
}
companion object {
private const val CTS_PACKAGE_NAME = "android.safetycenter.cts"
private val EVENT_SOURCE_STATE_CHANGED =
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(SAFETY_SOURCE_TYPE_DYNAMIC)
.setId(CTS_SOURCE_ID)
.setPackageName(CTS_PACKAGE_NAME)
.setTitleResId(android.R.string.ok)
.setSummaryResId(android.R.string.ok)
.setIntentAction(ACTION_SAFETY_CENTER)
.setProfile(SafetySource.PROFILE_PRIMARY)
.setRefreshOnPageOpenAllowed(true)
.build()
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_CONFIG =
SafetyCenterConfig.Builder().addSafetySourcesGroup(CTS_SOURCE_GROUP).build()
}
}