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

Commit 7cd10390 authored by Coco Duan's avatar Coco Duan Committed by Android (Google) Code Review
Browse files

Merge changes I0ac2d766,I198371b6 into main

* changes:
  Filter out work profile widgets when not allowed by device policy
  Allow disabling keyguard widgets for work profile
parents f880e7c8 20f797a8
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -7056,9 +7056,10 @@ public class DevicePolicyManager {
    public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
    /**
     * Disable all keyguard widgets. Has no effect starting from
     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} since keyguard widget is only supported
     * on Android versions lower than 5.0.
     * Disable all keyguard widgets. Has no effect between {@link
     * android.os.Build.VERSION_CODES#LOLLIPOP} and {@link
     * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} (both inclusive), since keyguard widget is
     * only supported on Android versions lower than 5.0 and versions higher than 14.
     */
    public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1 << 0;
@@ -7157,7 +7158,8 @@ public class DevicePolicyManager {
    public static final int ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY =
            DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
                    | DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
                    | DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL;
                    | DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
                    | DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL;
    /**
     * Keyguard features that when set on a normal or organization-owned managed profile, have
@@ -8978,6 +8980,10 @@ public class DevicePolicyManager {
     * by applications in the managed profile.
     * </ul>
     * <p>
     * From version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, the profile owner of a
     * managed profile can also set {@link #KEYGUARD_DISABLE_WIDGETS_ALL} which disables keyguard
     * widgets for the managed profile.
     * <p>
     * From version {@link android.os.Build.VERSION_CODES#R} the profile owner of an
     * organization-owned managed profile can set:
     * <ul>
@@ -8986,6 +8992,12 @@ public class DevicePolicyManager {
     * <li>{@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS} which affects the parent user when called
     * on the parent profile.
     * </ul>
     * Starting from version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} the profile
     * owner of an organization-owned managed profile can set:
     * <ul>
     * <li>{@link #KEYGUARD_DISABLE_WIDGETS_ALL} which affects the parent user when called on the
     * parent profile.
     * </ul>
     * {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, {@link #KEYGUARD_DISABLE_FINGERPRINT},
     * {@link #KEYGUARD_DISABLE_FACE}, {@link #KEYGUARD_DISABLE_IRIS},
     * {@link #KEYGUARD_DISABLE_SECURE_CAMERA} and {@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS}
+34 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.app.admin.devicePolicyManager
import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
import android.content.pm.UserInfo
import android.os.UserManager.USER_TYPE_PROFILE_MANAGED
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
@@ -59,6 +60,7 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
        setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
        setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
        setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_FEATURES_NONE)
        underTest = kosmos.communalSettingsRepository
    }

@@ -131,6 +133,30 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_DEVICE_POLICY)
        }

    @EnableFlags(FLAG_COMMUNAL_HUB)
    @Test
    fun widgetsAllowedForWorkProfile_isFalse_whenDisallowedByDevicePolicy() =
        testScope.runTest {
            val widgetsAllowedForWorkProfile by
                collectLastValue(underTest.getAllowedByDevicePolicy(WORK_PROFILE))
            assertThat(widgetsAllowedForWorkProfile).isTrue()

            setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_WIDGETS_ALL)
            assertThat(widgetsAllowedForWorkProfile).isFalse()
        }

    @EnableFlags(FLAG_COMMUNAL_HUB)
    @Test
    fun hubIsEnabled_whenDisallowedByDevicePolicyForWorkProfile() =
        testScope.runTest {
            val enabledStateForPrimaryUser by
                collectLastValue(underTest.getEnabledState(PRIMARY_USER))
            assertThat(enabledStateForPrimaryUser?.enabled).isTrue()

            setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_WIDGETS_ALL)
            assertThat(enabledStateForPrimaryUser?.enabled).isTrue()
        }

    @EnableFlags(FLAG_COMMUNAL_HUB)
    @Test
    fun hubIsDisabledByUserAndDevicePolicy() =
@@ -189,5 +215,13 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
        val PRIMARY_USER =
            UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
        val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
        val WORK_PROFILE =
            UserInfo(
                10,
                "work",
                /* iconPath= */ "",
                /* flags= */ 0,
                USER_TYPE_PROFILE_MANAGED,
            )
    }
}
+96 −5
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@

