1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package com.android.systemui.keyguard.domain.interactor 19 20 import android.app.admin.DevicePolicyManager 21 import android.content.Intent 22 import android.os.UserHandle 23 import androidx.test.filters.FlakyTest 24 import androidx.test.filters.SmallTest 25 import com.android.internal.widget.LockPatternUtils 26 import com.android.systemui.SysuiTestCase 27 import com.android.systemui.animation.ActivityTransitionAnimator 28 import com.android.systemui.animation.DialogTransitionAnimator 29 import com.android.systemui.animation.Expandable 30 import com.android.systemui.common.shared.model.ContentDescription 31 import com.android.systemui.common.shared.model.Icon 32 import com.android.systemui.dock.DockManagerFake 33 import com.android.systemui.flags.EnableSceneContainer 34 import com.android.systemui.flags.FakeFeatureFlags 35 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys 36 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig 37 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory 38 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig 39 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer 40 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager 41 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager 42 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository 43 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository 44 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition 45 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger 46 import com.android.systemui.plugins.ActivityStarter 47 import com.android.systemui.scene.domain.interactor.sceneInteractor 48 import com.android.systemui.settings.FakeUserTracker 49 import com.android.systemui.settings.UserFileManager 50 import com.android.systemui.settings.UserTracker 51 import com.android.systemui.shade.domain.interactor.shadeInteractor 52 import com.android.systemui.statusbar.policy.KeyguardStateController 53 import com.android.systemui.testKosmos 54 import com.android.systemui.util.FakeSharedPreferences 55 import com.android.systemui.util.mockito.any 56 import com.android.systemui.util.mockito.mock 57 import com.android.systemui.util.mockito.whenever 58 import com.android.systemui.util.settings.FakeSettings 59 import kotlinx.coroutines.CoroutineScope 60 import kotlinx.coroutines.Dispatchers 61 import kotlinx.coroutines.ExperimentalCoroutinesApi 62 import kotlinx.coroutines.test.StandardTestDispatcher 63 import kotlinx.coroutines.test.TestScope 64 import kotlinx.coroutines.test.runTest 65 import org.junit.Before 66 import platform.test.runner.parameterized.ParameterizedAndroidJunit4 67 import platform.test.runner.parameterized.Parameters 68 import platform.test.runner.parameterized.Parameter 69 import org.junit.Test 70 import org.junit.runner.RunWith 71 import org.mockito.ArgumentMatchers.anyInt 72 import org.mockito.ArgumentMatchers.anyString 73 import org.mockito.ArgumentMatchers.eq 74 import org.mockito.ArgumentMatchers.same 75 import org.mockito.Mock 76 import org.mockito.Mockito.verify 77 import org.mockito.Mockito.verifyZeroInteractions 78 import org.mockito.MockitoAnnotations 79 80 @OptIn(ExperimentalCoroutinesApi::class) 81 @FlakyTest( 82 bugId = 292574995, 83 detail = "on certain architectures all permutations with startActivity=true is causing failures" 84 ) 85 @SmallTest 86 @RunWith(ParameterizedAndroidJunit4::class) 87 @EnableSceneContainer 88 class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() { 89 90 companion object { 91 private val INTENT = Intent("some.intent.action") 92 private val DRAWABLE = <lambda>null93 mock<Icon> { 94 whenever(this.contentDescription) 95 .thenReturn( 96 ContentDescription.Resource( 97 res = CONTENT_DESCRIPTION_RESOURCE_ID, 98 ) 99 ) 100 } 101 private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337 102 103 @Parameters( 104 name = 105 "needStrongAuthAfterBoot={0}, canShowWhileLocked={1}," + 106 " keyguardIsUnlocked={2}, needsToUnlockFirst={3}, startActivity={4}" 107 ) 108 @JvmStatic datanull109 fun data() = 110 listOf( 111 arrayOf( 112 /* needStrongAuthAfterBoot= */ false, 113 /* canShowWhileLocked= */ false, 114 /* keyguardIsUnlocked= */ false, 115 /* needsToUnlockFirst= */ true, 116 /* startActivity= */ false, 117 ), 118 arrayOf( 119 /* needStrongAuthAfterBoot= */ false, 120 /* canShowWhileLocked= */ false, 121 /* keyguardIsUnlocked= */ true, 122 /* needsToUnlockFirst= */ false, 123 /* startActivity= */ false, 124 ), 125 arrayOf( 126 /* needStrongAuthAfterBoot= */ false, 127 /* canShowWhileLocked= */ true, 128 /* keyguardIsUnlocked= */ false, 129 /* needsToUnlockFirst= */ false, 130 /* startActivity= */ false, 131 ), 132 arrayOf( 133 /* needStrongAuthAfterBoot= */ false, 134 /* canShowWhileLocked= */ true, 135 /* keyguardIsUnlocked= */ true, 136 /* needsToUnlockFirst= */ false, 137 /* startActivity= */ false, 138 ), 139 arrayOf( 140 /* needStrongAuthAfterBoot= */ true, 141 /* canShowWhileLocked= */ false, 142 /* keyguardIsUnlocked= */ false, 143 /* needsToUnlockFirst= */ true, 144 /* startActivity= */ false, 145 ), 146 arrayOf( 147 /* needStrongAuthAfterBoot= */ true, 148 /* canShowWhileLocked= */ false, 149 /* keyguardIsUnlocked= */ true, 150 /* needsToUnlockFirst= */ true, 151 /* startActivity= */ false, 152 ), 153 arrayOf( 154 /* needStrongAuthAfterBoot= */ true, 155 /* canShowWhileLocked= */ true, 156 /* keyguardIsUnlocked= */ false, 157 /* needsToUnlockFirst= */ true, 158 /* startActivity= */ false, 159 ), 160 arrayOf( 161 /* needStrongAuthAfterBoot= */ true, 162 /* canShowWhileLocked= */ true, 163 /* keyguardIsUnlocked= */ true, 164 /* needsToUnlockFirst= */ true, 165 /* startActivity= */ false, 166 ), 167 arrayOf( 168 /* needStrongAuthAfterBoot= */ false, 169 /* canShowWhileLocked= */ false, 170 /* keyguardIsUnlocked= */ false, 171 /* needsToUnlockFirst= */ true, 172 /* startActivity= */ true, 173 ), 174 arrayOf( 175 /* needStrongAuthAfterBoot= */ false, 176 /* canShowWhileLocked= */ false, 177 /* keyguardIsUnlocked= */ true, 178 /* needsToUnlockFirst= */ false, 179 /* startActivity= */ true, 180 ), 181 arrayOf( 182 /* needStrongAuthAfterBoot= */ false, 183 /* canShowWhileLocked= */ true, 184 /* keyguardIsUnlocked= */ false, 185 /* needsToUnlockFirst= */ false, 186 /* startActivity= */ true, 187 ), 188 arrayOf( 189 /* needStrongAuthAfterBoot= */ false, 190 /* canShowWhileLocked= */ true, 191 /* keyguardIsUnlocked= */ true, 192 /* needsToUnlockFirst= */ false, 193 /* startActivity= */ true, 194 ), 195 arrayOf( 196 /* needStrongAuthAfterBoot= */ true, 197 /* canShowWhileLocked= */ false, 198 /* keyguardIsUnlocked= */ false, 199 /* needsToUnlockFirst= */ true, 200 /* startActivity= */ true, 201 ), 202 arrayOf( 203 /* needStrongAuthAfterBoot= */ true, 204 /* canShowWhileLocked= */ false, 205 /* keyguardIsUnlocked= */ true, 206 /* needsToUnlockFirst= */ true, 207 /* startActivity= */ true, 208 ), 209 arrayOf( 210 /* needStrongAuthAfterBoot= */ true, 211 /* canShowWhileLocked= */ true, 212 /* keyguardIsUnlocked= */ false, 213 /* needsToUnlockFirst= */ true, 214 /* startActivity= */ true, 215 ), 216 arrayOf( 217 /* needStrongAuthAfterBoot= */ true, 218 /* canShowWhileLocked= */ true, 219 /* keyguardIsUnlocked= */ true, 220 /* needsToUnlockFirst= */ true, 221 /* startActivity= */ true, 222 ), 223 ) 224 225 private val IMMEDIATE = Dispatchers.Main.immediate 226 } 227 228 @Mock private lateinit var lockPatternUtils: LockPatternUtils 229 @Mock private lateinit var keyguardStateController: KeyguardStateController 230 @Mock private lateinit var activityStarter: ActivityStarter 231 @Mock private lateinit var animationController: ActivityTransitionAnimator.Controller 232 @Mock private lateinit var expandable: Expandable 233 @Mock private lateinit var launchAnimator: DialogTransitionAnimator 234 @Mock private lateinit var devicePolicyManager: DevicePolicyManager 235 @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger 236 237 private lateinit var underTest: KeyguardQuickAffordanceInteractor 238 private lateinit var testScope: TestScope 239 240 @JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false 241 @JvmField @Parameter(1) var canShowWhileLocked: Boolean = false 242 @JvmField @Parameter(2) var keyguardIsUnlocked: Boolean = false 243 @JvmField @Parameter(3) var needsToUnlockFirst: Boolean = false 244 @JvmField @Parameter(4) var startActivity: Boolean = false 245 private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig 246 private lateinit var dockManager: DockManagerFake 247 private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository 248 private lateinit var userTracker: UserTracker 249 250 private val kosmos = testKosmos() 251 252 @Before 253 fun setUp() { 254 MockitoAnnotations.initMocks(this) 255 whenever(expandable.activityTransitionController()).thenReturn(animationController) 256 257 userTracker = FakeUserTracker() 258 homeControls = 259 FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS) 260 dockManager = DockManagerFake() 261 biometricSettingsRepository = FakeBiometricSettingsRepository() 262 val quickAccessWallet = 263 FakeKeyguardQuickAffordanceConfig( 264 BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET 265 ) 266 val qrCodeScanner = 267 FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER) 268 val scope = CoroutineScope(IMMEDIATE) 269 val localUserSelectionManager = 270 KeyguardQuickAffordanceLocalUserSelectionManager( 271 context = context, 272 userFileManager = 273 mock<UserFileManager>().apply { 274 whenever( 275 getSharedPreferences( 276 anyString(), 277 anyInt(), 278 anyInt(), 279 ) 280 ) 281 .thenReturn(FakeSharedPreferences()) 282 }, 283 userTracker = userTracker, 284 systemSettings = FakeSettings(), 285 broadcastDispatcher = fakeBroadcastDispatcher, 286 ) 287 val remoteUserSelectionManager = 288 KeyguardQuickAffordanceRemoteUserSelectionManager( 289 scope = scope, 290 userTracker = userTracker, 291 clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker), 292 userHandle = UserHandle.SYSTEM, 293 ) 294 val quickAffordanceRepository = 295 KeyguardQuickAffordanceRepository( 296 appContext = context, 297 scope = scope, 298 localUserSelectionManager = localUserSelectionManager, 299 remoteUserSelectionManager = remoteUserSelectionManager, 300 userTracker = userTracker, 301 legacySettingSyncer = 302 KeyguardQuickAffordanceLegacySettingSyncer( 303 scope = scope, 304 backgroundDispatcher = IMMEDIATE, 305 secureSettings = FakeSettings(), 306 selectionsManager = localUserSelectionManager, 307 ), 308 configs = setOf(homeControls, quickAccessWallet, qrCodeScanner), 309 dumpManager = mock(), 310 userHandle = UserHandle.SYSTEM, 311 ) 312 val featureFlags = FakeFeatureFlags() 313 val testDispatcher = StandardTestDispatcher() 314 testScope = TestScope(testDispatcher) 315 underTest = 316 KeyguardQuickAffordanceInteractor( 317 keyguardInteractor = 318 KeyguardInteractorFactory.create( 319 featureFlags = featureFlags, 320 ) 321 .keyguardInteractor, 322 shadeInteractor = kosmos.shadeInteractor, 323 lockPatternUtils = lockPatternUtils, 324 keyguardStateController = keyguardStateController, 325 userTracker = userTracker, 326 activityStarter = activityStarter, 327 featureFlags = featureFlags, 328 repository = { quickAffordanceRepository }, 329 launchAnimator = launchAnimator, 330 logger = logger, 331 devicePolicyManager = devicePolicyManager, 332 dockManager = dockManager, 333 biometricSettingsRepository = biometricSettingsRepository, 334 backgroundDispatcher = testDispatcher, 335 appContext = mContext, 336 sceneInteractor = { kosmos.sceneInteractor }, 337 ) 338 } 339 340 @Test onQuickAffordanceTriggerednull341 fun onQuickAffordanceTriggered() = 342 testScope.runTest { 343 val key = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS 344 setUpMocks( 345 needStrongAuthAfterBoot = needStrongAuthAfterBoot, 346 keyguardIsUnlocked = keyguardIsUnlocked, 347 ) 348 349 homeControls.setState( 350 lockScreenState = 351 KeyguardQuickAffordanceConfig.LockScreenState.Visible( 352 icon = DRAWABLE, 353 ) 354 ) 355 homeControls.onTriggeredResult = 356 if (startActivity) { 357 KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity( 358 intent = INTENT, 359 canShowWhileLocked = canShowWhileLocked, 360 ) 361 } else { 362 KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled 363 } 364 365 underTest.onQuickAffordanceTriggered( 366 configKey = "${KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()}::$key", 367 expandable = expandable, 368 slotId = "", 369 ) 370 371 if (startActivity) { 372 if (needsToUnlockFirst) { 373 verify(activityStarter) 374 .postStartActivityDismissingKeyguard( 375 any(), 376 /* delay= */ eq(0), 377 same(animationController), 378 ) 379 } else { 380 verify(activityStarter) 381 .startActivity( 382 any(), 383 /* dismissShade= */ eq(true), 384 same(animationController), 385 /* showOverLockscreenWhenLocked= */ eq(true), 386 ) 387 } 388 } else { 389 verifyZeroInteractions(activityStarter) 390 } 391 } 392 setUpMocksnull393 private fun setUpMocks( 394 needStrongAuthAfterBoot: Boolean = true, 395 keyguardIsUnlocked: Boolean = false, 396 ) { 397 whenever(lockPatternUtils.getStrongAuthForUser(any())) 398 .thenReturn( 399 if (needStrongAuthAfterBoot) { 400 LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT 401 } else { 402 LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED 403 } 404 ) 405 whenever(keyguardStateController.isUnlocked).thenReturn(keyguardIsUnlocked) 406 } 407 } 408