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

Commit fbbb1a8b authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

UserInteractor changes to not depend on USC

Makes the UserInteractor no longer depend on UserSwitcherController,
based on a flag.

Bug: 246631653
Test: tests included, also manually tested with followup CL(s)
Change-Id: Ia8783d461c5366dc437fad3308b6a776704ffad3
parent 7c822664
Loading
Loading
Loading
Loading
+114 −0
Original line number Original line Diff line number Diff line
/*
 * 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 com.android.systemui.user.domain.interactor

import android.os.UserHandle
import android.os.UserManager
import com.android.systemui.user.data.repository.UserRepository

/** Utilities related to user management actions. */
object UserActionsUtil {

    /** Returns `true` if it's possible to add a guest user to the device; `false` otherwise. */
    fun canCreateGuest(
        manager: UserManager,
        repository: UserRepository,
        isUserSwitcherEnabled: Boolean,
        isAddUsersFromLockScreenEnabled: Boolean,
    ): Boolean {
        if (!isUserSwitcherEnabled) {
            return false
        }

        return currentUserCanCreateUsers(manager, repository) ||
            anyoneCanCreateUsers(manager, isAddUsersFromLockScreenEnabled)
    }

    /** Returns `true` if it's possible to add a user to the device; `false` otherwise. */
    fun canCreateUser(
        manager: UserManager,
        repository: UserRepository,
        isUserSwitcherEnabled: Boolean,
        isAddUsersFromLockScreenEnabled: Boolean,
    ): Boolean {
        if (!isUserSwitcherEnabled) {
            return false
        }

        if (
            !currentUserCanCreateUsers(manager, repository) &&
                !anyoneCanCreateUsers(manager, isAddUsersFromLockScreenEnabled)
        ) {
            return false
        }

        return manager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY)
    }

    /**
     * Returns `true` if it's possible to add a supervised user to the device; `false` otherwise.
     */
    fun canCreateSupervisedUser(
        manager: UserManager,
        repository: UserRepository,
        isUserSwitcherEnabled: Boolean,
        isAddUsersFromLockScreenEnabled: Boolean,
        supervisedUserPackageName: String?
    ): Boolean {
        if (supervisedUserPackageName.isNullOrEmpty()) {
            return false
        }

        return canCreateUser(
            manager,
            repository,
            isUserSwitcherEnabled,
            isAddUsersFromLockScreenEnabled
        )
    }

    /**
     * Returns `true` if the current user is allowed to add users to the device; `false` otherwise.
     */
    private fun currentUserCanCreateUsers(
        manager: UserManager,
        repository: UserRepository,
    ): Boolean {
        val currentUser = repository.getSelectedUserInfo()
        if (!currentUser.isAdmin && currentUser.id != UserHandle.USER_SYSTEM) {
            return false
        }

        return systemCanCreateUsers(manager)
    }

    /** Returns `true` if the system can add users to the device; `false` otherwise. */
    private fun systemCanCreateUsers(
        manager: UserManager,
    ): Boolean {
        return !manager.hasBaseUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM)
    }

    /** Returns `true` if it's allowed to add users to the device at all; `false` otherwise. */
    private fun anyoneCanCreateUsers(
        manager: UserManager,
        isAddUsersFromLockScreenEnabled: Boolean,
    ): Boolean {
        return systemCanCreateUsers(manager) && isAddUsersFromLockScreenEnabled
    }
}
+522 −43

File changed.

Preview size limit exceeded, changes collapsed.

+554 −0

File added.

Preview size limit exceeded, changes collapsed.

+60 −159
Original line number Original line Diff line number Diff line
@@ -17,51 +17,60 @@


package com.android.systemui.user.domain.interactor
package com.android.systemui.user.domain.interactor


import androidx.test.filters.SmallTest
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
import android.os.UserManager
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.MockitoAnnotations


@SmallTest
abstract class UserInteractorTest : SysuiTestCase() {
@RunWith(JUnit4::class)
class UserInteractorTest : SysuiTestCase() {


    @Mock private lateinit var controller: UserSwitcherController
    @Mock protected lateinit var controller: UserSwitcherController
    @Mock private lateinit var activityStarter: ActivityStarter
    @Mock protected lateinit var activityStarter: ActivityStarter
    @Mock protected lateinit var manager: UserManager
    @Mock protected lateinit var activityManager: ActivityManager
    @Mock protected lateinit var deviceProvisionedController: DeviceProvisionedController
    @Mock protected lateinit var devicePolicyManager: DevicePolicyManager
    @Mock protected lateinit var uiEventLogger: UiEventLogger


    private lateinit var underTest: UserInteractor
    protected lateinit var underTest: UserInteractor


    private lateinit var userRepository: FakeUserRepository
    protected lateinit var userRepository: FakeUserRepository
    private lateinit var keyguardRepository: FakeKeyguardRepository
    protected lateinit var keyguardRepository: FakeKeyguardRepository
    protected lateinit var telephonyRepository: FakeTelephonyRepository


