1 package com.android.systemui.keyguard
2 
3 import android.app.ActivityManager
4 import android.app.WallpaperManager
5 import android.app.WindowConfiguration
6 import android.graphics.Point
7 import android.graphics.Rect
8 import android.os.PowerManager
9 import android.testing.AndroidTestingRunner
10 import android.testing.TestableLooper.RunWithLooper
11 import android.view.RemoteAnimationTarget
12 import android.view.SurfaceControl
13 import android.view.SyncRtSurfaceTransactionApplier
14 import android.view.View
15 import android.view.ViewRootImpl
16 import androidx.test.filters.SmallTest
17 import com.android.keyguard.KeyguardViewController
18 import com.android.systemui.SysuiTestCase
19 import com.android.systemui.flags.FeatureFlags
20 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
21 import com.android.systemui.statusbar.NotificationShadeWindowController
22 import com.android.systemui.statusbar.SysuiStatusBarStateController
23 import com.android.systemui.statusbar.phone.BiometricUnlockController
24 import com.android.systemui.statusbar.policy.KeyguardStateController
25 import com.android.systemui.util.mockito.any
26 import com.android.systemui.util.mockito.argThat
27 import com.android.systemui.util.mockito.whenever
28 import junit.framework.Assert.assertEquals
29 import junit.framework.Assert.assertFalse
30 import junit.framework.Assert.assertTrue
31 import org.junit.After
32 import org.junit.Before
33 import org.junit.Test
34 import org.junit.runner.RunWith
35 import org.mockito.Mock
36 import org.mockito.Mockito.atLeastOnce
37 import org.mockito.Mockito.eq
38 import org.mockito.Mockito.mock
39 import org.mockito.Mockito.never
40 import org.mockito.Mockito.times
41 import org.mockito.Mockito.verify
42 import org.mockito.Mockito.verifyNoMoreInteractions
43 import org.mockito.MockitoAnnotations
44 import java.util.function.Predicate
45 
46 @RunWith(AndroidTestingRunner::class)
47 @RunWithLooper
48 @SmallTest
49 class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
50     private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
51 
52     @Mock
53     private lateinit var keyguardViewMediator: KeyguardViewMediator
54     @Mock
55     private lateinit var keyguardStateController: KeyguardStateController
56     @Mock
57     private lateinit var keyguardViewController: KeyguardViewController
58     @Mock
59     private lateinit var featureFlags: FeatureFlags
60     @Mock
61     private lateinit var biometricUnlockController: BiometricUnlockController
62     @Mock
63     private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
64     @Mock
65     private lateinit var statusBarStateController: SysuiStatusBarStateController
66     @Mock
67     private lateinit var notificationShadeWindowController: NotificationShadeWindowController
68     @Mock
69     private lateinit var powerManager: PowerManager
70     @Mock
71     private lateinit var wallpaperManager: WallpaperManager
72 
73     @Mock
74     private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub
75 
76     private var surfaceControl1 = mock(SurfaceControl::class.java)
77     private var remoteTarget1 = RemoteAnimationTarget(
78             0 /* taskId */, 0, surfaceControl1, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
79             mock(WindowConfiguration::class.java), false, surfaceControl1, Rect(),
80             mock(ActivityManager.RunningTaskInfo::class.java), false)
81 
82     private var surfaceControl2 = mock(SurfaceControl::class.java)
83     private var remoteTarget2 = RemoteAnimationTarget(
84             1 /* taskId */, 0, surfaceControl2, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
85             mock(WindowConfiguration::class.java), false, surfaceControl2, Rect(),
86             mock(ActivityManager.RunningTaskInfo::class.java), false)
87     private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
88 
89     private var surfaceControlWp = mock(SurfaceControl::class.java)
90     private var wallpaperTarget = RemoteAnimationTarget(
91             2 /* taskId */, 0, surfaceControlWp, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
92             mock(WindowConfiguration::class.java), false, surfaceControlWp, Rect(),
93             mock(ActivityManager.RunningTaskInfo::class.java), false)
94     private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
95 
96     @Before
97     fun setUp() {
98         MockitoAnnotations.initMocks(this)
99         keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
100             context, keyguardStateController, { keyguardViewMediator }, keyguardViewController,
101             featureFlags, { biometricUnlockController }, statusBarStateController,
102             notificationShadeWindowController, powerManager, wallpaperManager
103         )
104         keyguardUnlockAnimationController.setLauncherUnlockController(
105             launcherUnlockAnimationController)
106 
107         whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
108         whenever(powerManager.isInteractive).thenReturn(true)
109         whenever(wallpaperManager.isLockscreenLiveWallpaperEnabled).thenReturn(false)
110 
111         // All of these fields are final, so we can't mock them, but are needed so that the surface
112         // appear amount setter doesn't short circuit.
113         remoteAnimationTargets = arrayOf(remoteTarget1)
114         wallpaperTargets = arrayOf(wallpaperTarget)
115 
116         // Set the surface applier to our mock so that we can verify the arguments passed to it.
117         // This applier does not have any side effects within the unlock animation controller, so
118         // this is a reasonable way to test.
119         keyguardUnlockAnimationController.surfaceTransactionApplier = surfaceTransactionApplier
120     }
121 
122     @After
123     fun tearDown() {
124         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
125     }
126 
127     /**
128      * If we're wake and unlocking, we are animating from the black/AOD screen to the app/launcher
129      * underneath. The LightRevealScrim will animate circularly from the fingerprint reader,
130      * revealing the app/launcher below. In this case, we want to make sure we are not animating the
131      * surface, or the user will see the wallpaper briefly as the app animates in.
132      */
133     @Test
134     fun noSurfaceAnimation_ifWakeAndUnlocking() {
135         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
136 
137         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
138             remoteAnimationTargets,
139             arrayOf(),
140             0 /* startTime */,
141             false /* requestedShowSurfaceBehindKeyguard */
142         )
143 
144         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
145         verify(surfaceTransactionApplier, times(1)).scheduleApply(
146                 captorSb.capture { sp -> sp.surface == surfaceControl1 })
147 
148         val params = captorSb.getLastValue()
149 
150         // We expect that we've instantly set the surface behind to alpha = 1f, and have no
151         // transforms (translate, scale) on its matrix.
152         assertEquals(1f, params.alpha)
153         assertTrue(params.matrix.isIdentity)
154 
155         // Also expect we've immediately asked the keyguard view mediator to finish the remote
156         // animation.
157         verify(keyguardViewMediator, times(1)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
158             false /* cancelled */)
159 
160         verifyNoMoreInteractions(surfaceTransactionApplier)
161     }
162 
163     /**
164      * If we are not wake and unlocking, we expect the unlock animation to play normally.
165      */
166     @Test
167     fun surfaceAnimation_ifNotWakeAndUnlocking() {
168         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
169 
170         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
171             remoteAnimationTargets,
172             wallpaperTargets,
173             0 /* startTime */,
174             false /* requestedShowSurfaceBehindKeyguard */
175         )
176 
177         // Since the animation is running, we should not have finished the remote animation.
178         verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
179             false /* cancelled */)
180     }
181 
182     @Test
183     fun onWakeAndUnlock_notifiesListenerWithTrue() {
184         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
185         whenever(biometricUnlockController.mode).thenReturn(
186             BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
187 
188         val listener = mock(
189             KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
190         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener)
191 
192         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
193             remoteAnimationTargets,
194             wallpaperTargets,
195             0 /* startTime */,
196             false /* requestedShowSurfaceBehindKeyguard */
197         )
198 
199         verify(listener).onUnlockAnimationStarted(any(), eq(true), any(), any())
200     }
201 
202     @Test
203     fun onWakeAndUnlockFromDream_notifiesListenerWithFalse() {
204         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
205         whenever(biometricUnlockController.mode).thenReturn(
206             BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
207 
208         val listener = mock(
209             KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
210         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener)
211 
212         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
213             remoteAnimationTargets,
214             wallpaperTargets,
215             0 /* startTime */,
216             false /* requestedShowSurfaceBehindKeyguard */
217         )
218 
219         verify(listener).onUnlockAnimationStarted(any(), eq(false), any(), any())
220     }
221 
222     /**
223      * If we requested that the surface behind be made visible, and we're not flinging away the
224      * keyguard, it means that we're swiping to unlock and want the surface visible so it can follow
225      * the user's touch event as they swipe to unlock.
226      *
227      * In this case, we should verify that the surface was made visible via the alpha fade in
228      * animator, and verify that we did not start the canned animation to animate the surface in
229      * (since it's supposed to be following the touch events).
230      */
231     @Test
232     fun fadeInSurfaceBehind_ifRequestedShowSurface_butNotFlinging() {
233         whenever(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false)
234 
235         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
236             remoteAnimationTargets,
237             wallpaperTargets,
238             0 /* startTime */,
239             true /* requestedShowSurfaceBehindKeyguard */
240         )
241 
242         assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
243         assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
244     }
245 
246     /**
247      * We requested the surface behind to be made visible, but we're now flinging to dismiss the
248      * keyguard. This means this was a swipe to dismiss gesture but the user flung the keyguard and
249      * lifted their finger while we were requesting the surface be made visible.
250      *
251      * In this case, we should verify that we are playing the canned unlock animation and not
252      * simply fading in the surface.
253      */
254     @Test
255     fun playCannedUnlockAnimation_ifRequestedShowSurface_andFlinging() {
256         whenever(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true)
257 
258         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
259             remoteAnimationTargets,
260             wallpaperTargets,
261             0 /* startTime */,
262             true /* requestedShowSurfaceBehindKeyguard */
263         )
264 
265         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
266         assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
267     }
268 
269     /**
270      * We never requested the surface behind to be made visible, which means no swiping to unlock
271      * ever happened and we're just playing the simple canned animation (happens via UDFPS unlock,
272      * long press on the lock icon, etc).
273      *
274      * In this case, we should verify that we are playing the canned unlock animation and not
275      * simply fading in the surface.
276      */
277     @Test
278     fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() {
279         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
280             remoteAnimationTargets,
281             wallpaperTargets,
282             0 /* startTime */,
283             false /* requestedShowSurfaceBehindKeyguard */
284         )
285 
286         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
287         assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
288     }
289 
290     @Test
291     fun doNotPlayCannedUnlockAnimation_ifLaunchingApp() {
292         whenever(notificationShadeWindowController.isLaunchingActivity).thenReturn(true)
293 
294         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
295             remoteAnimationTargets,
296             wallpaperTargets,
297             0 /* startTime */,
298             true /* requestedShowSurfaceBehindKeyguard */
299         )
300 
301         assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
302         assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
303     }
304 
305     @Test
306     fun playCannedUnlockAnimation_nullSmartspaceView_doesNotThrowExecption() {
307         keyguardUnlockAnimationController.lockscreenSmartspace = null
308         keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true
309 
310         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
311                 remoteAnimationTargets,
312                 wallpaperTargets,
313                 0 /* startTime */,
314                 false /* requestedShowSurfaceBehindKeyguard */
315         )
316 
317         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
318     }
319 
320     /**
321      * If we are not wake and unlocking, we expect the unlock animation to play normally.
322      */
323     @Test
324     fun surfaceAnimation_multipleTargets() {
325         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
326                 arrayOf(remoteTarget1, remoteTarget2),
327                 wallpaperTargets,
328                 0 /* startTime */,
329                 false /* requestedShowSurfaceBehindKeyguard */
330         )
331 
332         // Set appear to 50%, we'll just verify that we're not applying the identity matrix which
333         // means an animation is in progress.
334         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
335 
336         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
337         verify(surfaceTransactionApplier, times(2)).scheduleApply(captorSb
338                 .capture { sp -> sp.surface == surfaceControl1 || sp.surface == surfaceControl2 })
339         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
340         verify(surfaceTransactionApplier, times(1).description(
341                 "WallpaperSurface was expected to receive scheduleApply once"
342         )).scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp})
343 
344         val allParams = captorSb.getAllValues()
345 
346         val remainingTargets = mutableListOf(surfaceControl1, surfaceControl2)
347         allParams.forEach { params ->
348             assertTrue(!params.matrix.isIdentity)
349             remainingTargets.remove(params.surface)
350         }
351 
352         // Make sure we called applyParams with each of the surface controls once. The order does
353         // not matter, so don't explicitly check for that.
354         assertTrue(remainingTargets.isEmpty())
355 
356         // Since the animation is running, we should not have finished the remote animation.
357         verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
358                 false /* cancelled */)
359     }
360 
361     @Test
362     fun surfaceBehindAlphaOverriddenTo0_ifNotInteractive() {
363         whenever(powerManager.isInteractive).thenReturn(false)
364 
365         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
366                 remoteAnimationTargets,
367                 wallpaperTargets,
368                 0 /* startTime */,
369                 false /* requestedShowSurfaceBehindKeyguard */
370         )
371 
372         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
373         keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
374 
375         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
376         verify(surfaceTransactionApplier, times(1)).scheduleApply(
377                 captorSb.capture { sp -> sp.surface == surfaceControl1})
378         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
379         verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has  not " +
380                 "received scheduleApply")).scheduleApply(
381                 captorWp.capture { sp -> sp.surface == surfaceControlWp })
382 
383         val params = captorSb.getLastValue()
384 
385         // We expect that we've set the surface behind to alpha = 0f since we're not interactive.
386         assertEquals(0f, params.alpha)
387         assertTrue(params.matrix.isIdentity)
388 
389         verifyNoMoreInteractions(surfaceTransactionApplier)
390     }
391 
392     @Test
393     fun surfaceBehindAlphaNotOverriddenTo0_ifInteractive() {
394         whenever(powerManager.isInteractive).thenReturn(true)
395 
396         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
397                 remoteAnimationTargets,
398                 wallpaperTargets,
399                 0 /* startTime */,
400                 false /* requestedShowSurfaceBehindKeyguard */
401         )
402 
403         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
404         keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
405 
406         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
407         verify(surfaceTransactionApplier, times(1)).scheduleApply(
408                 captorSb.capture { sp -> sp.surface == surfaceControl1 })
409         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
410         verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has  not " +
411                 "received scheduleApply")).scheduleApply(
412                 captorWp.capture { sp -> sp.surface == surfaceControlWp })
413 
414         val params = captorSb.getLastValue()
415         assertEquals(1f, params.alpha)
416         assertTrue(params.matrix.isIdentity)
417         assertEquals("Wallpaper surface was expected to have opacity 1",
418                 1f, captorWp.getLastValue().alpha)
419 
420         verifyNoMoreInteractions(surfaceTransactionApplier)
421     }
422 
423     @Test
424     fun unlockToLauncherWithInWindowAnimations_ssViewIsVisible() {
425         val mockLockscreenSmartspaceView = mock(View::class.java)
426         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE)
427         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
428 
429         keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
430 
431         verify(mockLockscreenSmartspaceView).visibility = View.INVISIBLE
432     }
433 
434     @Test
435     fun unlockToLauncherWithInWindowAnimations_ssViewIsInvisible() {
436         val mockLockscreenSmartspaceView = mock(View::class.java)
437         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
438         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
439 
440         keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
441 
442         verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
443     }
444 
445     @Test
446     fun unlockToLauncherWithInWindowAnimations_ssViewIsGone() {
447         val mockLockscreenSmartspaceView = mock(View::class.java)
448         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
449         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
450 
451         keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
452 
453         verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
454     }
455 
456     @Test
457     fun notifyFinishedKeyguardExitAnimation_ssViewIsInvisibleAndCancelledIsTrue() {
458         val mockLockscreenSmartspaceView = mock(View::class.java)
459         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
460         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
461 
462         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
463 
464         verify(mockLockscreenSmartspaceView).visibility = View.VISIBLE
465     }
466 
467     @Test
468     fun notifyFinishedKeyguardExitAnimation_ssViewIsGoneAndCancelledIsTrue() {
469         val mockLockscreenSmartspaceView = mock(View::class.java)
470         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
471         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
472 
473         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
474 
475         verify(mockLockscreenSmartspaceView, never()).visibility = View.VISIBLE
476     }
477 
478     @Test
479     fun notifyFinishedKeyguardExitAnimation_ssViewIsInvisibleAndCancelledIsFalse() {
480         val mockLockscreenSmartspaceView = mock(View::class.java)
481         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
482         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
483 
484         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(false)
485 
486         verify(mockLockscreenSmartspaceView).visibility = View.VISIBLE
487     }
488 
489     @Test
490     fun notifyFinishedKeyguardExitAnimation_ssViewIsGoneAndCancelledIsFalse() {
491         val mockLockscreenSmartspaceView = mock(View::class.java)
492         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
493         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
494 
495         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(false)
496 
497         verify(mockLockscreenSmartspaceView, never()).visibility = View.VISIBLE
498     }
499 
500     private class ArgThatCaptor<T> {
501         private var allArgs: MutableList<T> = mutableListOf()
502 
503         fun capture(predicate: Predicate<T>): T {
504             return argThat{x: T ->
505                 if (predicate.test(x)) {
506                     allArgs.add(x)
507                     return@argThat true
508                 }
509                 return@argThat false
510             }
511         }
512 
513         fun getLastValue(): T {
514             return allArgs.last()
515         }
516 
517         fun getAllValues(): List<T> {
518             return allArgs
519         }
520     }
521 }
522