diff options
9 files changed, 122 insertions, 26 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt index 4a39ba234c67..b7e08da42afd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt @@ -104,6 +104,7 @@ class QSTileLoggerTest : SysuiTestCase() { underTest.logUserActionRejectedByPolicy( QSTileUserAction.Click(null), TileSpec.create("test_spec"), + "test_restriction", ) assertThat(logBuffer.getStringBuffer()).contains("tile click: rejected by policy") diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt index a8bc8d6b36b6..9ce2e0f87499 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt @@ -60,7 +60,9 @@ class QSTileViewModelTest : SysuiTestCase() { @Mock private lateinit var qsTileAnalytics: QSTileAnalytics private val tileConfig = - QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") } + QSTileConfigTestBuilder.build { + policy = QSTilePolicy.Restricted(listOf("test_restriction")) + } private val userRepository = FakeUserRepository() private val tileDataInteractor = FakeQSTileDataInteractor<String>() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt index 18cdd71e25b6..6066d24c2214 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt @@ -16,18 +16,17 @@ package com.android.systemui.qs.tiles.viewmodel -import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest -import com.android.settingslib.RestrictedLockUtils import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger -import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor +import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor.Companion.DISABLED_RESTRICTION +import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor.Companion.ENABLED_RESTRICTION import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper @@ -54,7 +53,6 @@ import org.mockito.MockitoAnnotations /** Tests all possible [QSTileUserAction]s. If you need */ @MediumTest -@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class QSTileViewModelUserInputTest : SysuiTestCase() { @@ -65,8 +63,10 @@ class QSTileViewModelUserInputTest : SysuiTestCase() { // TODO(b/299909989): this should be parametrised. b/299096521 blocks this. private val userAction: QSTileUserAction = QSTileUserAction.Click(null) - private val tileConfig = - QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") } + private var tileConfig = + QSTileConfigTestBuilder.build { + policy = QSTilePolicy.Restricted(listOf(ENABLED_RESTRICTION)) + } private val userRepository = FakeUserRepository() private val tileDataInteractor = FakeQSTileDataInteractor<String>() @@ -112,11 +112,77 @@ class QSTileViewModelUserInputTest : SysuiTestCase() { @Test fun disabledByPolicyUserInputIsSkipped() = testScope.runTest { + tileConfig = + QSTileConfigTestBuilder.build { + policy = QSTilePolicy.Restricted(listOf(DISABLED_RESTRICTION)) + } + underTest = createViewModel(testScope) underTest.state.launchIn(backgroundScope) - disabledByPolicyInteractor.policyResult = - DisabledByPolicyInteractor.PolicyResult.TileDisabled( - RestrictedLockUtils.EnforcedAdmin() + + runCurrent() + + underTest.onActionPerformed(userAction) + runCurrent() + + assertThat(tileDataInteractor.triggers.last()) + .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java) + verify(qsTileLogger) + .logUserActionRejectedByPolicy( + eq(userAction), + eq(tileConfig.tileSpec), + eq(DISABLED_RESTRICTION) + ) + verify(qsTileAnalytics, never()).trackUserAction(any(), any()) + } + + @Test + fun disabledByPolicySecondRestriction_userInputIsSkipped() = + testScope.runTest { + tileConfig = + QSTileConfigTestBuilder.build { + policy = + QSTilePolicy.Restricted(listOf(ENABLED_RESTRICTION, DISABLED_RESTRICTION)) + } + + underTest = createViewModel(testScope) + + underTest.state.launchIn(backgroundScope) + + runCurrent() + + underTest.onActionPerformed(userAction) + runCurrent() + + assertThat(tileDataInteractor.triggers.last()) + .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java) + verify(qsTileLogger) + .logUserActionRejectedByPolicy( + eq(userAction), + eq(tileConfig.tileSpec), + eq(DISABLED_RESTRICTION) ) + verify(qsTileAnalytics, never()).trackUserAction(any(), any()) + } + + /** This tests that the policies are applied sequentially */ + @Test + fun disabledByPolicySecondRestriction_onlyFirstIsTriggered() = + testScope.runTest { + tileConfig = + QSTileConfigTestBuilder.build { + policy = + QSTilePolicy.Restricted( + listOf( + DISABLED_RESTRICTION, + FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2 + ) + ) + } + + underTest = createViewModel(testScope) + + underTest.state.launchIn(backgroundScope) + runCurrent() underTest.onActionPerformed(userAction) @@ -125,7 +191,17 @@ class QSTileViewModelUserInputTest : SysuiTestCase() { assertThat(tileDataInteractor.triggers.last()) .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java) verify(qsTileLogger) - .logUserActionRejectedByPolicy(eq(userAction), eq(tileConfig.tileSpec)) + .logUserActionRejectedByPolicy( + eq(userAction), + eq(tileConfig.tileSpec), + eq(DISABLED_RESTRICTION) + ) + verify(qsTileLogger, never()) + .logUserActionRejectedByPolicy( + eq(userAction), + eq(tileConfig.tileSpec), + eq(FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2) + ) verify(qsTileAnalytics, never()).trackUserAction(any(), any()) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt index bc016bd221e9..065e89f10ef6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt @@ -88,6 +88,7 @@ constructor( fun logUserActionRejectedByPolicy( userAction: QSTileUserAction, tileSpec: TileSpec, + restriction: String, ) { tileSpec .getLogBuffer() @@ -95,7 +96,7 @@ constructor( tileSpec.getLogTag(), LogLevel.DEBUG, { str1 = userAction.toLogString() }, - { "tile $str1: rejected by policy" } + { "tile $str1: rejected by policy, restriction: $restriction" } ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt index 45c6fffe6bd1..8782524cf250 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt @@ -178,7 +178,8 @@ class QSTileViewModelImpl<DATA_TYPE>( /** * Creates a user input flow which: * - filters false inputs with [falsingManager] - * - takes care of a tile being disable by policy using [disabledByPolicyInteractor] + * - takes care of a tile being disable by policy using [disabledByPolicyInteractor]. The + * restrictions will be checked sequentially and the first one to block will be considered. * - notifies [userActionInteractor] about the action * - logs it accordingly using [qsTileLogger] and [qsTileAnalytics] * @@ -201,18 +202,22 @@ class QSTileViewModelImpl<DATA_TYPE>( .onEach { userActionInteractor().handleInput(it.input) } .flowOn(backgroundDispatcher) + /** + * The restrictions will be checked sequentially and the first one to block will be considered. + */ private fun Flow<QSTileUserAction>.filterByPolicy(user: UserHandle): Flow<QSTileUserAction> = config.policy.let { policy -> when (policy) { is QSTilePolicy.NoRestrictions -> this@filterByPolicy is QSTilePolicy.Restricted -> filter { action -> - val result = - disabledByPolicyInteractor.isDisabled(user, policy.userRestriction) - !disabledByPolicyInteractor.handlePolicyResult(result).also { isDisabled -> - if (isDisabled) { - qsTileLogger.logUserActionRejectedByPolicy(action, spec) + policy.userRestrictions.none { + val result = disabledByPolicyInteractor.isDisabled(user, it) + val handleResult = disabledByPolicyInteractor.handlePolicyResult(result) + if (handleResult) { + qsTileLogger.logUserActionRejectedByPolicy(action, spec, it) } + handleResult } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt index 18a4e2d26e89..e9e9d8b0bbfc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt @@ -65,10 +65,10 @@ sealed interface QSTilePolicy { data object NoRestrictions : QSTilePolicy /** - * Tile might be disabled by policy. [userRestriction] is usually a constant from + * Tile might be disabled by policy. Each item in [userRestrictions] is usually a constant from * [android.os.UserManager] like [android.os.UserManager.DISALLOW_AIRPLANE_MODE]. * [com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor] is commonly used * to resolve this and show user a message when needed. */ - data class Restricted(val userRestriction: String) : QSTilePolicy + data class Restricted(val userRestrictions: List<String>) : QSTilePolicy } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt index c4d9cbfcd55c..7a7cb7d3bfdb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt @@ -123,7 +123,7 @@ interface ConnectivityModule { labelRes = R.string.airplane_mode, ), instanceId = uiEventLogger.getNewInstanceId(), - policy = QSTilePolicy.Restricted(UserManager.DISALLOW_AIRPLANE_MODE), + policy = QSTilePolicy.Restricted(listOf(UserManager.DISALLOW_AIRPLANE_MODE)), ) /** Inject AirplaneModeTile into tileViewModelMap in QSModule */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt index ea2b22c0a746..0ec8552e8595 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt @@ -77,7 +77,7 @@ class QSTileViewModelImplTest : SysuiTestCase() { underTest = QSTileViewModelImpl( QSTileConfigTestBuilder.build { - policy = QSTilePolicy.Restricted("test_restriction") + policy = QSTilePolicy.Restricted(listOf("test_restriction")) }, { tileUserActionInteractor }, { tileDataInteractor }, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt index 62765d10486c..fb6ba20e4c51 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt @@ -17,16 +17,21 @@ package com.android.systemui.qs.tiles.base.interactor import android.os.UserHandle +import com.android.settingslib.RestrictedLockUtils class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor { - var policyResult: DisabledByPolicyInteractor.PolicyResult = - DisabledByPolicyInteractor.PolicyResult.TileEnabled - override suspend fun isDisabled( user: UserHandle, userRestriction: String? - ): DisabledByPolicyInteractor.PolicyResult = policyResult + ): DisabledByPolicyInteractor.PolicyResult = + if (userRestriction == DISABLED_RESTRICTION || userRestriction == DISABLED_RESTRICTION_2) { + DisabledByPolicyInteractor.PolicyResult.TileDisabled( + RestrictedLockUtils.EnforcedAdmin() + ) + } else { + DisabledByPolicyInteractor.PolicyResult.TileEnabled + } override fun handlePolicyResult( policyResult: DisabledByPolicyInteractor.PolicyResult @@ -35,4 +40,10 @@ class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor { is DisabledByPolicyInteractor.PolicyResult.TileEnabled -> false is DisabledByPolicyInteractor.PolicyResult.TileDisabled -> true } + + companion object { + const val DISABLED_RESTRICTION = "disabled_restriction" + const val DISABLED_RESTRICTION_2 = "disabled_restriction_2" + const val ENABLED_RESTRICTION = "test_restriction" + } } |