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