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

Commit 6e98efc3 authored by Zak Cohen's avatar Zak Cohen Committed by Android (Google) Code Review
Browse files

Merge "Communal - expose users that are disallowed by work profile to picker." into main

parents f7cc923a 89167883
Loading
Loading
Loading
Loading
+126 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.communal.domain.interactor

import android.app.admin.DevicePolicyManager
import android.app.admin.devicePolicyManager
import android.content.Intent
import android.content.pm.UserInfo
import android.os.UserManager
import android.os.userManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalSettingsInteractorTest : SysuiTestCase() {

    private lateinit var userManager: UserManager
    private lateinit var userRepository: FakeUserRepository
    private lateinit var userTracker: FakeUserTracker

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope

    private lateinit var underTest: CommunalSettingsInteractor

    @Before
    fun setUp() {
        userManager = kosmos.userManager
        userRepository = kosmos.fakeUserRepository
        userTracker = kosmos.fakeUserTracker

        val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
        userRepository.setUserInfos(userInfos)
        userTracker.set(
            userInfos = userInfos,
            selectedUserIndex = 0,
        )

        underTest = kosmos.communalSettingsInteractor
    }

    @Test
    fun filterUsers_dontFilteredUsersWhenAllAreAllowed() =
        testScope.runTest {
            // If no users have any keyguard features disabled...
            val disallowedUser by
                collectLastValue(underTest.workProfileUserDisallowedByDevicePolicy)
            // ...then the disallowed user should be null
            assertNull(disallowedUser)
        }

    @Test
    fun filterUsers_filterWorkProfileUserWhenDisallowed() =
        testScope.runTest {
            // If the work profile user has keyguard widgets disabled...
            setKeyguardFeaturesDisabled(
                USER_INFO_WORK,
                DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
            )
            // ...then the disallowed user match the work profile
            val disallowedUser by
                collectLastValue(underTest.workProfileUserDisallowedByDevicePolicy)
            assertNotNull(disallowedUser)
            assertEquals(USER_INFO_WORK.id, disallowedUser!!.id)
        }

    private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
        whenever(
                kosmos.devicePolicyManager.getKeyguardDisabledFeatures(
                    anyOrNull(),
                    ArgumentMatchers.eq(user.id)
                )
            )
            .thenReturn(disabledFlags)
        kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly(
            context,
            Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
        )
    }

    private companion object {
        val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
        val USER_INFO_WORK =
            UserInfo(
                10,
                "work",
                /* iconPath= */ "",
                /* flags= */ 0,
                UserManager.USER_TYPE_PROFILE_MANAGED,
            )
    }
}
+7 −8
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.smartspace.SmartspaceTarget
import android.content.ComponentName
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.UserInfo
import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
@@ -386,11 +387,11 @@ constructor(
        combine(
            widgetRepository.communalWidgets
                .map { filterWidgetsByExistingUsers(it) }
                .combine(communalSettingsInteractor.allowedByDevicePolicyForWorkProfile) {
                .combine(communalSettingsInteractor.workProfileUserDisallowedByDevicePolicy) {
                    // exclude widgets under work profile if not allowed by device policy
                    widgets,
                    allowedForWorkProfile ->
                    filterWidgetsAllowedByDevicePolicy(widgets, allowedForWorkProfile)
                    disallowedByPolicyUser ->
                    filterWidgetsAllowedByDevicePolicy(widgets, disallowedByPolicyUser)
                },
            updateOnWorkProfileBroadcastReceived,
        ) { widgets, _ ->
@@ -418,13 +419,11 @@ constructor(
    /** Filter widgets based on whether their associated profile is allowed by device policy. */
    private fun filterWidgetsAllowedByDevicePolicy(
        list: List<CommunalWidgetContentModel>,
        allowedByDevicePolicyForWorkProfile: Boolean
        disallowedByDevicePolicyUser: UserInfo?
    ): List<CommunalWidgetContentModel> =
        if (allowedByDevicePolicyForWorkProfile) {
        if (disallowedByDevicePolicyUser == null) {
            list
        } else {
            // Get associated work profile for the currently selected user.
            val workProfile = userTracker.userProfiles.find { it.isManagedProfile }
            list.filter { model ->
                val uid =
                    when (model) {
@@ -432,7 +431,7 @@ constructor(
                            model.providerInfo.profile.identifier
                        is CommunalWidgetContentModel.Pending -> model.user.identifier
                    }
                uid != workProfile?.id
                uid != disallowedByDevicePolicyUser.id
            }
        }

+11 −4
Original line number Diff line number Diff line
@@ -92,15 +92,22 @@ constructor(
        awaitClose { userTracker.removeCallback(callback) }
    }

    /** Whether or not keyguard widgets are allowed for work profile by device policy manager. */
    val allowedByDevicePolicyForWorkProfile: StateFlow<Boolean> =
    /**
     * A user that device policy says shouldn't allow communal widgets, or null if there are no
     * restrictions.
     */
    val workProfileUserDisallowedByDevicePolicy: StateFlow<UserInfo?> =
        workProfileUserInfoCallbackFlow
            .flatMapLatest { workProfile ->
                workProfile?.let { repository.getAllowedByDevicePolicy(it) } ?: flowOf(false)
                workProfile?.let {
                    repository.getAllowedByDevicePolicy(it).map { allowed ->
                        if (!allowed) it else null
                    }
                } ?: flowOf(null)
            }
            .stateIn(
                scope = bgScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = false
                initialValue = null
            )
}
+5 −0
Original line number Diff line number Diff line
@@ -186,6 +186,10 @@ constructor(
                AppWidgetManager.EXTRA_CATEGORY_FILTER,
                CommunalWidgetCategories.defaultCategories
            )

            communalSettingsInteractor.workProfileUserDisallowedByDevicePolicy.value?.let {
                putExtra(EXTRA_USER_ID_FILTER, arrayListOf(it.id))
            }
            putExtra(EXTRA_UI_SURFACE_KEY, EXTRA_UI_SURFACE_VALUE)
            putExtra(EXTRA_PICKER_TITLE, resources.getString(R.string.communal_widget_picker_title))
            putExtra(
@@ -223,6 +227,7 @@ constructor(
        private const val EXTRA_PICKER_DESCRIPTION = "picker_description"
        private const val EXTRA_UI_SURFACE_KEY = "ui_surface"
        private const val EXTRA_UI_SURFACE_VALUE = "widgets_hub"
        private const val EXTRA_USER_ID_FILTER = "filtered_user_ids"
        const val EXTRA_ADDED_APP_WIDGETS_KEY = "added_app_widgets"
    }
}