package com.android.systemui.communal.domain.interactor

import android.app.admin.DevicePolicyManager
import android.app.admin.devicePolicyManager
import android.app.smartspace.SmartspaceTarget
import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
@@ -32,6 +34,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
@@ -71,6 +74,7 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
@@ -929,7 +933,6 @@ class CommunalInteractorTest : SysuiTestCase() {
            keyguardRepository.setKeyguardShowing(true)
            keyguardRepository.setKeyguardOccluded(false)
            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
            userRepository.setSelectedUserInfo(mainUser)

            val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
            userRepository.setUserInfos(userInfos)
@@ -937,6 +940,7 @@ class CommunalInteractorTest : SysuiTestCase() {
                userInfos = userInfos,
                selectedUserIndex = 0,
            )
            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
            runCurrent()

            // Widgets available.
@@ -955,7 +959,6 @@ class CommunalInteractorTest : SysuiTestCase() {
                AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
                mainUser.id
            )
            runCurrent()

            // Only the keyguard widget is enabled.
            assertThat(widgetContent).hasSize(3)
@@ -974,7 +977,6 @@ class CommunalInteractorTest : SysuiTestCase() {
            keyguardRepository.setKeyguardShowing(true)
            keyguardRepository.setKeyguardOccluded(false)
            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
            userRepository.setSelectedUserInfo(mainUser)

            val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
            userRepository.setUserInfos(userInfos)
@@ -982,6 +984,7 @@ class CommunalInteractorTest : SysuiTestCase() {
                userInfos = userInfos,
                selectedUserIndex = 0,
            )
            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
            runCurrent()

            // Widgets available.
@@ -1001,7 +1004,6 @@ class CommunalInteractorTest : SysuiTestCase() {
                    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
                mainUser.id
            )
            runCurrent()

            // All widgets are enabled.
            assertThat(widgetContent).hasSize(3)
@@ -1011,6 +1013,79 @@ class CommunalInteractorTest : SysuiTestCase() {
            }
        }

    @Test
    fun filterWidgets_whenDisallowedByDevicePolicyForWorkProfile() =
        testScope.runTest {
            // Keyguard showing, and tutorial completed.
            keyguardRepository.setKeyguardShowing(true)
            keyguardRepository.setKeyguardOccluded(false)
            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)

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

            val widgetContent by collectLastValue(underTest.widgetContent)
            // Given three widgets, and one of them is associated with work profile.
            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
            val widgets = listOf(widget1, widget2, widget3)
            widgetRepository.setCommunalWidgets(widgets)

            setKeyguardFeaturesDisabled(
                USER_INFO_WORK,
                DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
            )

            // Widget under work profile is filtered out and the remaining two link to main user id.
            assertThat(widgetContent).hasSize(2)
            widgetContent!!.forEach { model ->
                assertThat(model.providerInfo.profile?.identifier).isEqualTo(MAIN_USER_INFO.id)
            }
        }

    @Test
    fun filterWidgets_whenAllowedByDevicePolicyForWorkProfile() =
        testScope.runTest {
            // Keyguard showing, and tutorial completed.
            keyguardRepository.setKeyguardShowing(true)
            keyguardRepository.setKeyguardOccluded(false)
            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)

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

            val widgetContent by collectLastValue(underTest.widgetContent)
            // Given three widgets, and one of them is associated with work profile.
            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
            val widgets = listOf(widget1, widget2, widget3)
            widgetRepository.setCommunalWidgets(widgets)

            setKeyguardFeaturesDisabled(
                USER_INFO_WORK,
                DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
            )

            // Widget under work profile is available.
            assertThat(widgetContent).hasSize(3)
            assertThat(widgetContent!![0].providerInfo.profile?.identifier)
                .isEqualTo(USER_INFO_WORK.id)
        }

    private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget {
        val timer = mock(SmartspaceTarget::class.java)
        whenever(timer.smartspaceTargetId).thenReturn(id)
@@ -1020,6 +1095,15 @@ class CommunalInteractorTest : SysuiTestCase() {
        return timer
    }

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

    private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel =
        mock<CommunalWidgetContentModel> {
            whenever(this.appWidgetId).thenReturn(appWidgetId)
@@ -1044,6 +1128,13 @@ class CommunalInteractorTest : SysuiTestCase() {

    private companion object {
        val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
        val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
        val USER_INFO_WORK =
            UserInfo(
                10,
                "work",
                /* iconPath= */ "",
                /* flags= */ 0,
                UserManager.USER_TYPE_PROFILE_MANAGED,
            )
    }
}
+13 −10
Original line number Diff line number Diff line
@@ -57,6 +57,9 @@ interface CommunalSettingsRepository {
     * Settings.
     */
    fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories>

    /** Keyguard widgets enabled state by Device Policy Manager for the specified user. */
    fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean>
}

@SysUISingleton
@@ -115,6 +118,16 @@ constructor(
            }
            .flowOn(bgDispatcher)

    override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
        broadcastDispatcher
            .broadcastFlow(
                filter =
                    IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
                user = user.userHandle
            )
            .emitOnStart()
            .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }

    private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
        secureSettings
            .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.GLANCEABLE_HUB_ENABLED))