    @Before
    abstract fun isRefactored(): Boolean
    fun setUp() {

    open fun setUp() {
        MockitoAnnotations.initMocks(this)
        MockitoAnnotations.initMocks(this)


        userRepository = FakeUserRepository()
        userRepository = FakeUserRepository()
        keyguardRepository = FakeKeyguardRepository()
        keyguardRepository = FakeKeyguardRepository()
        telephonyRepository = FakeTelephonyRepository()
        val applicationScope = TestCoroutineScope()
        val refreshUsersScheduler =
            RefreshUsersScheduler(
                applicationScope = applicationScope,
                mainDispatcher = IMMEDIATE,
                repository = userRepository,
            )
        underTest =
        underTest =
            UserInteractor(
            UserInteractor(
                applicationContext = context,
                repository = userRepository,
                repository = userRepository,
                controller = controller,
                controller = controller,
                activityStarter = activityStarter,
                activityStarter = activityStarter,
@@ -69,142 +78,34 @@ class UserInteractorTest : SysuiTestCase() {
                    KeyguardInteractor(
                    KeyguardInteractor(
                        repository = keyguardRepository,
                        repository = keyguardRepository,
                    ),
                    ),
            )
                featureFlags =
    }
                    FakeFeatureFlags().apply {

                        set(Flags.REFACTORED_USER_SWITCHER_CONTROLLER, isRefactored())
    @Test
                    },
    fun `actions - not actionable when locked and locked - no actions`() =
                manager = manager,
        runBlocking(IMMEDIATE) {
                applicationScope = applicationScope,
            userRepository.setActions(UserActionModel.values().toList())
                telephonyInteractor =
            userRepository.setActionableWhenLocked(false)
                    TelephonyInteractor(
            keyguardRepository.setKeyguardShowing(true)
                        repository = telephonyRepository,

                    ),
            var actions: List<UserActionModel>? = null
                broadcastDispatcher = fakeBroadcastDispatcher,
            val job = underTest.actions.onEach { actions = it }.launchIn(this)
                backgroundDispatcher = IMMEDIATE,

                activityManager = activityManager,
            assertThat(actions).isEmpty()
                refreshUsersScheduler = refreshUsersScheduler,
            job.cancel()
                guestUserInteractor =
        }
                    GuestUserInteractor(

                        applicationContext = context,
    @Test
                        applicationScope = applicationScope,
    fun `actions - not actionable when locked and not locked`() =
                        mainDispatcher = IMMEDIATE,
        runBlocking(IMMEDIATE) {
                        backgroundDispatcher = IMMEDIATE,
            userRepository.setActions(
                        manager = manager,
                listOf(
                        repository = userRepository,
                    UserActionModel.ENTER_GUEST_MODE,
                        deviceProvisionedController = deviceProvisionedController,
                    UserActionModel.ADD_USER,
                        devicePolicyManager = devicePolicyManager,
                    UserActionModel.ADD_SUPERVISED_USER,
                        refreshUsersScheduler = refreshUsersScheduler,
                )
                        uiEventLogger = uiEventLogger,
            )
            userRepository.setActionableWhenLocked(false)
            keyguardRepository.setKeyguardShowing(false)

            var actions: List<UserActionModel>? = null
            val job = underTest.actions.onEach { actions = it }.launchIn(this)

            assertThat(actions)
                .isEqualTo(
                    listOf(
                        UserActionModel.ENTER_GUEST_MODE,
                        UserActionModel.ADD_USER,
                        UserActionModel.ADD_SUPERVISED_USER,
                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                    )
                )
            job.cancel()
        }

    @Test
    fun `actions - actionable when locked and not locked`() =
        runBlocking(IMMEDIATE) {
            userRepository.setActions(
                listOf(
                    UserActionModel.ENTER_GUEST_MODE,
                    UserActionModel.ADD_USER,
                    UserActionModel.ADD_SUPERVISED_USER,
                )
            )
            userRepository.setActionableWhenLocked(true)
            keyguardRepository.setKeyguardShowing(false)

            var actions: List<UserActionModel>? = null
            val job = underTest.actions.onEach { actions = it }.launchIn(this)

            assertThat(actions)
                .isEqualTo(
                    listOf(
                        UserActionModel.ENTER_GUEST_MODE,
                        UserActionModel.ADD_USER,
                        UserActionModel.ADD_SUPERVISED_USER,
                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                    )
                )
            job.cancel()
        }

    @Test
    fun `actions - actionable when locked and locked`() =
        runBlocking(IMMEDIATE) {
            userRepository.setActions(
                listOf(
                    UserActionModel.ENTER_GUEST_MODE,
                    UserActionModel.ADD_USER,
                    UserActionModel.ADD_SUPERVISED_USER,
                )
            )
            userRepository.setActionableWhenLocked(true)
            keyguardRepository.setKeyguardShowing(true)

            var actions: List<UserActionModel>? = null
            val job = underTest.actions.onEach { actions = it }.launchIn(this)

            assertThat(actions)
                .isEqualTo(
                    listOf(
                        UserActionModel.ENTER_GUEST_MODE,
                        UserActionModel.ADD_USER,
                        UserActionModel.ADD_SUPERVISED_USER,
                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                    )
                    )
            )
            )
            job.cancel()
        }

    @Test
    fun selectUser() {
        val userId = 3

        underTest.selectUser(userId)

        verify(controller).onUserSelected(eq(userId), nullable())
    }

    @Test
    fun `executeAction - guest`() {
        underTest.executeAction(UserActionModel.ENTER_GUEST_MODE)

        verify(controller).createAndSwitchToGuestUser(nullable())
    }

    @Test
    fun `executeAction - add user`() {
        underTest.executeAction(UserActionModel.ADD_USER)

        verify(controller).showAddUserDialog(nullable())
    }

    @Test
    fun `executeAction - add supervised user`() {
        underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)

        verify(controller).startSupervisedUserActivity()
    }

    @Test
    fun `executeAction - manage users`() {
        underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)

        verify(activityStarter).startActivity(any(), anyBoolean())
    }
    }


