Moving FingerprintSettings to Kotlin

This change is the first of many, it will

1. Change java -> kotlin
2. Use the MVVM architecture
3. Be feature flagged

This change in particular is focused on transitions to and from various activities.

Enable feature via
adb shell setprop sys.fflag.override.settings_biometrics2_fingerprint true

Bug: 280862076
Test: atest FingerprintSettingsViewModelTest
Change-Id: I8eb5c30e6f2e92c256ae7c257a9d560439ba418f
Merged-In: I8eb5c30e6f2e92c256ae7c257a9d560439ba418f
This commit is contained in:
Joshua McCloskey
2023-04-13 19:58:26 +00:00
parent 997ef1e5b3
commit b7f0d7a8ff
11 changed files with 989 additions and 2 deletions

View File

@@ -0,0 +1,332 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fingerprint2.viewmodel
import android.hardware.fingerprint.Fingerprint
import android.hardware.fingerprint.FingerprintManager
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.EnrollFirstFingerprint
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.FinishSettings
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.FinishSettingsWithResult
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.LaunchConfirmDeviceCredential
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.NextStepViewModel
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.ShowSettings
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.FingerprintSettingsViewModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.Mockito.`when` as whenever
@RunWith(MockitoJUnitRunner::class)
class FingerprintSettingsViewModelTest {
@JvmField
@Rule
var rule = MockitoJUnit.rule()
@Mock
private lateinit var fingerprintManager: FingerprintManager
private lateinit var underTest: FingerprintSettingsViewModel
private val defaultUserId = 0
@Before
fun setup() {
// @formatter:off
underTest = FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
fingerprintManager,
).create(FingerprintSettingsViewModel::class.java)
// @formatter:on
}
@Test
fun testNoGateKeeper_launchesConfirmDeviceCredential() = runTest {
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
runCurrent()
assertThat(nextStep).isEqualTo(LaunchConfirmDeviceCredential(defaultUserId))
job.cancel()
}
@Test
fun testConfirmDevice_fails() = runTest {
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(false, null)
runCurrent()
assertThat(nextStep).isInstanceOf(FinishSettings::class.java)
job.cancel()
}
@Test
fun confirmDeviceSuccess_noGateKeeper() = runTest {
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, null)
runCurrent()
assertThat(nextStep).isInstanceOf(FinishSettings::class.java)
job.cancel()
}
@Test
fun confirmDeviceSuccess_launchesEnrollment_ifNoPreviousEnrollments() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
runCurrent()
assertThat(nextStep).isEqualTo(EnrollFirstFingerprint(defaultUserId, 10L, null, null))
job.cancel()
}
@Test
fun firstEnrollment_fails() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
underTest.onEnrollFirstFailure("We failed!!")
runCurrent()
assertThat(nextStep).isInstanceOf(FinishSettings::class.java)
job.cancel()
}
@Test
fun firstEnrollment_failsWithReason() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
val failStr = "We failed!!"
val failReason = 101
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
underTest.onEnrollFirstFailure(failStr, failReason)
runCurrent()
assertThat(nextStep).isEqualTo(FinishSettingsWithResult(failReason, failStr))
job.cancel()
}
@Test
fun firstEnrollmentSucceeds_noToken() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
underTest.onEnrollFirst(null, null)
runCurrent()
assertThat(nextStep).isEqualTo(FinishSettings("Error, empty token"))
job.cancel()
}
@Test
fun firstEnrollmentSucceeds_noKeyChallenge() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
val byteArray = ByteArray(1) {
3
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
underTest.onEnrollFirst(byteArray, null)
runCurrent()
assertThat(nextStep).isEqualTo(FinishSettings("Error, empty keyChallenge"))
job.cancel()
}
@Test
fun firstEnrollment_succeeds() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
val byteArray = ByteArray(1) {
3
}
val keyChallenge = 89L
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
underTest.onEnrollFirst(byteArray, keyChallenge)
runCurrent()
assertThat(nextStep).isEqualTo(ShowSettings(defaultUserId))
job.cancel()
}
@Test
fun confirmDeviceCredential_withEnrolledFingerprint_showsSettings() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(
listOf(
Fingerprint(
"a", 1, 2, 3L
)
)
)
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
runCurrent()
assertThat(nextStep).isEqualTo(ShowSettings(defaultUserId))
job.cancel()
}
@Test
fun enrollAdditionalFingerprints_fails() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(
listOf(
Fingerprint(
"a", 1, 2, 3L
)
)
)
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
underTest.onEnrollAdditionalFailure()
runCurrent()
assertThat(nextStep).isInstanceOf(FinishSettings::class.java)
job.cancel()
}
@Test
fun enrollAdditional_success() = runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(
listOf(
Fingerprint(
"a", 1, 2, 3L
)
)
)
var nextStep: NextStepViewModel? = null
val job = launch {
underTest.nextStep.collect {
nextStep = it
}
}
underTest.updateTokenAndChallenge(null, null)
underTest.onConfirmDevice(true, 10L)
underTest.onEnrollSuccess()
runCurrent()
assertThat(nextStep).isEqualTo(ShowSettings(defaultUserId))
job.cancel()
}
}