@@ -128,16 +141,6 @@ constructor(
                ) == 1
            }

    private fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
        broadcastDispatcher
            .broadcastFlow(
                filter =
                    IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
                user = user.userHandle
            )
            .emitOnStart()
            .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }

    companion object {
        const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
        private const val ENABLED_SETTING_DEFAULT = 1
+22 −2
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ constructor(
    mediaRepository: CommunalMediaRepository,
    smartspaceRepository: SmartspaceRepository,
    keyguardInteractor: KeyguardInteractor,
    communalSettingsInteractor: CommunalSettingsInteractor,
    private val communalSettingsInteractor: CommunalSettingsInteractor,
    private val appWidgetHost: CommunalAppWidgetHost,
    private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
    private val userTracker: UserTracker,
@@ -358,7 +358,14 @@ constructor(
    /** A list of widget content to be displayed in the communal hub. */
    val widgetContent: Flow<List<WidgetContent>> =
        combine(
            widgetRepository.communalWidgets.map { filterWidgetsByExistingUsers(it) },
            widgetRepository.communalWidgets
                .map { filterWidgetsByExistingUsers(it) }
                .combine(communalSettingsInteractor.allowedByDevicePolicyForWorkProfile) {
                    // exclude widgets under work profile if not allowed by device policy
                    widgets,
                    allowedForWorkProfile ->
                    filterWidgetsAllowedByDevicePolicy(widgets, allowedForWorkProfile)
                },
            communalSettingsInteractor.communalWidgetCategories,
            updateOnWorkProfileBroadcastReceived,
        ) { widgets, allowedCategories, _ ->
@@ -380,6 +387,19 @@ constructor(
            }
        }

    /** Filter widgets based on whether their associated profile is allowed by device policy. */
    private fun filterWidgetsAllowedByDevicePolicy(
        list: List<CommunalWidgetContentModel>,
        allowedByDevicePolicyForWorkProfile: Boolean
    ): List<CommunalWidgetContentModel> =
        if (allowedByDevicePolicyForWorkProfile) {
            list
        } else {
            // Get associated work profile for the currently selected user.
            val workProfile = userTracker.userProfiles.find { it.isManagedProfile }
            list.filter { it.providerInfo.profile.identifier != workProfile?.id }
        }

    /** A flow of available smartspace targets. Currently only showing timers. */
    private val smartspaceTargets: Flow<List<SmartspaceTarget>> =
        if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) {
Loading