    companion object {
    companion object {
+188 −0
Original line number Original line Diff line number Diff line
/*
 * 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 com.android.systemui.user.domain.interactor

import androidx.test.filters.SmallTest
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.verify

@SmallTest
@RunWith(JUnit4::class)
open class UserInteractorUnrefactoredTest : UserInteractorTest() {

    override fun isRefactored(): Boolean {
        return false
    }

    @Before
    override fun setUp() {
        super.setUp()
    }

    @Test
    fun `actions - not actionable when locked and locked - no actions`() =
        runBlocking(IMMEDIATE) {
            userRepository.setActions(UserActionModel.values().toList())
            userRepository.setActionableWhenLocked(false)
            keyguardRepository.setKeyguardShowing(true)

            var actions: List<UserActionModel>? = null
            val job = underTest.actions.onEach { actions = it }.launchIn(this)

            assertThat(actions).isEmpty()
            job.cancel()
        }

    @Test
    fun `actions - not actionable when locked and not locked`() =
        runBlocking(IMMEDIATE) {
            userRepository.setActions(
                listOf(
                    UserActionModel.ENTER_GUEST_MODE,
                    UserActionModel.ADD_USER,
                    UserActionModel.ADD_SUPERVISED_USER,
                )
            )
            userRepository.setActionableWhenLocked(false)
            keyguardRepository.setKeyguardShowing(false)

            var actions: List<UserActionModel>? = null
            val job = underTest.actions.onEach { actions = it }.launchIn(this)

            assertThat(actions)
                .isEqualTo(
                    listOf(
                        UserActionModel.ENTER_GUEST_MODE,
                        UserActionModel.ADD_USER,
                        UserActionModel.ADD_SUPERVISED_USER,
                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                    )
                )
            job.cancel()
        }

    @Test
    fun `actions - actionable when locked and not locked`() =
        runBlocking(IMMEDIATE) {
            userRepository.setActions(
                listOf(
                    UserActionModel.ENTER_GUEST_MODE,
                    UserActionModel.ADD_USER,
                    UserActionModel.ADD_SUPERVISED_USER,
                )
            )
            userRepository.setActionableWhenLocked(true)
            keyguardRepository.setKeyguardShowing(false)

            var actions: List<UserActionModel>? = null
            val job = underTest.actions.onEach { actions = it }.launchIn(this)

            assertThat(actions)
                .isEqualTo(
                    listOf(
                        UserActionModel.ENTER_GUEST_MODE,
                        UserActionModel.ADD_USER,
                        UserActionModel.ADD_SUPERVISED_USER,
                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                    )
                )
            job.cancel()
        }

    @Test
    fun `actions - actionable when locked and locked`() =
        runBlocking(IMMEDIATE) {
            userRepository.setActions(
                listOf(
                    UserActionModel.ENTER_GUEST_MODE,
                    UserActionModel.ADD_USER,
                    UserActionModel.ADD_SUPERVISED_USER,
                )
            )
            userRepository.setActionableWhenLocked(true)
            keyguardRepository.setKeyguardShowing(true)

            var actions: List<UserActionModel>? = null
            val job = underTest.actions.onEach { actions = it }.launchIn(this)

            assertThat(actions)
                .isEqualTo(
                    listOf(
                        UserActionModel.ENTER_GUEST_MODE,
                        UserActionModel.ADD_USER,
                        UserActionModel.ADD_SUPERVISED_USER,
                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                    )
                )
            job.cancel()
        }

    @Test
    fun selectUser() {
        val userId = 3

        underTest.selectUser(userId)

        verify(controller).onUserSelected(eq(userId), nullable())
    }

    @Test
    fun `executeAction - guest`() {
        underTest.executeAction(UserActionModel.ENTER_GUEST_MODE)

        verify(controller).createAndSwitchToGuestUser(nullable())
    }

    @Test
    fun `executeAction - add user`() {
        underTest.executeAction(UserActionModel.ADD_USER)

        verify(controller).showAddUserDialog(nullable())
    }

    @Test
    fun `executeAction - add supervised user`() {
        underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)

        verify(controller).startSupervisedUserActivity()
    }

    @Test
    fun `executeAction - manage users`() {
        underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)

        verify(activityStarter).startActivity(any(), anyBoolean())
    }

    companion object {
        private val IMMEDIATE = Dispatchers.Main.immediate
    }
}
Loading