1 /* 2 * Copyright (C) 2022 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.DisableSceneContainer 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 @SmallTest 83 @RunWith(ParameterizedAndroidJunit4::class) 84 @DisableSceneContainer 85 @FlakyTest(bugId = 292574995, detail = "NullPointer on MockMakerTypeMockability.mockable()") 86 class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { 87 88 companion object { 89 private val INTENT = Intent("some.intent.action") 90 private val DRAWABLE = <lambda>null91 mock<Icon> { 92 whenever(this.contentDescription) 93 .thenReturn(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) 94 } 95 private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337 96 97 @Parameters( 98 name = 99 "needStrongAuthAfterBoot={0}, canShowWhileLocked={1}," + 100 " keyguardIsUnlocked={2}, needsToUnlockFirst={3}, startActivity={4}" 101 ) 102 @JvmStatic datanull103 fun data() = 104 listOf( 105 arrayOf( 106 /* needStrongAuthAfterBoot= */ false, 107 /* canShowWhileLocked= */ false, 108 /* keyguardIsUnlocked= */ false, 109 /* needsToUnlockFirst= */ true, 110 /* startActivity= */ false, 111 ), 112 arrayOf( 113 /* needStrongAuthAfterBoot= */ false, 114 /* canShowWhileLocked= */ false, 115 /* keyguardIsUnlocked= */ true, 116 /* needsToUnlockFirst= */ false, 117 /* startActivity= */ false, 118 ), 119 arrayOf( 120 /* needStrongAuthAfterBoot= */ false, 121 /* canShowWhileLocked= */ true, 122 /* keyguardIsUnlocked= */ false, 123 /* needsToUnlockFirst= */ false, 124 /* startActivity= */ false, 125 ), 126 arrayOf( 127 /* needStrongAuthAfterBoot= */ false, 128 /* canShowWhileLocked= */ true, 129 /* keyguardIsUnlocked= */ true, 130 /* needsToUnlockFirst= */ false, 131 /* startActivity= */ false, 132 ), 133 arrayOf( 134 /* needStrongAuthAfterBoot= */ true, 135 /* canShowWhileLocked= */ false, 136 /* keyguardIsUnlocked= */ false, 137 /* needsToUnlockFirst= */ true, 138 /* startActivity= */ false, 139 ), 140 arrayOf( 141 /* needStrongAuthAfterBoot= */ true, 142 /* canShowWhileLocked= */ false, 143 /* keyguardIsUnlocked= */ true, 144 /* needsToUnlockFirst= */ true, 145 /* startActivity= */ false, 146 ), 147 arrayOf( 148 /* needStrongAuthAfterBoot= */ true, 149 /* canShowWhileLocked= */ true, 150 /* keyguardIsUnlocked= */ false, 151 /* needsToUnlockFirst= */ true, 152 /* startActivity= */ false, 153 ), 154 arrayOf( 155 /* needStrongAuthAfterBoot= */ true, 156 /* canShowWhileLocked= */ true, 157 /* keyguardIsUnlocked= */ true, 158 /* needsToUnlockFirst= */ true, 159 /* startActivity= */ false, 160 ), 161 arrayOf( 162 /* needStrongAuthAfterBoot= */ false, 163 /* canShowWhileLocked= */ false, 164 /* keyguardIsUnlocked= */ false, 165 /* needsToUnlockFirst= */ true, 166 /* startActivity= */ true, 167 ), 168 arrayOf( 169 /* needStrongAuthAfterBoot= */ false, 170 /* canShowWhileLocked= */ false, 171 /* keyguardIsUnlocked= */ true, 172 /* needsToUnlockFirst= */ false, 173 /* startActivity= */ true, 174 ), 175 arrayOf( 176 /* needStrongAuthAfterBoot= */ false, 177 /* canShowWhileLocked= */ true, 178 /* keyguardIsUnlocked= */ false, 179 /* needsToUnlockFirst= */ false, 180 /* startActivity= */ true, 181 ), 182 arrayOf( 183 /* needStrongAuthAfterBoot= */ false, 184 /* canShowWhileLocked= */ true, 185 /* keyguardIsUnlocked= */ true, 186 /* needsToUnlockFirst= */ false, 187 /* startActivity= */ true, 188 ), 189 arrayOf( 190 /* needStrongAuthAfterBoot= */ true, 191 /* canShowWhileLocked= */ false, 192 /* keyguardIsUnlocked= */ false, 193 /* needsToUnlockFirst= */ true, 194 /* startActivity= */ true, 195 ), 196 arrayOf( 197 /* needStrongAuthAfterBoot= */ true, 198 /* canShowWhileLocked= */ false, 199 /* keyguardIsUnlocked= */ true, 200 /* needsToUnlockFirst= */ true, 201 /* startActivity= */ true, 202 ), 203 arrayOf( 204 /* needStrongAuthAfterBoot= */ true, 205 /* canShowWhileLocked= */ true, 206 /* keyguardIsUnlocked= */ false, 207 /* needsToUnlockFirst= */ true, 208 /* startActivity= */ true, 209 ), 210 arrayOf( 211 /* needStrongAuthAfterBoot= */ true, 212 /* canShowWhileLocked= */ true, 213 /* keyguardIsUnlocked= */ true, 214 /* needsToUnlockFirst= */ true, 215 /* startActivity= */ true, 216 ), 217 ) 218 219 private val IMMEDIATE = Dispatchers.Main.immediate 220 } 221 222 @Mock private lateinit var lockPatternUtils: LockPatternUtils 223 @Mock private lateinit var keyguardStateController: KeyguardStateController 224 @Mock private lateinit var activityStarter: ActivityStarter 225 @Mock private lateinit var animationController: ActivityTransitionAnimator.Controller 226 @Mock private lateinit var expandable: Expandable 227 @Mock private lateinit var launchAnimator: DialogTransitionAnimator 228 @Mock private lateinit var devicePolicyManager: DevicePolicyManager 229 @Mock private lateinit var logger: KeyguardQuickAffordancesLogger 230 @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger 231 232 private lateinit var underTest: KeyguardQuickAffordanceInteractor 233 private lateinit var testScope: TestScope 234 235 @JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false 236 @JvmField @Parameter(1) var canShowWhileLocked: Boolean = false 237 @JvmField @Parameter(2) var keyguardIsUnlocked: Boolean = false 238 @JvmField @Parameter(3) var needsToUnlockFirst: Boolean = false 239 @JvmField @Parameter(4) var startActivity: Boolean = false 240 private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig 241 private lateinit var dockManager: DockManagerFake 242 private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository 243 private lateinit var userTracker: UserTracker 244 245 private val kosmos = testKosmos() 246 247 @Before 248 fun setUp() { 249 MockitoAnnotations.initMocks(this) 250 whenever(expandable.activityTransitionController()).thenReturn(animationController) 251 252 userTracker = FakeUserTracker() 253 homeControls = 254 FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS) 255 dockManager = DockManagerFake() 256 biometricSettingsRepository = FakeBiometricSettingsRepository() 257 val quickAccessWallet = 258 FakeKeyguardQuickAffordanceConfig( 259 BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET 260 ) 261 val qrCodeScanner = 262 FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER) 263 val scope = CoroutineScope(IMMEDIATE) 264 val localUserSelectionManager = 265 KeyguardQuickAffordanceLocalUserSelectionManager( 266 context = context, 267 userFileManager = 268 mock<UserFileManager>().apply { 269 whenever(getSharedPreferences(anyString(), anyInt(), anyInt())) 270 .thenReturn(FakeSharedPreferences()) 271 }, 272 userTracker = userTracker, 273 broadcastDispatcher = fakeBroadcastDispatcher, 274 ) 275 val remoteUserSelectionManager = 276 KeyguardQuickAffordanceRemoteUserSelectionManager( 277 scope = scope, 278 userTracker = userTracker, 279 clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker), 280 userHandle = UserHandle.SYSTEM, 281 ) 282 val quickAffordanceRepository = 283 KeyguardQuickAffordanceRepository( 284 appContext = context, 285 scope = scope, 286 localUserSelectionManager = localUserSelectionManager, 287 remoteUserSelectionManager = remoteUserSelectionManager, 288 userTracker = userTracker, 289 legacySettingSyncer = 290 KeyguardQuickAffordanceLegacySettingSyncer( 291 scope = scope, 292 backgroundDispatcher = IMMEDIATE, 293 secureSettings = FakeSettings(), 294 selectionsManager = localUserSelectionManager, 295 ), 296 configs = setOf(homeControls, quickAccessWallet, qrCodeScanner), 297 dumpManager = mock(), 298 userHandle = UserHandle.SYSTEM, 299 ) 300 val featureFlags = FakeFeatureFlags() 301 val testDispatcher = StandardTestDispatcher() 302 testScope = TestScope(testDispatcher) 303 underTest = 304 KeyguardQuickAffordanceInteractor( 305 keyguardInteractor = 306 KeyguardInteractorFactory.create(featureFlags = featureFlags) 307 .keyguardInteractor, 308 shadeInteractor = kosmos.shadeInteractor, 309 lockPatternUtils = lockPatternUtils, 310 keyguardStateController = keyguardStateController, 311 userTracker = userTracker, 312 activityStarter = activityStarter, 313 featureFlags = featureFlags, 314 repository = { quickAffordanceRepository }, 315 launchAnimator = launchAnimator, 316 logger = logger, 317 metricsLogger = metricsLogger, 318 devicePolicyManager = devicePolicyManager, 319 dockManager = dockManager, 320 biometricSettingsRepository = biometricSettingsRepository, 321 backgroundDispatcher = testDispatcher, 322 appContext = mContext, 323 sceneInteractor = { kosmos.sceneInteractor }, 324 ) 325 } 326 327 @Test onQuickAffordanceTriggerednull328 fun onQuickAffordanceTriggered() = 329 testScope.runTest { 330 val key = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS 331 setUpMocks( 332 needStrongAuthAfterBoot = needStrongAuthAfterBoot, 333 keyguardIsUnlocked = keyguardIsUnlocked, 334 ) 335 336 homeControls.setState( 337 lockScreenState = 338 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = DRAWABLE) 339 ) 340 homeControls.onTriggeredResult = 341 if (startActivity) { 342 KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity( 343 intent = INTENT, 344 canShowWhileLocked = canShowWhileLocked, 345 ) 346 } else { 347 KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled 348 } 349 350 underTest.onQuickAffordanceTriggered( 351 configKey = "${KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()}::$key", 352 expandable = expandable, 353 slotId = "", 354 ) 355 356 if (startActivity) { 357 if (needsToUnlockFirst) { 358 verify(activityStarter) 359 .postStartActivityDismissingKeyguard( 360 any(), 361 /* delay= */ eq(0), 362 same(animationController), 363 ) 364 } else { 365 verify(activityStarter) 366 .startActivity( 367 any(), 368 /* dismissShade= */ eq(true), 369 same(animationController), 370 /* showOverLockscreenWhenLocked= */ eq(true), 371 ) 372 } 373 } else { 374 verifyNoMoreInteractions(activityStarter) 375 } 376 } 377 setUpMocksnull378 private fun setUpMocks( 379 needStrongAuthAfterBoot: Boolean = true, 380 keyguardIsUnlocked: Boolean = false, 381 ) { 382 whenever(lockPatternUtils.getStrongAuthForUser(any())) 383 .thenReturn( 384 if (needStrongAuthAfterBoot) { 385 LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT 386 } else { 387 LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED 388 } 389 ) 390 whenever(keyguardStateController.isUnlocked).thenReturn(keyguardIsUnlocked) 391 } 392 } 393