diff options
| author | 2023-02-17 06:27:13 +0000 | |
|---|---|---|
| committer | 2023-02-17 06:27:13 +0000 | |
| commit | ab0e99767aea12f9f80b385958a45ae71d7c5730 (patch) | |
| tree | 50e7bbe92d27d64773ca72d1dd38d0a817750c6d | |
| parent | 29073686b9652c814774e3e20aba4f52127a968c (diff) | |
| parent | fac8e2cf0018ad29ca359db6f11063b669ced59e (diff) | |
Merge "Respects device policy to disble quick affordances" into tm-qpr-dev am: 06acd50977 am: fac8e2cf00
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21396218
Change-Id: I431cbe3c1cce2331b7bf0315c0acff66bc96e285
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
7 files changed, 175 insertions, 55 deletions
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md index 79d5718efe0d..d662649ac419 100644 --- a/packages/SystemUI/docs/device-entry/quickaffordance.md +++ b/packages/SystemUI/docs/device-entry/quickaffordance.md @@ -52,6 +52,11 @@ A picker experience may: * Unselect an already-selected quick affordance from a slot * Unselect all already-selected quick affordances from a slot +## Device Policy +Returning `DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL` or +`DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL` from +`DevicePolicyManager#getKeyguardDisabledFeatures` will disable the keyguard quick affordance feature on the device. + ## Testing * Add a unit test for your implementation of `KeyguardQuickAffordanceConfig` * Manually verify that your implementation works in multi-user environments from both the main user and a secondary user diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt index 680c504d50fc..79de6bf9ada0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt @@ -131,7 +131,7 @@ class CustomizationProvider : throw UnsupportedOperationException() } - return insertSelection(values) + return runBlocking { insertSelection(values) } } override fun query( @@ -171,7 +171,7 @@ class CustomizationProvider : throw UnsupportedOperationException() } - return deleteSelection(uri, selectionArgs) + return runBlocking { deleteSelection(uri, selectionArgs) } } override fun call(method: String, arg: String?, extras: Bundle?): Bundle? { @@ -189,7 +189,7 @@ class CustomizationProvider : } } - private fun insertSelection(values: ContentValues?): Uri? { + private suspend fun insertSelection(values: ContentValues?): Uri? { if (values == null) { throw IllegalArgumentException("Cannot insert selection, no values passed in!") } @@ -311,7 +311,7 @@ class CustomizationProvider : } } - private fun querySlots(): Cursor { + private suspend fun querySlots(): Cursor { return MatrixCursor( arrayOf( Contract.LockScreenQuickAffordances.SlotTable.Columns.ID, @@ -330,7 +330,7 @@ class CustomizationProvider : } } - private fun queryFlags(): Cursor { + private suspend fun queryFlags(): Cursor { return MatrixCursor( arrayOf( Contract.FlagsTable.Columns.NAME, @@ -353,7 +353,7 @@ class CustomizationProvider : } } - private fun deleteSelection( + private suspend fun deleteSelection( uri: Uri, selectionArgs: Array<out String>?, ): Int { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index fb0fb1fcbf7f..e70899828f3f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -18,12 +18,14 @@ package com.android.systemui.keyguard.domain.interactor import android.app.AlertDialog +import android.app.admin.DevicePolicyManager import android.content.Intent import android.util.Log import com.android.internal.widget.LockPatternUtils import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig @@ -41,13 +43,17 @@ import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext +@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class KeyguardQuickAffordanceInteractor @Inject @@ -61,6 +67,8 @@ constructor( private val featureFlags: FeatureFlags, private val repository: Lazy<KeyguardQuickAffordanceRepository>, private val launchAnimator: DialogLaunchAnimator, + private val devicePolicyManager: DevicePolicyManager, + @Background private val backgroundDispatcher: CoroutineDispatcher, ) { private val isUsingRepository: Boolean get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES) @@ -74,9 +82,13 @@ constructor( get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES) /** Returns an observable for the quick affordance at the given position. */ - fun quickAffordance( + suspend fun quickAffordance( position: KeyguardQuickAffordancePosition ): Flow<KeyguardQuickAffordanceModel> { + if (isFeatureDisabledByDevicePolicy()) { + return flowOf(KeyguardQuickAffordanceModel.Hidden) + } + return combine( quickAffordanceAlwaysVisible(position), keyguardInteractor.isDozing, @@ -148,8 +160,11 @@ constructor( * * @return `true` if the affordance was selected successfully; `false` otherwise. */ - fun select(slotId: String, affordanceId: String): Boolean { + suspend fun select(slotId: String, affordanceId: String): Boolean { check(isUsingRepository) + if (isFeatureDisabledByDevicePolicy()) { + return false + } val slots = repository.get().getSlotPickerRepresentations() val slot = slots.find { it.id == slotId } ?: return false @@ -187,8 +202,11 @@ constructor( * @return `true` if the affordance was successfully removed; `false` otherwise (for example, if * the affordance was not on the slot to begin with). */ - fun unselect(slotId: String, affordanceId: String?): Boolean { + suspend fun unselect(slotId: String, affordanceId: String?): Boolean { check(isUsingRepository) + if (isFeatureDisabledByDevicePolicy()) { + return false + } val slots = repository.get().getSlotPickerRepresentations() if (slots.find { it.id == slotId } == null) { @@ -227,6 +245,10 @@ constructor( /** Returns affordance IDs indexed by slot ID, for all known slots. */ suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> { + if (isFeatureDisabledByDevicePolicy()) { + return emptyMap() + } + val slots = repository.get().getSlotPickerRepresentations() val selections = repository.get().getCurrentSelections() val affordanceById = @@ -351,13 +373,17 @@ constructor( return repository.get().getAffordancePickerRepresentations() } - fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> { + suspend fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> { check(isUsingRepository) + if (isFeatureDisabledByDevicePolicy()) { + return emptyList() + } + return repository.get().getSlotPickerRepresentations() } - fun getPickerFlags(): List<KeyguardPickerFlag> { + suspend fun getPickerFlags(): List<KeyguardPickerFlag> { return listOf( KeyguardPickerFlag( name = Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI, @@ -365,7 +391,9 @@ constructor( ), KeyguardPickerFlag( name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED, - value = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES), + value = + !isFeatureDisabledByDevicePolicy() && + featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES), ), KeyguardPickerFlag( name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED, @@ -382,6 +410,16 @@ constructor( ) } + private suspend fun isFeatureDisabledByDevicePolicy(): Boolean { + val flags = + withContext(backgroundDispatcher) { + devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) + } + val flagsToCheck = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL + // TODO(b/268218507): "or" with DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL + return flagsToCheck and flags != 0 + } + companion object { private const val TAG = "KeyguardQuickAffordanceInteractor" private const val DELIMITER = "::" diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt index 15a454b3a24e..f8bd48c79f1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard +import android.app.admin.DevicePolicyManager import android.content.ContentValues import android.content.pm.PackageManager import android.content.pm.ProviderInfo @@ -90,6 +91,7 @@ class CustomizationProviderTest : SysuiTestCase() { @Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage @Mock private lateinit var launchAnimator: DialogLaunchAnimator @Mock private lateinit var commandQueue: CommandQueue + @Mock private lateinit var devicePolicyManager: DevicePolicyManager private lateinit var underTest: CustomizationProvider private lateinit var testScope: TestScope @@ -183,6 +185,8 @@ class CustomizationProviderTest : SysuiTestCase() { featureFlags = featureFlags, repository = { quickAffordanceRepository }, launchAnimator = launchAnimator, + devicePolicyManager = devicePolicyManager, + backgroundDispatcher = testDispatcher, ) underTest.previewManager = KeyguardRemotePreviewManager( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index 23e06ec181c0..84ec125bfa55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor +import android.app.admin.DevicePolicyManager import android.content.Intent import android.os.UserHandle import androidx.test.filters.SmallTest @@ -54,7 +55,10 @@ import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.FakeSettings import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -70,6 +74,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(Parameterized::class) class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @@ -219,8 +224,10 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @Mock private lateinit var expandable: Expandable @Mock private lateinit var launchAnimator: DialogLaunchAnimator @Mock private lateinit var commandQueue: CommandQueue + @Mock private lateinit var devicePolicyManager: DevicePolicyManager private lateinit var underTest: KeyguardQuickAffordanceInteractor + private lateinit var testScope: TestScope @JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false @JvmField @Parameter(1) var canShowWhileLocked: Boolean = false @@ -292,6 +299,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false) set(Flags.FACE_AUTH_REFACTOR, true) } + val testDispatcher = StandardTestDispatcher() + testScope = TestScope(testDispatcher) underTest = KeyguardQuickAffordanceInteractor( keyguardInteractor = @@ -322,58 +331,61 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { featureFlags = featureFlags, repository = { quickAffordanceRepository }, launchAnimator = launchAnimator, + devicePolicyManager = devicePolicyManager, + backgroundDispatcher = testDispatcher, ) } @Test - fun onQuickAffordanceTriggered() = runBlockingTest { - setUpMocks( - needStrongAuthAfterBoot = needStrongAuthAfterBoot, - keyguardIsUnlocked = keyguardIsUnlocked, - ) + fun onQuickAffordanceTriggered() = + testScope.runTest { + setUpMocks( + needStrongAuthAfterBoot = needStrongAuthAfterBoot, + keyguardIsUnlocked = keyguardIsUnlocked, + ) - homeControls.setState( - lockScreenState = - KeyguardQuickAffordanceConfig.LockScreenState.Visible( - icon = DRAWABLE, - ) - ) - homeControls.onTriggeredResult = - if (startActivity) { - KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity( - intent = INTENT, - canShowWhileLocked = canShowWhileLocked, - ) - } else { - KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled - } + homeControls.setState( + lockScreenState = + KeyguardQuickAffordanceConfig.LockScreenState.Visible( + icon = DRAWABLE, + ) + ) + homeControls.onTriggeredResult = + if (startActivity) { + KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity( + intent = INTENT, + canShowWhileLocked = canShowWhileLocked, + ) + } else { + KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled + } - underTest.onQuickAffordanceTriggered( - configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, - expandable = expandable, - ) + underTest.onQuickAffordanceTriggered( + configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, + expandable = expandable, + ) - if (startActivity) { - if (needsToUnlockFirst) { - verify(activityStarter) - .postStartActivityDismissingKeyguard( - any(), - /* delay= */ eq(0), - same(animationController), - ) + if (startActivity) { + if (needsToUnlockFirst) { + verify(activityStarter) + .postStartActivityDismissingKeyguard( + any(), + /* delay= */ eq(0), + same(animationController), + ) + } else { + verify(activityStarter) + .startActivity( + any(), + /* dismissShade= */ eq(true), + same(animationController), + /* showOverLockscreenWhenLocked= */ eq(true), + ) + } } else { - verify(activityStarter) - .startActivity( - any(), - /* dismissShade= */ eq(true), - same(animationController), - /* showOverLockscreenWhenLocked= */ eq(true), - ) + verifyZeroInteractions(activityStarter) } - } else { - verifyZeroInteractions(activityStarter) } - } private fun setUpMocks( needStrongAuthAfterBoot: Boolean = true, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 1b8c6273e2d8..94044ad6d7ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor +import android.app.admin.DevicePolicyManager import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -78,6 +79,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var launchAnimator: DialogLaunchAnimator @Mock private lateinit var commandQueue: CommandQueue + @Mock private lateinit var devicePolicyManager: DevicePolicyManager private lateinit var underTest: KeyguardQuickAffordanceInteractor @@ -184,6 +186,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { featureFlags = featureFlags, repository = { quickAffordanceRepository }, launchAnimator = launchAnimator, + devicePolicyManager = devicePolicyManager, + backgroundDispatcher = testDispatcher, ) } @@ -239,6 +243,25 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test + fun `quickAffordance - bottom end affordance is hidden when disabled by device policy`() = + testScope.runTest { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) + quickAccessWallet.setState( + KeyguardQuickAffordanceConfig.LockScreenState.Visible( + icon = ICON, + ) + ) + + val collectedValue by + collectLastValue( + underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END) + ) + + assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java) + } + + @Test fun `quickAffordance - bottom start affordance hidden while dozing`() = testScope.runTest { repository.setDozing(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index 6afeddda18ab..8bd8be565eee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.app.admin.DevicePolicyManager import android.content.Intent import android.os.UserHandle import androidx.test.filters.SmallTest @@ -87,6 +88,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var launchAnimator: DialogLaunchAnimator @Mock private lateinit var commandQueue: CommandQueue + @Mock private lateinit var devicePolicyManager: DevicePolicyManager private lateinit var underTest: KeyguardBottomAreaViewModel @@ -140,6 +142,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { bouncerRepository = FakeKeyguardBouncerRepository(), ) whenever(userTracker.userHandle).thenReturn(mock()) + whenever(userTracker.userId).thenReturn(10) whenever(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED) val testDispatcher = StandardTestDispatcher() @@ -205,6 +208,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { featureFlags = featureFlags, repository = { quickAffordanceRepository }, launchAnimator = launchAnimator, + devicePolicyManager = devicePolicyManager, + backgroundDispatcher = testDispatcher, ), bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository), burnInHelperWrapper = burnInHelperWrapper, @@ -240,6 +245,39 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @Test + fun `startButton - hidden when device policy disables all keyguard features`() = + testScope.runTest { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) + repository.setKeyguardShowing(true) + val latest by collectLastValue(underTest.startButton) + + val testConfig = + TestConfig( + isVisible = true, + isClickable = true, + isActivated = true, + icon = mock(), + canShowWhileLocked = false, + intent = Intent("action"), + ) + val configKey = + setUpQuickAffordanceModel( + position = KeyguardQuickAffordancePosition.BOTTOM_START, + testConfig = testConfig, + ) + + assertQuickAffordanceViewModel( + viewModel = latest, + testConfig = + TestConfig( + isVisible = false, + ), + configKey = configKey, + ) + } + + @Test fun `startButton - in preview mode - visible even when keyguard not showing`() = testScope.runTest { underTest.enablePreviewMode( |