Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 73f87434 authored by Behnam Heydarshahi's avatar Behnam Heydarshahi
Browse files

Allow multiple user restrictions for tile policy

A list of restrictions can be provided, and they will be processed in
order. The first one failing results in tile being disabled.

Flag: aconfig com.android.systemui.qs_new_tiles DEVELOPMENT
Fixes: 331850913
Test: atest QSTileViewModelUserInputTest QSTileViewModelImplTest QSTileViewModelTest
Change-Id: I061bf68f0a0ed7d50342c6ef7c4909486f051d3c
parent 57089e1b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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")
+3 −1
Original line number Diff line number Diff line
@@ -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>()
+86 −10
Original line number Diff line number Diff line
@@ -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,42 @@ 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)
@@ -125,7 +156,52 @@ 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(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)
            runCurrent()

            assertThat(tileDataInteractor.triggers.last())
                .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java)
            verify(qsTileLogger)
                .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())
        }

+2 −1
Original line number Diff line number Diff line
@@ -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" }
            )
    }

+11 −6
Original line number Diff line number Diff line
@@ -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
                        }
                    }
            }
Loading