1 /*
<lambda>null2  * Copyright (C) 2023 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 package com.android.settings.biometrics2.ui.view
17 
18 import android.content.Context
19 import android.content.Intent
20 import android.content.pm.PackageManager.FEATURE_FINGERPRINT
21 import android.hardware.fingerprint.FingerprintManager
22 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
23 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
24 import android.os.UserHandle
25 import android.util.Log
26 import androidx.test.ext.junit.runners.AndroidJUnit4
27 import androidx.test.platform.app.InstrumentationRegistry
28 import androidx.test.uiautomator.By
29 import androidx.test.uiautomator.UiDevice
30 import androidx.test.uiautomator.UiObject2
31 import androidx.test.uiautomator.Until
32 import com.android.internal.widget.LockPatternChecker
33 import com.android.internal.widget.LockPatternUtils
34 import com.android.internal.widget.LockscreenCredential
35 import com.android.internal.widget.VerifyCredentialResponse
36 import com.android.settings.biometrics2.utils.LockScreenUtil
37 import com.google.common.truth.Truth.assertThat
38 import org.junit.After
39 import org.junit.Assume
40 import org.junit.Before
41 import org.junit.Ignore
42 import org.junit.Test
43 import org.junit.runner.RunWith
44 import java.io.IOException
45 
46 @Ignore
47 @RunWith(AndroidJUnit4::class)
48 class FingerprintEnrollmentActivityTest {
49 
50     private val context: Context by lazy {
51         InstrumentationRegistry.getInstrumentation().context
52     }
53 
54     private val fingerprintManager: FingerprintManager by lazy {
55         context.getSystemService(FingerprintManager::class.java)!!
56     }
57 
58     private var fingerprintPropCallbackLaunched = false
59     private var canAssumeUdfps = false
60     private var canAssumeSfps = false
61     private var enrollingPageTitle: String = ""
62     private var runAsLandscape = false
63 
64     private val device: UiDevice by lazy {
65         UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
66     }
67 
68     @Before
69     @Throws(InterruptedException::class)
70     fun setUp() {
71         // Stop every test if it is not a fingerprint device
72         Assume.assumeTrue(context.packageManager.hasSystemFeature(FEATURE_FINGERPRINT))
73 
74         fingerprintPropCallbackLaunched = false
75         fingerprintManager.addAuthenticatorsRegisteredCallback(
76             object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
77                 override fun onAllAuthenticatorsRegistered(
78                     list: List<FingerprintSensorPropertiesInternal>
79                 ) {
80                     fingerprintPropCallbackLaunched = true
81                     assertThat(list).isNotNull()
82                     assertThat(list).isNotEmpty()
83                     val prop = list[0]
84                     canAssumeUdfps = prop.isAnyUdfpsType
85                     canAssumeSfps = prop.isAnySidefpsType
86                     enrollingPageTitle = if (canAssumeUdfps) {
87                         UDFPS_ENROLLING_TITLE
88                     } else if (canAssumeSfps) {
89                         SFPS_ENROLLING_TITLE
90                     } else {
91                         RFPS_ENROLLING_TITLE
92                     }
93                 }
94             })
95         var i: Long = 0
96         while (i < IDLE_TIMEOUT && !fingerprintPropCallbackLaunched) {
97             Thread.sleep(100L)
98             i += 100L
99         }
100         assertThat(fingerprintPropCallbackLaunched).isTrue()
101         device.pressHome()
102 
103         // Stop settings before performing test
104         try {
105             device.executeShellCommand("am force-stop $SETTINGS_PACKAGE_NAME")
106         } catch (e: IOException) {
107             Log.e(TAG, "Fail to stop settings app", e)
108         }
109     }
110 
111     @After
112     @Throws(Exception::class)
113     fun tearDown() {
114         runAsLandscape = false
115         setDeviceOrientation()
116 
117         LockScreenUtil.resetLockscreen(TEST_PIN)
118         device.pressHome()
119     }
120 
121     @Test
122     fun testIntroChooseLock() {
123         setDeviceOrientation()
124         val intent = newActivityIntent(false)
125         context.startActivity(intent)
126         assertThat(
127             device.wait(
128                 Until.hasObject(By.text("Choose your backup screen lock method")),
129                 IDLE_TIMEOUT
130             )
131         ).isTrue()
132     }
133 
134     @Test
135     fun testIntroChooseLock_runAslandscape() {
136         runAsLandscape = true
137         testIntroChooseLock()
138     }
139 
140     private fun verifyIntroPage() {
141         device.waitForIdle()
142         run {
143             var i: Long = 0
144             while (i < IDLE_TIMEOUT) {
145                 if (device.wait(Until.hasObject(By.text("More")), 50L)) {
146                     break
147                 } else if (device.wait(Until.hasObject(By.text("I agree")), 50L)) {
148                     break
149                 }
150                 i += 100L
151             }
152         }
153 
154         // Click more btn at most twice and the introduction should stay in the last page
155         var moreBtn: UiObject2? = null
156         var i = 0
157         val more = if (runAsLandscape) 5 else 2
158         while (i < more && device.findObject(By.text("More")).also { moreBtn = it } != null) {
159             moreBtn!!.click()
160             device.waitForIdle()
161             device.wait(Until.hasObject(By.text("More")), IDLE_TIMEOUT)
162             ++i
163         }
164         assertThat(device.wait(Until.hasObject(By.text("No thanks")), IDLE_TIMEOUT)).isTrue()
165         assertThat(device.wait(Until.hasObject(By.text("I agree")), IDLE_TIMEOUT)).isTrue()
166     }
167 
168     @Test
169     fun testIntroWithGkPwHandle_withUdfps_clickStart() {
170         Assume.assumeTrue(canAssumeUdfps)
171 
172         setDeviceOrientation()
173         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
174         launchIntroWithGkPwHandle(false)
175 
176         // Intro page
177         verifyIntroPage()
178         val agreeBtn = device.findObject(By.text("I agree"))
179         assertThat(agreeBtn).isNotNull()
180         agreeBtn.click()
181 
182         // FindUdfps page
183         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
184         val lottie = device.findObject(
185             By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
186         )
187         assertThat(lottie).isNotNull()
188         assertThat(lottie.isClickable).isTrue()
189         val startBtn = device.findObject(By.text("Start"))
190         assertThat(startBtn.isClickable).isTrue()
191         startBtn.click()
192 
193         // Enrolling page
194         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
195     }
196 
197     @Test
198     fun testIntroWithGkPwHandle_withUdfps_clickStart_runAslandscape() {
199         runAsLandscape = true
200         testIntroWithGkPwHandle_withUdfps_clickStart()
201     }
202 
203     @Test
204     fun testIntroWithGkPwHandle_withUdfps_clickLottie() {
205         Assume.assumeTrue(canAssumeUdfps)
206 
207         setDeviceOrientation()
208         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
209         launchIntroWithGkPwHandle(false)
210 
211         // Intro page
212         verifyIntroPage()
213         val agreeBtn = device.findObject(By.text("I agree"))
214         assertThat(agreeBtn).isNotNull()
215         agreeBtn.click()
216 
217         // FindUdfps page
218         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
219         val lottie = device.findObject(By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie"))
220         assertThat(lottie).isNotNull()
221         assertThat(lottie.isClickable).isTrue()
222         val startBtn = device.findObject(By.text("Start"))
223         assertThat(startBtn.isClickable).isTrue()
224         lottie.click()
225 
226         // Enrolling page
227         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
228     }
229 
230     @Test
231     fun testIntroWithGkPwHandle_withUdfps_clickLottie_runAslandscape() {
232         runAsLandscape = true
233         testIntroWithGkPwHandle_withUdfps_clickLottie()
234     }
235 
236     @Test
237     fun testIntroWithGkPwHandle_withSfps() {
238         Assume.assumeTrue(canAssumeSfps)
239 
240         setDeviceOrientation()
241         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
242         launchIntroWithGkPwHandle(false)
243 
244         // Intro page
245         verifyIntroPage()
246         val agreeBtn = device.findObject(By.text("I agree"))
247         assertThat(agreeBtn).isNotNull()
248         agreeBtn.click()
249 
250         // FindSfps page
251         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
252         val lottie = device.findObject(
253             By.res(SETTINGS_PACKAGE_NAME,"illustration_lottie")
254         )
255         assertThat(lottie).isNotNull()
256 
257         // We don't have view which can be clicked to run to next page, stop at here.
258     }
259 
260     @Test
261     fun testIntroWithGkPwHandle_withSfps_runAslandscape() {
262         runAsLandscape = true
263         testIntroWithGkPwHandle_withSfps()
264     }
265 
266     @Test
267     fun testIntroWithGkPwHandle_withRfps() {
268         Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
269 
270         setDeviceOrientation()
271         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
272         launchIntroWithGkPwHandle(false)
273 
274         // Intro page
275         verifyIntroPage()
276         val agreeBtn = device.findObject(By.text("I agree"))
277         assertThat(agreeBtn).isNotNull()
278         agreeBtn.click()
279 
280         // FindRfps page
281         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
282         val lottie = device.findObject(
283             By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
284         )
285         if (lottie == null) {
286             // FindSfps page shall have an animation view if no lottie view
287             assertThat(
288                 device.findObject(
289                     By.res(SETTINGS_PACKAGE_NAME, "fingerprint_sensor_location_animation")
290                 )
291             ).isNotNull()
292         }
293     }
294 
295     @Test
296     fun testIntroWithGkPwHandle_withRfps_runAslandscape() {
297         runAsLandscape = true
298         testIntroWithGkPwHandle_withRfps()
299     }
300 
301     @Test
302     fun testIntroWithGkPwHandle_clickNoThanksInIntroPage() {
303         setDeviceOrientation()
304         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
305         launchIntroWithGkPwHandle(false)
306 
307         // Intro page
308         verifyIntroPage()
309         val noThanksBtn = device.findObject(By.text("No thanks"))
310         assertThat(noThanksBtn).isNotNull()
311         noThanksBtn.click()
312 
313         // Back to home
314         device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
315         assertThat(device.findObject(By.text("No thanks"))).isNull()
316     }
317 
318     @Test
319     fun testIntroWithGkPwHandle_clickNoThanksInIntroPage_runAslandscape() {
320         runAsLandscape = true
321         testIntroWithGkPwHandle_clickNoThanksInIntroPage()
322     }
323 
324     @Test
325     fun testIntroWithGkPwHandle_clickSkipInFindSensor() {
326         setDeviceOrientation()
327         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
328         launchIntroWithGkPwHandle(false)
329 
330         // Intro page
331         verifyIntroPage()
332         val agreeBtn = device.findObject(By.text("I agree"))
333         assertThat(agreeBtn).isNotNull()
334         agreeBtn.click()
335 
336         // FindSensor page
337         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
338         val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
339         assertThat(doItLaterBtn).isNotNull()
340         assertThat(doItLaterBtn.isClickable).isTrue()
341         doItLaterBtn.click()
342 
343         // Back to home
344         device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
345         assertThat(device.findObject(By.text(DO_IT_LATER))).isNull()
346     }
347 
348     @Test
349     fun testIntroWithGkPwHandle_clickSkipInFindSensor_runAslandscape() {
350         runAsLandscape = true
351         testIntroWithGkPwHandle_clickSkipInFindSensor()
352     }
353 
354     @Test
355     fun testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() {
356         setDeviceOrientation()
357         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
358         launchIntroWithGkPwHandle(true)
359 
360         // Intro page
361         verifyIntroPage()
362         val agreeBtn = device.findObject(By.text("I agree"))
363         assertThat(agreeBtn).isNotNull()
364         agreeBtn.click()
365 
366         // FindSensor page
367         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
368         val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
369         assertThat(doItLaterBtn).isNotNull()
370         assertThat(doItLaterBtn.isClickable).isTrue()
371         doItLaterBtn.click()
372 
373         // SkipSetupFindFpsDialog
374         assertThat(device.wait(Until.hasObject(By.text("Skip fingerprint?")), IDLE_TIMEOUT)).isTrue()
375         val skipAnywayBtn = device.findObject(By.text("Skip anyway"))
376         assertThat(skipAnywayBtn).isNotNull()
377         assertThat(skipAnywayBtn.isClickable).isTrue()
378         skipAnywayBtn.click()
379 
380         // Back to home
381         device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
382         assertThat(device.findObject(By.text("Skip anyway"))).isNull()
383         assertThat(device.findObject(By.text(DO_IT_LATER))).isNull()
384     }
385 
386     @Test
387     fun testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw_runAslandscape() {
388         runAsLandscape = true
389         testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw()
390     }
391 
392     @Test
393     fun testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() {
394         setDeviceOrientation()
395         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
396         launchIntroWithGkPwHandle(true)
397 
398         // Intro page
399         verifyIntroPage()
400         val agreeBtn = device.findObject(By.text("I agree"))
401         assertThat(agreeBtn).isNotNull()
402         agreeBtn.click()
403 
404         // FindSensor page
405         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
406         val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
407         assertThat(doItLaterBtn).isNotNull()
408         assertThat(doItLaterBtn.isClickable).isTrue()
409         doItLaterBtn.click()
410 
411         // SkipSetupFindFpsDialog
412         assertThat(device.wait(Until.hasObject(By.text("Skip fingerprint?")), IDLE_TIMEOUT)).isTrue()
413         val goBackBtn = device.findObject(By.text("Go back"))
414         assertThat(goBackBtn).isNotNull()
415         assertThat(goBackBtn.isClickable).isTrue()
416         goBackBtn.click()
417 
418         // FindSensor page again
419         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
420     }
421 
422     @Test
423     fun testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw_runAslandscape() {
424         runAsLandscape = true
425         testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw()
426     }
427 
428     @Test
429     fun testIntroCheckPin() {
430         setDeviceOrientation()
431         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
432         val intent = newActivityIntent(false)
433         context.startActivity(intent)
434         assertThat(
435             device.wait(
436                 Until.hasObject(By.text("Enter your device PIN to continue")),
437                 IDLE_TIMEOUT
438             )
439         ).isTrue()
440     }
441 
442     @Test
443     fun testEnrollingWithGkPwHandle() {
444         setDeviceOrientation()
445         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
446         launchEnrollingWithGkPwHandle()
447 
448         // Enrolling screen
449         device.waitForIdle()
450         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
451     }
452 
453     @Test
454     fun testEnrollingWithGkPwHandle_runAslandscape() {
455         runAsLandscape = true
456         testEnrollingWithGkPwHandle()
457     }
458 
459     @Test
460     fun testEnrollingIconTouchDialog_withSfps() {
461         Assume.assumeTrue(canAssumeSfps)
462 
463         setDeviceOrientation()
464         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
465         launchEnrollingWithGkPwHandle()
466 
467         // Enrolling screen
468         device.waitForIdle()
469         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
470         val lottie = device.findObject(
471             By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
472         )
473         assertThat(lottie).isNotNull()
474         lottie.click()
475         lottie.click()
476         lottie.click()
477 
478         // IconTouchDialog
479         device.waitForIdle()
480         assertThat(
481             device.wait(
482                 Until.hasObject(By.text("Touch the sensor instead")),
483                 IDLE_TIMEOUT
484             )
485         )
486             .isTrue()
487         val okButton = device.findObject(By.text("OK"))
488         assertThat(okButton).isNotNull()
489         okButton.click()
490 
491         // Enrolling screen again
492         device.waitForIdle()
493         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
494     }
495 
496     @Test
497     fun testEnrollingIconTouchDialog_withSfps_runAslandscape() {
498         runAsLandscape = true
499         testEnrollingIconTouchDialog_withSfps()
500     }
501 
502     @Test
503     fun testEnrollingIconTouchDialog_withRfps() {
504         Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
505 
506         setDeviceOrientation()
507         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
508         launchEnrollingWithGkPwHandle()
509 
510         // Enrolling screen
511         device.waitForIdle()
512         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
513         val lottie = device.findObject(
514             By.res(SETTINGS_PACKAGE_NAME, "fingerprint_progress_bar")
515         )
516         assertThat(lottie).isNotNull()
517         lottie.click()
518         lottie.click()
519         lottie.click()
520 
521         // IconTouchDialog
522         device.waitForIdle()
523         assertThat(
524             device.wait(
525                 Until.hasObject(By.text("Whoops, that\u2019s not the sensor")),
526                 IDLE_TIMEOUT
527             )
528         ).isTrue()
529         val okButton = device.findObject(By.text("OK"))
530         assertThat(okButton).isNotNull()
531         okButton.click()
532 
533         // Enrolling screen again
534         device.waitForIdle()
535         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
536     }
537 
538     @Test
539     fun testEnrollingIconTouchDialog_withRfps_runAslandscape() {
540         runAsLandscape = true
541         testEnrollingIconTouchDialog_withRfps()
542     }
543 
544     @Test
545     fun testFindUdfpsWithGkPwHandle_clickStart() {
546         Assume.assumeTrue(canAssumeUdfps)
547 
548         setDeviceOrientation()
549         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
550         launchFindSensorWithGkPwHandle()
551 
552         // FindUdfps page
553         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
554         val lottie = device.findObject(
555             By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
556         )
557         assertThat(lottie).isNotNull()
558         assertThat(lottie.isClickable).isTrue()
559         val startBtn = device.findObject(By.text("Start"))
560         assertThat(startBtn.isClickable).isTrue()
561         startBtn.click()
562 
563         // Enrolling page
564         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
565     }
566 
567     @Test
568     fun testFindUdfpsWithGkPwHandle_clickStart_runAslandscape() {
569         runAsLandscape = true
570         testFindUdfpsWithGkPwHandle_clickStart()
571     }
572 
573     @Test
574     fun testFindUdfpsLandscapeWithGkPwHandle_clickStartThenBack() {
575         Assume.assumeTrue(canAssumeUdfps)
576 
577         setDeviceOrientation()
578         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
579         launchFindSensorWithGkPwHandle()
580 
581         // FindUdfps page (portrait)
582         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
583 
584         // rotate device
585         if (runAsLandscape) {
586             device.setOrientationPortrait()
587         } else {
588             device.setOrientationLandscape()
589         }
590         device.waitForIdle()
591 
592         // FindUdfps page (landscape)
593         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
594         val lottie = device.findObject(
595             By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
596         )
597         assertThat(lottie).isNotNull()
598         assertThat(lottie.isClickable).isTrue()
599         val startBtn = device.findObject(By.text("Start"))
600         assertThat(startBtn.isClickable).isTrue()
601         startBtn.click()
602 
603         // Enrolling page
604         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
605 
606         // Press back
607         device.pressBack()
608         device.waitForIdle()
609 
610         // FindUdfps page (landscape-again)
611         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
612     }
613 
614     @Test
615     fun testFindUdfpsLandscapeWithGkPwHandle_clickStartThenBack_runAslandscape() {
616         runAsLandscape = true
617         testFindUdfpsLandscapeWithGkPwHandle_clickStartThenBack()
618     }
619 
620     @Test
621     fun testFindUdfpsWithGkPwHandle_clickLottie() {
622         Assume.assumeTrue(canAssumeUdfps)
623 
624         setDeviceOrientation()
625         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
626         launchFindSensorWithGkPwHandle()
627 
628         // FindUdfps page
629         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
630         val lottie = device.findObject(
631             By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
632         )
633         assertThat(lottie).isNotNull()
634         assertThat(lottie.isClickable).isTrue()
635         val startBtn = device.findObject(By.text("Start"))
636         assertThat(startBtn.isClickable).isTrue()
637         lottie.click()
638 
639         // Enrolling page
640         assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
641     }
642 
643     @Test
644     fun testFindUdfpsWithGkPwHandle_clickLottie_runAslandscape() {
645         runAsLandscape = true
646         testFindUdfpsWithGkPwHandle_clickLottie()
647     }
648 
649     @Test
650     fun testFindSfpsWithGkPwHandle() {
651         Assume.assumeTrue(canAssumeSfps)
652 
653         setDeviceOrientation()
654         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
655         launchFindSensorWithGkPwHandle()
656 
657         // FindSfps page
658         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
659         val lottie = device.findObject(
660             By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
661         )
662         assertThat(lottie).isNotNull()
663 
664         // We don't have view which can be clicked to run to next page, stop at here.
665     }
666 
667     @Test
668     fun testFindSfpsWithGkPwHandle_runAslandscape() {
669         runAsLandscape = true
670         testFindSfpsWithGkPwHandle()
671     }
672 
673     @Test
674     fun testFindRfpsWithGkPwHandle() {
675         Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
676 
677         setDeviceOrientation()
678         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
679         launchFindSensorWithGkPwHandle()
680 
681         // FindRfps page
682         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
683         val lottie = device.findObject(
684             By.res(
685                 SETTINGS_PACKAGE_NAME,
686                 "illustration_lottie"
687             )
688         )
689         if (lottie == null) {
690             // FindSfps page shall have an animation view if no lottie view
691             assertThat(
692                 device.findObject(
693                     By.res(
694                         SETTINGS_PACKAGE_NAME,
695                         "fingerprint_sensor_location_animation"
696                     )
697                 )
698             ).isNotNull()
699         }
700     }
701 
702     @Test
703     fun testFindRfpsWithGkPwHandle_runAslandscape() {
704         runAsLandscape = true
705         testFindRfpsWithGkPwHandle()
706     }
707 
708     @Test
709     fun testFindSensorWithGkPwHandle_clickSkipInFindSensor() {
710         setDeviceOrientation()
711         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
712         launchFindSensorWithGkPwHandle()
713 
714         // FindSensor page
715         assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
716         val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
717         assertThat(doItLaterBtn).isNotNull()
718         assertThat(doItLaterBtn.isClickable).isTrue()
719         doItLaterBtn.click()
720 
721         // Back to home
722         device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
723         assertThat(device.wait(Until.gone(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
724     }
725 
726     @Test
727     fun testFindSensorWithGkPwHandle_clickSkipInFindSensor_runAslandscape() {
728         runAsLandscape = true
729         testFindSensorWithGkPwHandle_clickSkipInFindSensor()
730     }
731 
732     private fun launchIntroWithGkPwHandle(isSuw: Boolean) {
733         val lockPatternUtils = LockPatternUtils(context)
734         val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
735         val userId = UserHandle.myUserId()
736         val onVerifyCallback =
737             LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
738                 val intent = newActivityIntent(isSuw)
739                 intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
740                 context.startActivity(intent)
741             }
742         LockPatternChecker.verifyCredential(
743             lockPatternUtils, lockscreenCredential,
744             userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
745         )
746     }
747 
748     private fun launchFindSensorWithGkPwHandle() {
749         val lockPatternUtils = LockPatternUtils(context)
750         val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
751         val userId = UserHandle.myUserId()
752         val onVerifyCallback =
753             LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
754                 val intent = newActivityIntent(false)
755                 intent.putExtra(EXTRA_SKIP_INTRO, true)
756                 intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
757                 context.startActivity(intent)
758             }
759         LockPatternChecker.verifyCredential(
760             lockPatternUtils, lockscreenCredential,
761             userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
762         )
763     }
764 
765     private fun launchEnrollingWithGkPwHandle() {
766         val lockPatternUtils = LockPatternUtils(context)
767         val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
768         val userId = UserHandle.myUserId()
769         val onVerifyCallback =
770             LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
771                 val intent = newActivityIntent(false)
772                 intent.putExtra(EXTRA_SKIP_FIND_SENSOR, true)
773                 intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
774                 context.startActivity(intent)
775             }
776         LockPatternChecker.verifyCredential(
777             lockPatternUtils, lockscreenCredential,
778             userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
779         )
780     }
781 
782     private fun newActivityIntent(isSuw: Boolean): Intent {
783         val intent = Intent()
784         intent.setClassName(
785             SETTINGS_PACKAGE_NAME,
786             if (isSuw) SUW_ACTIVITY_CLASS_NAME else ACTIVITY_CLASS_NAME
787         )
788         if (isSuw) {
789             intent.putExtra(EXTRA_IS_SETUP_FLOW, true)
790         }
791         intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, 1)
792         intent.putExtra(Intent.EXTRA_USER_ID, context.userId)
793         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
794         return intent
795     }
796 
797     private fun setDeviceOrientation() {
798         if (runAsLandscape) {
799             device.setOrientationLandscape()
800         } else {
801             device.setOrientationPortrait()
802         }
803         device.waitForIdle()
804     }
805 
806     companion object {
807         private const val TAG = "FingerprintEnrollmentActivityTest"
808         const val SETTINGS_PACKAGE_NAME = "com.android.settings"
809         private const val ACTIVITY_CLASS_NAME =
810             "com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity"
811         private const val SUW_ACTIVITY_CLASS_NAME = "$ACTIVITY_CLASS_NAME\$SetupActivity"
812         private const val EXTRA_IS_SETUP_FLOW = "isSetupFlow"
813         private const val EXTRA_SKIP_INTRO = "skip_intro"
814         private const val EXTRA_SKIP_FIND_SENSOR = "skip_find_sensor"
815         private const val EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type"
816         private const val EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle"
817         private const val TEST_PIN = "1234"
818         private const val DO_IT_LATER = "Do it later"
819         private const val UDFPS_ENROLLING_TITLE = "Touch & hold the fingerprint sensor"
820         private const val SFPS_ENROLLING_TITLE =
821             "Lift, then touch. Move your finger slightly each time."
822         private const val RFPS_ENROLLING_TITLE = "Lift, then touch again"
823         private const val IDLE_TIMEOUT = 10000L
824     }
825 }
826