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

Commit 7e4247a3 authored by Denis's avatar Denis
Browse files

Rename logout methods in UserRepository for extra clarity

This is a preliminary work for fixing 404183874

There are two actions in Android that are called "Logout".

The older one is the process of returning from secondary user
into a primary one on devices managed by DO, where user switch
from primary user to secondary was initiated via
DevicePolicyManager, and secondary user might be restricted
from simply switching to primary user via user restrictions.

Recent one is being added to support desktop-like user interaction
where instead of directly switching between different users
they have to end session for one user, return to neutral state
(login screen) that is ran under system user, and only then
start new user session. This logic is contained in UserManager.

While both of these actions are called "logout", they have quite
different underlying logic. As this logic might evolve with time
(especially for UserManager path) it better to add a bit more clarity
in method names, so it does not lead to confusion like
ag/32246147 that fixed b/401000285 but lead to b/404183874.

Test: m sysui
Bug: 404183874
Flag: EXEMPT Pure refactoring (rename)
Change-Id: I0b1c399e2fadbe66ff670277f7f73e519283f448
parent 2a9f96f4
Loading
Loading
Loading
Loading
+38 −38
Original line number Diff line number Diff line
@@ -310,114 +310,114 @@ class UserRepositoryImplTest : SysuiTestCase() {
        }

    @Test
    fun isSecondaryUserLogoutEnabled_secondaryLogoutDisabled_alwaysFalse() =
    fun isPolicyManagerLogoutEnabled_policyManagerLogoutDisabled_alwaysFalse() =
        testScope.runTest {
            underTest = create(testScope.backgroundScope)
            mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
            setSecondaryUserLogoutEnabled(false)
            mockPolicyManagerLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
            setPolicyManagerLogoutEnabled(false)
            setUpUsers(count = 2, selectedIndex = 0)
            tracker.onProfileChanged()

            val secondaryUserLogoutEnabled by
                collectLastValue(underTest.isSecondaryUserLogoutEnabled)
            val policyManagerLogoutEnabled by
                collectLastValue(underTest.isPolicyManagerLogoutEnabled)

            assertThat(secondaryUserLogoutEnabled).isFalse()
            assertThat(policyManagerLogoutEnabled).isFalse()

            setUpUsers(count = 2, selectedIndex = 1)
            tracker.onProfileChanged()
            assertThat(secondaryUserLogoutEnabled).isFalse()
            assertThat(policyManagerLogoutEnabled).isFalse()
        }

    @Test
    fun isSecondaryUserLogoutEnabled_secondaryLogoutEnabled_NullLogoutUser_alwaysFalse() =
    fun isPolicyManagerLogoutEnabled_policyManagerLogoutEnabled_NullLogoutUser_alwaysFalse() =
        testScope.runTest {
            underTest = create(testScope.backgroundScope)
            mockLogoutUser(LogoutUserResult.NONE)
            setSecondaryUserLogoutEnabled(true)
            mockPolicyManagerLogoutUser(LogoutUserResult.NONE)
            setPolicyManagerLogoutEnabled(true)
            setUpUsers(count = 2, selectedIndex = 0)
            tracker.onProfileChanged()

            val secondaryUserLogoutEnabled by
                collectLastValue(underTest.isSecondaryUserLogoutEnabled)
            val policyManagerLogoutEnabled by
                collectLastValue(underTest.isPolicyManagerLogoutEnabled)

            assertThat(secondaryUserLogoutEnabled).isFalse()
            assertThat(policyManagerLogoutEnabled).isFalse()

            setUpUsers(count = 2, selectedIndex = 1)
            tracker.onProfileChanged()
            assertThat(secondaryUserLogoutEnabled).isFalse()
            assertThat(policyManagerLogoutEnabled).isFalse()
        }

    @Test
    fun isSecondaryUserLogoutEnabled_secondaryLogoutEnabled_NonSystemLogoutUser_trueWhenNonSystem() =
    fun isPolicyManagerLogoutEnabled_policyManagerLogoutEnabled_NonSystemLogoutUser_trueWhenNonSystem() =
        testScope.runTest {
            underTest = create(testScope.backgroundScope)
            mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
            setSecondaryUserLogoutEnabled(true)
            mockPolicyManagerLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
            setPolicyManagerLogoutEnabled(true)
            setUpUsers(count = 2, selectedIndex = 0)
            tracker.onProfileChanged()

            val secondaryUserLogoutEnabled by
                collectLastValue(underTest.isSecondaryUserLogoutEnabled)
            val policyManagerLogoutEnabled by
                collectLastValue(underTest.isPolicyManagerLogoutEnabled)

            assertThat(secondaryUserLogoutEnabled).isFalse()
            assertThat(policyManagerLogoutEnabled).isFalse()

            setUpUsers(count = 2, selectedIndex = 1)
            tracker.onProfileChanged()
            assertThat(secondaryUserLogoutEnabled).isTrue()
            assertThat(policyManagerLogoutEnabled).isTrue()
        }

    @Test
    fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenDisabled_alwaysFalse() =
    fun isLogoutWithUserManagerEnabled_userManagerLogoutDisabled_alwaysFalse() =
        testScope.runTest {
            underTest = create(testScope.backgroundScope)
            mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
            mockPolicyManagerLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
            setUserSwitchingMustGoThroughLoginScreen(false)
            setUpUsers(count = 2, selectedIndex = 0)
            tracker.onProfileChanged()

            val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
            val userManagerLogoutEnabled by collectLastValue(underTest.isUserManagerLogoutEnabled)

            assertThat(logoutToSystemUserEnabled).isFalse()
            assertThat(userManagerLogoutEnabled).isFalse()

            setUpUsers(count = 2, selectedIndex = 1)
            tracker.onProfileChanged()
            assertThat(logoutToSystemUserEnabled).isFalse()
            assertThat(userManagerLogoutEnabled).isFalse()
        }

    @Test
    fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenEnabled_NullLogoutUser_alwaysFalse() =
    fun isLogoutWithUserManagerEnabled_userManagerLogoutEnabled_NullLogoutUser_Manager_alwaysFalse() =
        testScope.runTest {
            underTest = create(testScope.backgroundScope)
            mockLogoutUser(LogoutUserResult.NONE)
            mockPolicyManagerLogoutUser(LogoutUserResult.NONE)
            setUserSwitchingMustGoThroughLoginScreen(true)
            setUpUsers(count = 2, selectedIndex = 0)
            tracker.onProfileChanged()

            val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
            val userManagerLogoutEnabled by collectLastValue(underTest.isUserManagerLogoutEnabled)

            assertThat(logoutToSystemUserEnabled).isFalse()
            assertThat(userManagerLogoutEnabled).isFalse()

            setUpUsers(count = 2, selectedIndex = 1)
            tracker.onProfileChanged()
            assertThat(logoutToSystemUserEnabled).isFalse()
            assertThat(userManagerLogoutEnabled).isFalse()
        }

    @Test
    fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenEnabled_NonSystemLogoutUser_trueWhenNonSystem() =
    fun isLogoutWithManager() =
        testScope.runTest {
            underTest = create(testScope.backgroundScope)
            mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
            mockPolicyManagerLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
            setUserSwitchingMustGoThroughLoginScreen(true)
            setUpUsers(count = 2, selectedIndex = 0)
            tracker.onProfileChanged()

            val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
            val userManagerLogoutEnabled by collectLastValue(underTest.isUserManagerLogoutEnabled)

            assertThat(logoutToSystemUserEnabled).isFalse()
            assertThat(userManagerLogoutEnabled).isFalse()

            setUpUsers(count = 2, selectedIndex = 1)
            tracker.onProfileChanged()
            assertThat(logoutToSystemUserEnabled).isTrue()
            assertThat(userManagerLogoutEnabled).isTrue()
        }

    private fun createUserInfo(id: Int, isGuest: Boolean): UserInfo {
@@ -466,7 +466,7 @@ class UserRepositoryImplTest : SysuiTestCase() {
        assertThat(model.isUserSwitcherEnabled).isEqualTo(expectedUserSwitcherEnabled)
    }

    private fun setSecondaryUserLogoutEnabled(enabled: Boolean) {
    private fun setPolicyManagerLogoutEnabled(enabled: Boolean) {
        whenever(devicePolicyManager.isLogoutEnabled).thenReturn(enabled)
        broadcastDispatcher.sendIntentToMatchingReceiversOnly(
            kosmos.applicationContext,
@@ -481,7 +481,7 @@ class UserRepositoryImplTest : SysuiTestCase() {
        )
    }

    private fun mockLogoutUser(result: LogoutUserResult) {
    private fun mockPolicyManagerLogoutUser(result: LogoutUserResult) {
        when (result) {
            LogoutUserResult.NONE -> {
                whenever(devicePolicyManager.logoutUser).thenReturn(null)
+27 −25
Original line number Diff line number Diff line
@@ -48,67 +48,69 @@ class UserLogoutInteractorTest : SysuiTestCase() {
    fun setUp() {
        userRepository.setUserInfos(USER_INFOS)
        runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[2]) }
        userRepository.setLogoutToSystemUserEnabled(false)
        userRepository.setSecondaryUserLogoutEnabled(false)
        userRepository.setUserManagerLogoutEnabled(false)
        userRepository.setPolicyManagerLogoutEnabled(false)
    }

    @Test
    fun logOut_doesNothing_whenBothLogoutOptionsAreDisabled() {
        testScope.runTest {
            val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
            val secondaryUserLogoutCount = userRepository.logOutSecondaryUserCallCount
            val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
            val secondaryUserLogoutCount = userRepository.logOutWithPolicyManagerCallCount
            val logoutToSystemUserCount = userRepository.logOutWithUserManagerCallCount
            assertThat(isLogoutEnabled).isFalse()
            underTest.logOut()
            assertThat(userRepository.logOutSecondaryUserCallCount)
            assertThat(userRepository.logOutWithPolicyManagerCallCount)
                .isEqualTo(secondaryUserLogoutCount)
            assertThat(userRepository.logOutToSystemUserCallCount)
            assertThat(userRepository.logOutWithUserManagerCallCount)
                .isEqualTo(logoutToSystemUserCount)
        }
    }

    @Test
    fun logOut_logsOutSecondaryUser_whenAdminEnabledSecondaryLogout() {
    fun logOut_logsOutUsingPolicyManager_whenPolicyManagerLogoutIsEnabled() {
        testScope.runTest {
            val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
            val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
            val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
            userRepository.setSecondaryUserLogoutEnabled(true)
            val lastLogoutCount = userRepository.logOutWithPolicyManagerCallCount
            val logoutToSystemUserCount = userRepository.logOutWithUserManagerCallCount
            userRepository.setPolicyManagerLogoutEnabled(true)
            assertThat(isLogoutEnabled).isTrue()
            underTest.logOut()
            assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount + 1)
            assertThat(userRepository.logOutToSystemUserCallCount)
            assertThat(userRepository.logOutWithPolicyManagerCallCount)
                .isEqualTo(lastLogoutCount + 1)
            assertThat(userRepository.logOutWithUserManagerCallCount)
                .isEqualTo(logoutToSystemUserCount)
        }
    }

    @Test
    fun logOut_logsOutToSystemUser_whenLogoutToSystemUserIsEnabled() {
    fun logOut_logsOutUsingUserManager_whenLogoutWithUserManagerIsEnabled() {
        testScope.runTest {
            val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
            val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
            val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
            userRepository.setLogoutToSystemUserEnabled(true)
            val lastLogoutCount = userRepository.logOutWithPolicyManagerCallCount
            val logoutToSystemUserCount = userRepository.logOutWithUserManagerCallCount
            userRepository.setUserManagerLogoutEnabled(true)
            assertThat(isLogoutEnabled).isTrue()
            underTest.logOut()
            assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount)
            assertThat(userRepository.logOutToSystemUserCallCount)
            assertThat(userRepository.logOutWithPolicyManagerCallCount).isEqualTo(lastLogoutCount)
            assertThat(userRepository.logOutWithUserManagerCallCount)
                .isEqualTo(logoutToSystemUserCount + 1)
        }
    }

    @Test
    fun logOut_secondaryUserTakesPrecedence() {
    fun logOut_policyManagerTakesPrecedence() {
        testScope.runTest {
            val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
            val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
            val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
            userRepository.setLogoutToSystemUserEnabled(true)
            userRepository.setSecondaryUserLogoutEnabled(true)
            val lastLogoutCount = userRepository.logOutWithPolicyManagerCallCount
            val logoutToSystemUserCount = userRepository.logOutWithUserManagerCallCount
            userRepository.setUserManagerLogoutEnabled(true)
            userRepository.setPolicyManagerLogoutEnabled(true)
            assertThat(isLogoutEnabled).isTrue()
            underTest.logOut()
            assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount + 1)
            assertThat(userRepository.logOutToSystemUserCallCount)
            assertThat(userRepository.logOutWithPolicyManagerCallCount)
                .isEqualTo(lastLogoutCount + 1)
            assertThat(userRepository.logOutWithUserManagerCallCount)
                .isEqualTo(logoutToSystemUserCount)
        }
    }
+24 −16
Original line number Diff line number Diff line
@@ -113,11 +113,19 @@ interface UserRepository {
    /** Whether refresh users should be paused. */
    var isRefreshUsersPaused: Boolean

    /** Whether logout for secondary users is enabled by admin device policy. */
    val isSecondaryUserLogoutEnabled: StateFlow<Boolean>
    /**
     * Whether logout back to primary user can be performed by the device Policy Manager for the
     * secondary account. See
     * https://developer.android.com/work/dpc/dedicated-devices/multiple-users#logout for more
     * details.
     */
    val isPolicyManagerLogoutEnabled: StateFlow<Boolean>

    /** Whether logout into system user is enabled. */
    val isLogoutToSystemUserEnabled: StateFlow<Boolean>
    /**
     * Whether logout can be performed by the UserManager, to support desktop-style logout into the
     * neutral login space.
     */
    val isUserManagerLogoutEnabled: StateFlow<Boolean>

    /** Asynchronously refresh the list of users. This will cause [userInfos] to be updated. */
    fun refreshUsers()
@@ -128,11 +136,11 @@ interface UserRepository {

    fun isUserSwitcherEnabled(): Boolean

    /** Performs logout logout for secondary users. */
    suspend fun logOutSecondaryUser()
    /** Performs logout from secondary user into primary user via DevicePolicyManager. */
    suspend fun logOutWithPolicyManager()

    /** Performs logout into the system user. */
    suspend fun logOutToSystemUser()
    /** Performs logout into the system user via UserManager. */
    suspend fun logOutWithUserManager()

    /**
     * Returns the user ID of the "main user" of the device. This user may have access to certain
@@ -259,7 +267,7 @@ constructor(
    override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }

    /** Whether the secondary user logout is enabled by the admin device policy. */
    private val isSecondaryUserLogoutSupported: Flow<Boolean> =
    private val isPolicyManagerLogoutSupported: Flow<Boolean> =
        broadcastDispatcher
            .broadcastFlow(
                filter =
@@ -279,11 +287,11 @@ constructor(
            .flowOn(backgroundDispatcher)

    @SuppressLint("MissingPermission")
    override val isSecondaryUserLogoutEnabled: StateFlow<Boolean> =
    override val isPolicyManagerLogoutEnabled: StateFlow<Boolean> =
        selectedUser
            .flatMapLatestConflated { selectedUser ->
                if (selectedUser.isEligibleForLogout()) {
                    isSecondaryUserLogoutSupported
                    isPolicyManagerLogoutSupported
                } else {
                    flowOf(false)
                }
@@ -303,7 +311,7 @@ constructor(
    }

    @SuppressLint("MissingPermission")
    override val isLogoutToSystemUserEnabled: StateFlow<Boolean> =
    override val isUserManagerLogoutEnabled: StateFlow<Boolean> =
        selectedUser
            .flatMapLatestConflated { selectedUser ->
                if (selectedUser.isEligibleForLogout()) {
@@ -319,16 +327,16 @@ constructor(
            .stateIn(applicationScope, SharingStarted.Eagerly, false)

    @SuppressLint("MissingPermission")
    override suspend fun logOutSecondaryUser() {
        if (isSecondaryUserLogoutEnabled.value) {
    override suspend fun logOutWithPolicyManager() {
        if (isPolicyManagerLogoutEnabled.value) {
            withContext(backgroundDispatcher) { devicePolicyManager.logoutUser() }
        }
    }

    override suspend fun logOutToSystemUser() {
    override suspend fun logOutWithUserManager() {
        // TODO(b/377493351) : start using proper logout API once it is available.
        // Using reboot is a temporary solution.
        if (isLogoutToSystemUserEnabled.value) {
        if (isUserManagerLogoutEnabled.value) {
            if (android.multiuser.Flags.logoutUserApi()) {
                withContext(backgroundDispatcher) {
                    val currentUserId = tracker.userId
+6 −6
Original line number Diff line number Diff line
@@ -39,18 +39,18 @@ constructor(

    val isLogoutEnabled: StateFlow<Boolean> =
        combine(
                userRepository.isSecondaryUserLogoutEnabled,
                userRepository.isLogoutToSystemUserEnabled,
                userRepository.isPolicyManagerLogoutEnabled,
                userRepository.isUserManagerLogoutEnabled,
                Boolean::or,
            )
            .stateIn(applicationScope, SharingStarted.Eagerly, false)

    fun logOut() {
        applicationScope.launch {
            if (userRepository.isSecondaryUserLogoutEnabled.value) {
                userRepository.logOutSecondaryUser()
            } else if (userRepository.isLogoutToSystemUserEnabled.value) {
                userRepository.logOutToSystemUser()
            if (userRepository.isPolicyManagerLogoutEnabled.value) {
                userRepository.logOutWithPolicyManager()
            } else if (userRepository.isUserManagerLogoutEnabled.value) {
                userRepository.logOutWithUserManager()
            }
        }
    }
+16 −16
Original line number Diff line number Diff line
@@ -69,13 +69,13 @@ class FakeUserRepository @Inject constructor() : UserRepository {
        )
    override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }

    private val _isSecondaryUserLogoutEnabled = MutableStateFlow<Boolean>(false)
    override val isSecondaryUserLogoutEnabled: StateFlow<Boolean> =
        _isSecondaryUserLogoutEnabled.asStateFlow()
    private val _isPolicyManagerLogoutEnabled = MutableStateFlow<Boolean>(false)
    override val isPolicyManagerLogoutEnabled: StateFlow<Boolean> =
        _isPolicyManagerLogoutEnabled.asStateFlow()

    private val _isLogoutToSystemUserEnabled = MutableStateFlow<Boolean>(false)
    override val isLogoutToSystemUserEnabled: StateFlow<Boolean> =
        _isLogoutToSystemUserEnabled.asStateFlow()
    private val _isUserManagerLogoutEnabled = MutableStateFlow<Boolean>(false)
    override val isUserManagerLogoutEnabled: StateFlow<Boolean> =
        _isUserManagerLogoutEnabled.asStateFlow()

    private val _userUnlockedState = MutableStateFlow(emptyMap<UserHandle, Boolean>())

@@ -122,26 +122,26 @@ class FakeUserRepository @Inject constructor() : UserRepository {
        return _userSwitcherSettings.value.isUserSwitcherEnabled
    }

    fun setSecondaryUserLogoutEnabled(logoutEnabled: Boolean) {
        _isSecondaryUserLogoutEnabled.value = logoutEnabled
    fun setPolicyManagerLogoutEnabled(logoutEnabled: Boolean) {
        _isPolicyManagerLogoutEnabled.value = logoutEnabled
    }

    var logOutSecondaryUserCallCount: Int = 0
    var logOutWithPolicyManagerCallCount: Int = 0
        private set

    override suspend fun logOutSecondaryUser() {
        logOutSecondaryUserCallCount++
    override suspend fun logOutWithPolicyManager() {
        logOutWithPolicyManagerCallCount++
    }

    fun setLogoutToSystemUserEnabled(logoutEnabled: Boolean) {
        _isLogoutToSystemUserEnabled.value = logoutEnabled
    fun setUserManagerLogoutEnabled(logoutEnabled: Boolean) {
        _isUserManagerLogoutEnabled.value = logoutEnabled
    }

    var logOutToSystemUserCallCount: Int = 0
    var logOutWithUserManagerCallCount: Int = 0
        private set

    override suspend fun logOutToSystemUser() {
        logOutToSystemUserCallCount++
    override suspend fun logOutWithUserManager() {
        logOutWithUserManagerCallCount++
    }

    fun setUserInfos(infos: List<UserInfo>) {