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