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