Split up FingerprintManagerInteractor

Test: atest, screenshot tests passed
Flag: com.android.settings.flags.fingerprint_v2_enrollment
Change-Id: I70833d5d9888f730233a9757589ce7faa45eccc9
This commit is contained in:
Joshua McCloskey
2024-08-07 23:52:11 +00:00
parent 59f11d9377
commit 882e1c3621
51 changed files with 1231 additions and 523 deletions

View File

@@ -18,7 +18,9 @@ package com.android.settings;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri; import android.net.Uri;
import android.provider.Settings; import android.provider.Settings;
import android.util.FeatureFlagUtils; import android.util.FeatureFlagUtils;
@@ -74,9 +76,6 @@ public class SettingsApplication extends Application {
// Set Spa environment. // Set Spa environment.
setSpaEnvironment(); setSpaEnvironment();
if (Flags.fingerprintV2Enrollment()) {
mBiometricsEnvironment = new BiometricsEnvironment(this);
}
if (ActivityEmbeddingUtils.isSettingsSplitEnabled(this) if (ActivityEmbeddingUtils.isSettingsSplitEnabled(this)
&& FeatureFlagUtils.isEnabled(this, && FeatureFlagUtils.isEnabled(this,
@@ -120,7 +119,20 @@ public class SettingsApplication extends Application {
@Nullable @Nullable
public BiometricsEnvironment getBiometricEnvironment() { public BiometricsEnvironment getBiometricEnvironment() {
if (Flags.fingerprintV2Enrollment()) {
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
final FingerprintManager fpm = getSystemService(FingerprintManager.class);
if (mBiometricsEnvironment == null) {
mBiometricsEnvironment = new BiometricsEnvironment(this, fpm);
}
return mBiometricsEnvironment; return mBiometricsEnvironment;
} else {
return null;
}
}
return null;
} }
@Override @Override

View File

@@ -16,12 +16,9 @@
package com.android.settings.biometrics.fingerprint2 package com.android.settings.biometrics.fingerprint2
import android.content.pm.PackageManager
import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager
import android.os.ServiceManager.ServiceNotFoundException
import android.view.MotionEvent import android.view.MotionEvent
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.ViewModelStoreOwner
import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils
@@ -29,33 +26,47 @@ import com.android.settings.SettingsApplication
import com.android.settings.biometrics.GatekeeperPasswordProvider import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepository import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepository
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSettingsRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.UserRepoImpl
import com.android.settings.biometrics.fingerprint2.debug.data.repository.UdfpsEnrollDebugRepositoryImpl import com.android.settings.biometrics.fingerprint2.debug.data.repository.UdfpsEnrollDebugRepositoryImpl
import com.android.settings.biometrics.fingerprint2.debug.domain.interactor.DebugTouchEventInteractorImpl import com.android.settings.biometrics.fingerprint2.debug.domain.interactor.DebugTouchEventInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.AuthenticateInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.CanEnrollFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollFingerprintInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrolledFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.GenerateChallengeInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.RemoveFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.RenameFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.SensorInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RemoveFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RenameFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.Settings import com.android.settings.biometrics.fingerprint2.lib.model.Settings
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
@@ -70,43 +81,53 @@ import kotlinx.coroutines.flow.flowOf
* This code is instantiated within the [SettingsApplication], all repos should be private & * This code is instantiated within the [SettingsApplication], all repos should be private &
* immutable and all interactors should public and immutable * immutable and all interactors should public and immutable
*/ */
class BiometricsEnvironment(context: SettingsApplication) : ViewModelStoreOwner { class BiometricsEnvironment(
val context: SettingsApplication,
private val fingerprintManager: FingerprintManager,
) : ViewModelStoreOwner {
private val executorService = Executors.newSingleThreadExecutor() private val executorService = Executors.newSingleThreadExecutor()
private val backgroundDispatcher = executorService.asCoroutineDispatcher() private val backgroundDispatcher = executorService.asCoroutineDispatcher()
private val applicationScope = MainScope() private val applicationScope = MainScope()
private val gateKeeperPasswordProvider = GatekeeperPasswordProvider(LockPatternUtils(context)) private val gateKeeperPasswordProvider = GatekeeperPasswordProvider(LockPatternUtils(context))
private val fingerprintManager = try {
if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
context.getSystemService(FragmentActivity.FINGERPRINT_SERVICE) as FingerprintManager?
} else {
null
}
} catch (exception: ServiceNotFoundException){
null
}
private val userRepo = UserRepoImpl(context.userId)
private val fingerprintSettingsRepository =
FingerprintSettingsRepositoryImpl(
context.resources.getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser
)
)
private val fingerprintEnrollmentRepository =
FingerprintEnrollmentRepositoryImpl(fingerprintManager, userRepo, fingerprintSettingsRepository,
backgroundDispatcher, applicationScope)
private val fingerprintSensorRepository: FingerprintSensorRepository = private val fingerprintSensorRepository: FingerprintSensorRepository =
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, applicationScope) FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, applicationScope)
private val debuggingRepository: DebuggingRepository = DebuggingRepositoryImpl() private val debuggingRepository: DebuggingRepository = DebuggingRepositoryImpl()
private val udfpsDebugRepo = UdfpsEnrollDebugRepositoryImpl() private val udfpsDebugRepo = UdfpsEnrollDebugRepositoryImpl()
/** For now, interactors are public to those with access to the [BiometricsEnvironment] class */ fun createSensorPropertiesInteractor(): SensorInteractor =
val fingerprintEnrollInteractor: FingerprintEnrollInteractor by lazy { SensorInteractorImpl(fingerprintSensorRepository)
FingerprintEnrollInteractorImpl(context, fingerprintManager, Settings)
}
/** [FingerprintManagerInteractor] to be used to construct view models */ fun createCanEnrollFingerprintsInteractor(): CanEnrollFingerprintsInteractor =
val fingerprintManagerInteractor: FingerprintManagerInteractor by lazy { CanEnrollFingerprintsInteractorImpl(fingerprintEnrollmentRepository)
FingerprintManagerInteractorImpl(
context, fun createGenerateChallengeInteractor(): GenerateChallengeInteractor =
backgroundDispatcher, GenerateChallengeInteractorImpl(fingerprintManager, context.userId, gateKeeperPasswordProvider)
fingerprintManager,
fingerprintSensorRepository, fun createFingerprintEnrollInteractor(): EnrollFingerprintInteractor =
gateKeeperPasswordProvider, EnrollFingerprintInteractorImpl(context.userId, fingerprintManager, Settings)
fingerprintEnrollInteractor,
) fun createFingerprintsEnrolledInteractor(): EnrolledFingerprintsInteractorImpl =
} EnrolledFingerprintsInteractorImpl(fingerprintManager, context.userId)
fun createAuthenticateInteractor(): AuthenitcateInteractor =
AuthenticateInteractorImpl(fingerprintManager, context.userId)
fun createRemoveFingerprintInteractor(): RemoveFingerprintInteractor =
RemoveFingerprintsInteractorImpl(fingerprintManager, context.userId)
fun createRenameFingerprintInteractor(): RenameFingerprintInteractor =
RenameFingerprintsInteractorImpl(fingerprintManager, context.userId, backgroundDispatcher)
val accessibilityInteractor: AccessibilityInteractor by lazy { val accessibilityInteractor: AccessibilityInteractor by lazy {
AccessibilityInteractorImpl( AccessibilityInteractorImpl(

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.data.repository
import android.hardware.biometrics.BiometricStateListener
import android.hardware.fingerprint.FingerprintManager
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
/** Repository that contains information about fingerprint enrollments. */
interface FingerprintEnrollmentRepository {
/** The current enrollments of the user */
val currentEnrollments: Flow<List<FingerprintData>?>
/** Indicates if a user can enroll another fingerprint */
val canEnrollUser: Flow<Boolean>
fun maxFingerprintsEnrollable(): Int
}
class FingerprintEnrollmentRepositoryImpl(
fingerprintManager: FingerprintManager,
userRepo: UserRepo,
private val settingsRepository: FingerprintSettingsRepository,
backgroundDispatcher: CoroutineDispatcher,
applicationScope: CoroutineScope,
) : FingerprintEnrollmentRepository {
private val enrollmentChangedFlow: Flow<Int?> =
callbackFlow {
val callback =
object : BiometricStateListener() {
override fun onEnrollmentsChanged(userId: Int, sensorId: Int, hasEnrollments: Boolean) {
trySend(userId)
}
}
withContext(backgroundDispatcher) {
fingerprintManager.registerBiometricStateListener(callback)
}
awaitClose {
// no way to unregister
}
}
.stateIn(applicationScope, started = SharingStarted.Eagerly, initialValue = null)
override val currentEnrollments: Flow<List<FingerprintData>> =
userRepo.currentUser
.distinctUntilChanged()
.flatMapLatest { currentUser ->
enrollmentChangedFlow.map { enrollmentChanged ->
if (enrollmentChanged == null || enrollmentChanged == currentUser) {
fingerprintManager
.getEnrolledFingerprints(currentUser)
?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }
?.toList()
} else {
null
}
}
}
.filterNotNull()
.flowOn(backgroundDispatcher)
override val canEnrollUser: Flow<Boolean> =
currentEnrollments.map {
it?.size?.let { it < settingsRepository.maxEnrollableFingerprints() } ?: false
}
override fun maxFingerprintsEnrollable(): Int {
return settingsRepository.maxEnrollableFingerprints()
}
}

View File

@@ -31,6 +31,8 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transform import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -43,10 +45,13 @@ import kotlinx.coroutines.withContext
interface FingerprintSensorRepository { interface FingerprintSensorRepository {
/** Get the [FingerprintSensor] */ /** Get the [FingerprintSensor] */
val fingerprintSensor: Flow<FingerprintSensor> val fingerprintSensor: Flow<FingerprintSensor>
/** Indicates if this device supports the side fingerprint sensor */
val hasSideFps: Flow<Boolean>
} }
class FingerprintSensorRepositoryImpl( class FingerprintSensorRepositoryImpl(
fingerprintManager: FingerprintManager?, private val fingerprintManager: FingerprintManager,
backgroundDispatcher: CoroutineDispatcher, backgroundDispatcher: CoroutineDispatcher,
activityScope: CoroutineScope, activityScope: CoroutineScope,
) : FingerprintSensorRepository { ) : FingerprintSensorRepository {
@@ -66,7 +71,7 @@ class FingerprintSensorRepositoryImpl(
} }
} }
withContext(backgroundDispatcher) { withContext(backgroundDispatcher) {
fingerprintManager?.addAuthenticatorsRegisteredCallback(callback) fingerprintManager.addAuthenticatorsRegisteredCallback(callback)
} }
awaitClose {} awaitClose {}
} }
@@ -75,6 +80,9 @@ class FingerprintSensorRepositoryImpl(
override val fingerprintSensor: Flow<FingerprintSensor> = override val fingerprintSensor: Flow<FingerprintSensor> =
fingerprintPropsInternal.transform { emit(it.toFingerprintSensor()) } fingerprintPropsInternal.transform { emit(it.toFingerprintSensor()) }
override val hasSideFps: Flow<Boolean> =
fingerprintSensor.flatMapLatest { flow { emit(fingerprintManager.isPowerbuttonFps()) } }
companion object { companion object {
private val DEFAULT_PROPS = private val DEFAULT_PROPS =

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.data.repository
/**
* Repository for storing metadata about fingerprint enrollments.
*/
interface FingerprintSettingsRepository {
/**
* Indicates the maximum number of fingerprints enrollable
*/
fun maxEnrollableFingerprints(): Int
}
class FingerprintSettingsRepositoryImpl(private val maxFingerprintsEnrollable: Int) :
FingerprintSettingsRepository {
override fun maxEnrollableFingerprints() = maxFingerprintsEnrollable
}

View File

@@ -16,7 +16,6 @@
package com.android.settings.biometrics.fingerprint2.data.repository package com.android.settings.biometrics.fingerprint2.data.repository
import android.graphics.Point
import android.view.MotionEvent import android.view.MotionEvent
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.data.repository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
/**
* A repository responsible for indicating the current user.
*/
interface UserRepo {
/**
* This flow indicates the current user.
*/
val currentUser: Flow<Int>
}
class UserRepoImpl(val currUser: Int): UserRepo {
override val currentUser: Flow<Int> = flowOf(currUser)
}

View File

@@ -97,6 +97,8 @@ class UdfpsEnrollDebugRepositoryImpl :
} }
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensorProps) override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensorProps)
override val hasSideFps: Flow<Boolean>
get() = flowOf(false)
private fun pointToLeftOfSensor(sensorLocation: Rect): MotionEvent = private fun pointToLeftOfSensor(sensorLocation: Rect): MotionEvent =
MotionEvent.obtain( MotionEvent.obtain(

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.domain.interactor
import android.hardware.fingerprint.FingerprintManager
import android.os.CancellationSignal
import android.util.Log
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import kotlin.coroutines.resume
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
class AuthenticateInteractorImpl(
private val fingerprintManager: FingerprintManager,
private val userId: Int,
) : AuthenitcateInteractor {
override suspend fun authenticate(): FingerprintAuthAttemptModel =
suspendCancellableCoroutine { c: CancellableContinuation<FingerprintAuthAttemptModel> ->
val authenticationCallback =
object : FingerprintManager.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (c.isCompleted) {
Log.d(TAG, "framework sent down onAuthError after finish")
return
}
c.resume(FingerprintAuthAttemptModel.Error(errorCode, errString.toString()))
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
if (c.isCompleted) {
Log.d(TAG, "framework sent down onAuthError after finish")
return
}
c.resume(FingerprintAuthAttemptModel.Success(result.fingerprint?.biometricId ?: -1))
}
}
val cancellationSignal = CancellationSignal()
c.invokeOnCancellation { cancellationSignal.cancel() }
fingerprintManager.authenticate(
null,
cancellationSignal,
authenticationCallback,
null,
userId,
)
}
companion object {
private const val TAG = "AuthenticateInteractor"
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.domain.interactor
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepository
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
import kotlinx.coroutines.flow.Flow
class CanEnrollFingerprintsInteractorImpl(
val fingerprintEnrollmentRepository: FingerprintEnrollmentRepository
) : CanEnrollFingerprintsInteractor {
override val canEnrollFingerprints: Flow<Boolean> = fingerprintEnrollmentRepository.canEnrollUser
/** Indicates the maximum fingerprints enrollable for a given user */
override fun maxFingerprintsEnrollable(): Int {
return fingerprintEnrollmentRepository.maxFingerprintsEnrollable()
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.domain.interactor
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintManager
import android.os.CancellationSignal
import android.util.Log
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.onFailure
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.update
class EnrollFingerprintInteractorImpl(
private val userId: Int,
private val fingerprintManager: FingerprintManager,
private val fingerprintFlow: FingerprintFlow,
) : EnrollFingerprintInteractor {
private val enrollRequestOutstanding = MutableStateFlow(false)
override suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState> = callbackFlow {
// TODO (b/308456120) Improve this logic
if (enrollRequestOutstanding.value) {
Log.d(TAG, "Outstanding enroll request, waiting 150ms")
delay(150)
if (enrollRequestOutstanding.value) {
Log.e(TAG, "Request still present, continuing")
}
}
enrollRequestOutstanding.update { true }
var streamEnded = false
var totalSteps: Int? = null
val enrollmentCallback =
object : FingerprintManager.EnrollmentCallback() {
override fun onEnrollmentProgress(remaining: Int) {
// This is sort of an implementation detail, but unfortunately the API isn't
// very expressive. If anything we should look at changing the FingerprintManager API.
if (totalSteps == null) {
totalSteps = remaining + 1
}
trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure { error ->
Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error")
}
if (remaining == 0) {
streamEnded = true
enrollRequestOutstanding.update { false }
}
}
override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) {
trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString())).onFailure { error
->
Log.d(TAG, "onEnrollmentHelp failed to send, due to $error")
}
}
override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) {
trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard)).onFailure { error ->
Log.d(TAG, "onEnrollmentError failed to send, due to $error")
}
Log.d(TAG, "onEnrollmentError($errMsgId)")
streamEnded = true
enrollRequestOutstanding.update { false }
}
override fun onUdfpsPointerDown(sensorId: Int) {
trySend(FingerEnrollState.PointerDown(sensorId)).onFailure { error ->
Log.d(TAG, "onUdfpsPointerDown failed to send, due to $error")
}
}
override fun onUdfpsPointerUp(sensorId: Int) {
trySend(FingerEnrollState.PointerUp(sensorId)).onFailure { error ->
Log.d(TAG, "onUdfpsPointerUp failed to send, due to $error")
}
}
override fun onUdfpsOverlayShown() {
trySend(FingerEnrollState.OverlayShown).onFailure { error ->
Log.d(TAG, "OverlayShown failed to send, due to $error")
}
}
override fun onAcquired(isAcquiredGood: Boolean) {
trySend(FingerEnrollState.Acquired(isAcquiredGood)).onFailure { error ->
Log.d(TAG, "Acquired failed to send, due to $error")
}
}
}
val cancellationSignal = CancellationSignal()
fingerprintManager.enroll(
hardwareAuthToken,
cancellationSignal,
userId,
enrollmentCallback,
enrollReason.toOriginalReason(),
fingerprintEnrollOptions,
)
awaitClose {
// If the stream has not been ended, and the user has stopped collecting the flow
// before it was over, send cancel.
if (!streamEnded) {
Log.e(TAG, "Cancel is sent from settings for enroll()")
cancellationSignal.cancel()
}
}
}
companion object {
private const val TAG = "FingerprintEnrollStateRepository"
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.domain.interactor
import android.hardware.fingerprint.FingerprintManager
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class EnrolledFingerprintsInteractorImpl(
private val fingerprintManager: FingerprintManager,
userId: Int,
) : EnrolledFingerprintsInteractor {
override val enrolledFingerprints: Flow<List<FingerprintData>?> = flow {
emit(
fingerprintManager
.getEnrolledFingerprints(userId)
?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }
?.toList()
)
}
}

View File

@@ -16,7 +16,6 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.content.Context
import android.hardware.fingerprint.FingerprintEnrollOptions import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager
import android.os.CancellationSignal import android.os.CancellationSignal
@@ -49,7 +48,7 @@ interface FingerprintEnrollInteractor {
} }
class FingerprintEnrollInteractorImpl( class FingerprintEnrollInteractorImpl(
private val applicationContext: Context, private val userId: Int,
private val fingerprintManager: FingerprintManager?, private val fingerprintManager: FingerprintManager?,
private val fingerprintFlow: FingerprintFlow, private val fingerprintFlow: FingerprintFlow,
) : FingerprintEnrollInteractor { ) : FingerprintEnrollInteractor {
@@ -138,7 +137,7 @@ class FingerprintEnrollInteractorImpl(
fingerprintManager?.enroll( fingerprintManager?.enroll(
hardwareAuthToken, hardwareAuthToken,
cancellationSignal, cancellationSignal,
applicationContext.userId, userId,
enrollmentCallback, enrollmentCallback,
enrollReason.toOriginalReason(), enrollReason.toOriginalReason(),
fingerprintEnrollOptions, fingerprintEnrollOptions,

View File

@@ -1,173 +0,0 @@
/*
* 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.biometrics.fingerprint2.domain.interactor
import android.content.Context
import android.content.Intent
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
import android.os.CancellationSignal
import android.util.Log
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.password.ChooseLockSettingsHelper
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
private const val TAG = "FingerprintManagerInteractor"
class FingerprintManagerInteractorImpl(
applicationContext: Context,
private val backgroundDispatcher: CoroutineDispatcher,
private val fingerprintManager: FingerprintManager?,
fingerprintSensorRepository: FingerprintSensorRepository,
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
private val fingerprintEnrollStateRepository: FingerprintEnrollInteractor,
) : FingerprintManagerInteractor {
private val maxFingerprints =
applicationContext.resources.getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser
)
private val applicationContext = applicationContext.applicationContext
override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
suspendCoroutine {
val callback = GenerateChallengeCallback { _, userId, challenge ->
val intent = Intent()
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gateKeeperPasswordHandle)
val challengeToken =
gatekeeperPasswordProvider.requestGatekeeperHat(intent, challenge, userId)
gatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, false)
val p = Pair(challenge, challengeToken)
it.resume(p)
}
fingerprintManager?.generateChallenge(applicationContext.userId, callback)
}
override val enrolledFingerprints: Flow<List<FingerprintData>?> = flow {
emit(
fingerprintManager?.getEnrolledFingerprints(applicationContext.userId)
?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }?.toList()
)
}
override val canEnrollFingerprints: Flow<Boolean> = flow {
emit(
fingerprintManager?.getEnrolledFingerprints(applicationContext.userId)?.size ?: maxFingerprints < maxFingerprints
)
}
override val sensorPropertiesInternal = fingerprintSensorRepository.fingerprintSensor
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
override suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState> =
fingerprintEnrollStateRepository.enroll(
hardwareAuthToken,
enrollReason,
fingerprintEnrollOptions,
)
override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
val callback =
object : RemovalCallback() {
override fun onRemovalError(
fp: android.hardware.fingerprint.Fingerprint,
errMsgId: Int,
errString: CharSequence,
) {
it.resume(false)
}
override fun onRemovalSucceeded(
fp: android.hardware.fingerprint.Fingerprint?,
remaining: Int,
) {
it.resume(true)
}
}
fingerprintManager?.remove(
android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId),
applicationContext.userId,
callback,
)
}
override suspend fun renameFingerprint(fp: FingerprintData, newName: String) {
withContext(backgroundDispatcher) {
fingerprintManager?.rename(fp.fingerId, applicationContext.userId, newName)
}
}
override suspend fun hasSideFps(): Boolean? = suspendCancellableCoroutine {
it.resume(fingerprintManager?.isPowerbuttonFps)
}
override suspend fun authenticate(): FingerprintAuthAttemptModel =
suspendCancellableCoroutine { c: CancellableContinuation<FingerprintAuthAttemptModel> ->
val authenticationCallback =
object : FingerprintManager.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (c.isCompleted) {
Log.d(TAG, "framework sent down onAuthError after finish")
return
}
c.resume(FingerprintAuthAttemptModel.Error(errorCode, errString.toString()))
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
if (c.isCompleted) {
Log.d(TAG, "framework sent down onAuthError after finish")
return
}
c.resume(FingerprintAuthAttemptModel.Success(result.fingerprint?.biometricId ?: -1))
}
}
val cancellationSignal = CancellationSignal()
c.invokeOnCancellation { cancellationSignal.cancel() }
fingerprintManager?.authenticate(
null,
cancellationSignal,
authenticationCallback,
null,
applicationContext.userId,
)
}
}

View File

@@ -20,9 +20,7 @@ import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintS
import com.android.systemui.biometrics.shared.model.FingerprintSensor import com.android.systemui.biometrics.shared.model.FingerprintSensor
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /** Interactor that propagates the type of [FingerprintSensor] this device supports. */
* Interactor that propagates the type of [FingerprintSensor] this device supports.
*/
interface FingerprintSensorInteractor { interface FingerprintSensorInteractor {
/** Get the [FingerprintSensor] */ /** Get the [FingerprintSensor] */
val fingerprintSensor: Flow<FingerprintSensor> val fingerprintSensor: Flow<FingerprintSensor>

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.domain.interactor
import android.content.Intent
import android.hardware.fingerprint.FingerprintManager
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
import com.android.settings.password.ChooseLockSettingsHelper
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class GenerateChallengeInteractorImpl(
private val fingerprintManager: FingerprintManager,
private val userId: Int,
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
) : GenerateChallengeInteractor {
override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
suspendCoroutine {
val callback =
FingerprintManager.GenerateChallengeCallback { _, userId, challenge ->
val intent = Intent()
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gateKeeperPasswordHandle)
val challengeToken =
gatekeeperPasswordProvider.requestGatekeeperHat(intent, challenge, userId)
gatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, false)
val p = Pair(challenge, challengeToken)
it.resume(p)
}
fingerprintManager.generateChallenge(userId, callback)
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.domain.interactor
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RemoveFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class RemoveFingerprintsInteractorImpl(
private val fingerprintManager: FingerprintManager,
private val userId: Int,
) : RemoveFingerprintInteractor {
override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
val callback =
object : RemovalCallback() {
override fun onRemovalError(
fp: android.hardware.fingerprint.Fingerprint,
errMsgId: Int,
errString: CharSequence,
) {
it.resume(false)
}
override fun onRemovalSucceeded(
fp: android.hardware.fingerprint.Fingerprint?,
remaining: Int,
) {
it.resume(true)
}
}
fingerprintManager.remove(
android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId),
userId,
callback,
)
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.domain.interactor
import android.hardware.fingerprint.FingerprintManager
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RenameFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
class RenameFingerprintsInteractorImpl(
private val fingerprintManager: FingerprintManager,
private val userId: Int,
private val backgroundDispatcher: CoroutineDispatcher,
) : RenameFingerprintInteractor {
override suspend fun renameFingerprint(fp: FingerprintData, newName: String) {
withContext(backgroundDispatcher) { fingerprintManager.rename(fp.fingerId, userId, newName) }
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.domain.interactor
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import kotlinx.coroutines.flow.Flow
class SensorInteractorImpl(private val repo: FingerprintSensorRepository) :
SensorInteractor {
override val sensorPropertiesInternal = repo.fingerprintSensor
override val hasSideFps: Flow<Boolean> = repo.hasSideFps
}

View File

@@ -24,4 +24,3 @@ interface TouchEventInteractor {
/** A flow simulating user touches. */ /** A flow simulating user touches. */
val touchEvent: Flow<MotionEvent> val touchEvent: Flow<MotionEvent>
} }

View File

@@ -13,6 +13,6 @@
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
package="com.android.settings.biometrics.fingerprint2.lib"> package="com.android.settings.biometrics.fingerprint2.lib">
</manifest> </manifest>

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
/** Interactor responsible for coordinating authentication. */
interface AuthenitcateInteractor {
/** Runs the authenticate flow */
suspend fun authenticate(): FingerprintAuthAttemptModel
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
import kotlinx.coroutines.flow.Flow
/** Returns whether or not a user can enroll a fingerprint */
interface CanEnrollFingerprintsInteractor {
/** Returns true if a user can enroll a fingerprint false otherwise. */
val canEnrollFingerprints: Flow<Boolean>
/** Indicates the maximum fingerprints enrollable for a given user */
fun maxFingerprintsEnrollable(): Int
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
import android.hardware.fingerprint.FingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import kotlinx.coroutines.flow.Flow
/** Interactor that enrolls a fingerprint */
interface EnrollFingerprintInteractor {
/**
* Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
* enrollment. If successful data in the [fingerprintEnrollState] should be populated.
*/
suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState>
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import kotlinx.coroutines.flow.Flow
/** Interface to obtain the enrolled fingerprints */
interface EnrolledFingerprintsInteractor {
/** Returns the list of current fingerprints. */
val enrolledFingerprints: Flow<List<FingerprintData>?>
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
import android.hardware.fingerprint.FingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import kotlinx.coroutines.flow.Flow
/**
* Interface to obtain the necessary data for FingerprintEnrollment/Settings
*
* Note that this interface should not have dependencies on heavyweight libraries such as the
* framework, hidl/aidl, etc. This makes it much easier to test and create fakes for.
*/
interface FingerprintManagerInteractor {
/** Returns the list of current fingerprints. */
val enrolledFingerprints: Flow<List<FingerprintData>?>
/** Returns the max enrollable fingerprints, note during SUW this might be 1 */
val maxEnrollableFingerprints: Flow<Int>
/** Returns true if a user can enroll a fingerprint false otherwise. */
val canEnrollFingerprints: Flow<Boolean>
/** Retrieves the sensor properties of a device */
val sensorPropertiesInternal: Flow<FingerprintSensor?>
/** Runs the authenticate flow */
suspend fun authenticate(): FingerprintAuthAttemptModel
/**
* Generates a challenge with the provided [gateKeeperPasswordHandle] and on success returns a
* challenge and challenge token. This info can be used for secure operations such as enrollment
*
* @param gateKeeperPasswordHandle GateKeeper password handle generated by a Confirm
* @return A [Pair] of the challenge and challenge token
*/
suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray>
/**
* Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
* enrollment. If successful data in the [fingerprintEnrollState] should be populated.
*/
suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState>
/**
* Removes the given fingerprint, returning true if it was successfully removed and false
* otherwise
*/
suspend fun removeFingerprint(fp: FingerprintData): Boolean
/** Renames the given fingerprint if one exists */
suspend fun renameFingerprint(fp: FingerprintData, newName: String)
/** Indicates if the device has side fingerprint */
suspend fun hasSideFps(): Boolean?
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
/** This interactor is responsible for generating a challenge. */
interface GenerateChallengeInteractor {
/**
* Generates a challenge with the provided [gateKeeperPasswordHandle] and on success returns a
* challenge and challenge token. This info can be used for secure operations such as enrollment
*
* @param gateKeeperPasswordHandle GateKeeper password handle generated by a Confirm
* @return A [Pair] of the challenge and challenge token
*/
suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray>
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
/** Interactor in charge of removing a fingerprint */
interface RemoveFingerprintInteractor {
/**
* Removes the given fingerprint, returning true if it was successfully removed and false
* otherwise
*/
suspend fun removeFingerprint(fp: FingerprintData): Boolean
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
/** Interactor that can rename a fingerprint. */
interface RenameFingerprintInteractor {
/** Renames the given fingerprint if one exists */
suspend fun renameFingerprint(fp: FingerprintData, newName: String)
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2024 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.biometrics.fingerprint2.lib.domain.interactor
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import kotlinx.coroutines.flow.Flow
/** Interactor that has various information about a fingerprint sensor */
interface SensorInteractor {
/** Retrieves the sensor properties of the device */
val sensorPropertiesInternal: Flow<FingerprintSensor?>
/** Indicates if the device supports side fps */
val hasSideFps: Flow<Boolean>
}

View File

@@ -96,8 +96,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
} }
/** /**
* View models below this line are not used by this class but must be initialized * View models below this line are not used by this class but must be initialized in the activity
* in the activity view model store to be used by other view models. * view model store to be used by other view models.
*/ */
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel by viewModels { private val fingerprintEnrollViewModel: FingerprintEnrollViewModel by viewModels {
FingerprintEnrollViewModel.Factory FingerprintEnrollViewModel.Factory

View File

@@ -25,7 +25,7 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
@@ -49,7 +49,7 @@ class RFPSViewModel(
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel, private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel, private val navigationViewModel: FingerprintNavigationViewModel,
orientationInteractor: OrientationInteractor, orientationInteractor: OrientationInteractor,
private val fingerprintManager: FingerprintManagerInteractor, private val sensorInteractor: SensorInteractor,
) : ViewModel() { ) : ViewModel() {
private val _textViewIsVisible = MutableStateFlow(false) private val _textViewIsVisible = MutableStateFlow(false)
@@ -62,7 +62,7 @@ class RFPSViewModel(
val shouldAnimateIcon = _shouldAnimateIcon val shouldAnimateIcon = _shouldAnimateIcon
private var enrollFlow: Flow<FingerEnrollState?> = private var enrollFlow: Flow<FingerEnrollState?> =
fingerprintManager.sensorPropertiesInternal.filterNotNull().combine( sensorInteractor.sensorPropertiesInternal.filterNotNull().combine(
fingerprintEnrollViewModel.enrollFlow fingerprintEnrollViewModel.enrollFlow
) { props, enroll -> ) { props, enroll ->
if (props.sensorType == FingerprintSensorType.REAR) { if (props.sensorType == FingerprintSensorType.REAR) {
@@ -181,7 +181,7 @@ class RFPSViewModel(
provider[FingerprintEnrollEnrollingViewModel::class], provider[FingerprintEnrollEnrollingViewModel::class],
provider[FingerprintNavigationViewModel::class], provider[FingerprintNavigationViewModel::class],
biometricEnvironment.orientationInteractor, biometricEnvironment.orientationInteractor,
biometricEnvironment.fingerprintManagerInteractor, biometricEnvironment.createSensorPropertiesInteractor(),
) )
} }
} }

View File

@@ -38,7 +38,7 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Orientatio
import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText
@@ -76,17 +76,17 @@ class UdfpsViewModel(
enrollStageInteractor: EnrollStageInteractor, enrollStageInteractor: EnrollStageInteractor,
orientationInteractor: OrientationInteractor, orientationInteractor: OrientationInteractor,
udfpsEnrollInteractor: UdfpsEnrollInteractor, udfpsEnrollInteractor: UdfpsEnrollInteractor,
fingerprintManager: FingerprintManagerInteractor,
accessibilityInteractor: AccessibilityInteractor, accessibilityInteractor: AccessibilityInteractor,
sensorRepository: FingerprintSensorInteractor, sensorRepository: FingerprintSensorInteractor,
touchEventInteractor: TouchEventInteractor, touchEventInteractor: TouchEventInteractor,
sensorInteractor: SensorInteractor,
) : ViewModel() { ) : ViewModel() {
private val isSetupWizard = flowOf(false) private val isSetupWizard = flowOf(false)
private var shouldResetErollment = false private var shouldResetErollment = false
private var _enrollState: Flow<FingerEnrollState?> = private var _enrollState: Flow<FingerEnrollState?> =
fingerprintManager.sensorPropertiesInternal.filterNotNull().combine( sensorInteractor.sensorPropertiesInternal.filterNotNull().combine(
fingerprintEnrollEnrollingViewModel.enrollFlow fingerprintEnrollEnrollingViewModel.enrollFlow
) { props, enroll -> ) { props, enroll ->
if (props.sensorType.isUdfps()) { if (props.sensorType.isUdfps()) {
@@ -198,8 +198,7 @@ class UdfpsViewModel(
.distinctUntilChanged() .distinctUntilChanged()
private val _touchEvent: MutableStateFlow<MotionEvent?> = MutableStateFlow(null) private val _touchEvent: MutableStateFlow<MotionEvent?> = MutableStateFlow(null)
val touchEvent = val touchEvent = _touchEvent.asStateFlow().filterNotNull()
_touchEvent.asStateFlow().filterNotNull()
/** Determines the current [EnrollStageModel] enrollment is in */ /** Determines the current [EnrollStageModel] enrollment is in */
private val enrollStage: Flow<EnrollStageModel> = private val enrollStage: Flow<EnrollStageModel> =
@@ -267,11 +266,7 @@ class UdfpsViewModel(
backgroundViewModel.background.filter { it }.collect { didGoToBackground() } backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
} }
viewModelScope.launch { viewModelScope.launch { touchEventInteractor.touchEvent.collect { _touchEvent.update { it } } }
touchEventInteractor.touchEvent.collect {
_touchEvent.update { it }
}
}
} }
/** Indicates if we should show the lottie. */ /** Indicates if we should show the lottie. */
@@ -430,10 +425,10 @@ class UdfpsViewModel(
biometricEnvironment.enrollStageInteractor, biometricEnvironment.enrollStageInteractor,
biometricEnvironment.orientationInteractor, biometricEnvironment.orientationInteractor,
biometricEnvironment.udfpsEnrollInteractor, biometricEnvironment.udfpsEnrollInteractor,
biometricEnvironment.fingerprintManagerInteractor,
biometricEnvironment.accessibilityInteractor, biometricEnvironment.accessibilityInteractor,
biometricEnvironment.sensorInteractor, biometricEnvironment.sensorInteractor,
biometricEnvironment.touchEventInteractor, biometricEnvironment.touchEventInteractor,
biometricEnvironment.createSensorPropertiesInteractor(),
) )
} }
} }

View File

@@ -16,27 +16,27 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import android.util.Log
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** Models the UI state for [FingerprintEnrollConfirmationV2Fragment] */ /** Models the UI state for [FingerprintEnrollConfirmationV2Fragment] */
class FingerprintEnrollConfirmationViewModel( class FingerprintEnrollConfirmationViewModel(
private val navigationViewModel: FingerprintNavigationViewModel, private val navigationViewModel: FingerprintNavigationViewModel,
fingerprintInteractor: FingerprintManagerInteractor, private val canEnrollFingerprintsInteractor: CanEnrollFingerprintsInteractor,
) : ViewModel() { ) : ViewModel() {
/** /**
* Indicates if the add another button is possible. This should only be true when the user is able * Indicates if the add another button is possible. This should only be true when the user is able
* to enroll more fingerprints. * to enroll more fingerprints.
*/ */
val isAddAnotherButtonVisible: Flow<Boolean> = fingerprintInteractor.canEnrollFingerprints val isAddAnotherButtonVisible: Flow<Boolean> =
canEnrollFingerprintsInteractor.canEnrollFingerprints
/** /**
* Indicates that the user has clicked the next button and is done with fingerprint enrollment. * Indicates that the user has clicked the next button and is done with fingerprint enrollment.
@@ -64,7 +64,7 @@ class FingerprintEnrollConfirmationViewModel(
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollConfirmationViewModel( FingerprintEnrollConfirmationViewModel(
provider[FingerprintNavigationViewModel::class], provider[FingerprintNavigationViewModel::class],
biometricEnvironment!!.fingerprintManagerInteractor, biometricEnvironment!!.createCanEnrollFingerprintsInteractor(),
) )
} }
} }

View File

@@ -27,7 +27,7 @@ import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
@@ -52,12 +52,12 @@ class FingerprintEnrollFindSensorViewModel(
accessibilityInteractor: AccessibilityInteractor, accessibilityInteractor: AccessibilityInteractor,
foldStateInteractor: FoldStateInteractor, foldStateInteractor: FoldStateInteractor,
orientationInteractor: OrientationInteractor, orientationInteractor: OrientationInteractor,
fingerprintManagerInteractor: FingerprintManagerInteractor, sensorInteractor: SensorInteractor,
) : ViewModel() { ) : ViewModel() {
/** Represents the stream of sensor type. */ /** Represents the stream of sensor type. */
val sensorType: Flow<FingerprintSensorType> = val sensorType: Flow<FingerprintSensorType> =
fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType } sensorInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
private val _isUdfps: Flow<Boolean> = private val _isUdfps: Flow<Boolean> =
sensorType.map { sensorType.map {
it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC
@@ -216,7 +216,7 @@ class FingerprintEnrollFindSensorViewModel(
biometricEnvironment.accessibilityInteractor, biometricEnvironment.accessibilityInteractor,
biometricEnvironment.foldStateInteractor, biometricEnvironment.foldStateInteractor,
biometricEnvironment.orientationInteractor, biometricEnvironment.orientationInteractor,
biometricEnvironment.fingerprintManagerInteractor, biometricEnvironment.createSensorPropertiesInteractor(),
) )
} }
} }

View File

@@ -22,7 +22,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Introduction import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Introduction
import com.android.systemui.biometrics.shared.model.FingerprintSensor import com.android.systemui.biometrics.shared.model.FingerprintSensor
@@ -32,11 +32,11 @@ import kotlinx.coroutines.flow.Flow
class FingerprintEnrollIntroViewModel( class FingerprintEnrollIntroViewModel(
val navigationViewModel: FingerprintNavigationViewModel, val navigationViewModel: FingerprintNavigationViewModel,
fingerprintFlowViewModel: FingerprintFlowViewModel, fingerprintFlowViewModel: FingerprintFlowViewModel,
fingerprintManagerInteractor: FingerprintManagerInteractor, sensorInteractor: SensorInteractor,
) : ViewModel() { ) : ViewModel() {
/** Represents a stream of [FingerprintSensor] */ /** Represents a stream of [FingerprintSensor] */
val sensor: Flow<FingerprintSensor?> = fingerprintManagerInteractor.sensorPropertiesInternal val sensor: Flow<FingerprintSensor?> = sensorInteractor.sensorPropertiesInternal
/** Represents a stream of [FingerprintFlow] */ /** Represents a stream of [FingerprintFlow] */
val fingerprintFlow: Flow<FingerprintFlow?> = fingerprintFlowViewModel.fingerprintFlow val fingerprintFlow: Flow<FingerprintFlow?> = fingerprintFlowViewModel.fingerprintFlow
@@ -67,7 +67,7 @@ class FingerprintEnrollIntroViewModel(
FingerprintEnrollIntroViewModel( FingerprintEnrollIntroViewModel(
provider[FingerprintNavigationViewModel::class], provider[FingerprintNavigationViewModel::class],
provider[FingerprintFlowViewModel::class], provider[FingerprintFlowViewModel::class],
biometricEnvironment!!.fingerprintManagerInteractor, biometricEnvironment!!.createSensorPropertiesInteractor(),
) )
} }
} }

View File

@@ -24,7 +24,8 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
@@ -42,9 +43,10 @@ import kotlinx.coroutines.flow.update
/** Represents all of the fingerprint information needed for a fingerprint enrollment process. */ /** Represents all of the fingerprint information needed for a fingerprint enrollment process. */
class FingerprintEnrollViewModel( class FingerprintEnrollViewModel(
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
gatekeeperViewModel: FingerprintGatekeeperViewModel, gatekeeperViewModel: FingerprintGatekeeperViewModel,
val navigationViewModel: FingerprintNavigationViewModel, val navigationViewModel: FingerprintNavigationViewModel,
private val sensorInteractor: SensorInteractor,
private val fingerprintEnrollInteractor: EnrollFingerprintInteractor,
) : ViewModel() { ) : ViewModel() {
/** /**
@@ -67,7 +69,7 @@ class FingerprintEnrollViewModel(
/** Represents the stream of [FingerprintSensorType] */ /** Represents the stream of [FingerprintSensorType] */
val sensorType: Flow<FingerprintSensorType?> = val sensorType: Flow<FingerprintSensorType?> =
fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType } sensorInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
/** /**
* A flow that contains a [FingerprintEnrollViewModel] which contains the relevant information for * A flow that contains a [FingerprintEnrollViewModel] which contains the relevant information for
@@ -90,7 +92,7 @@ class FingerprintEnrollViewModel(
enrollReason != null && enrollReason != null &&
enrollOptions != null enrollOptions != null
) { ) {
fingerprintManagerInteractor fingerprintEnrollInteractor
.enroll(hardwareAuthToken.token, enrollReason, enrollOptions) .enroll(hardwareAuthToken.token, enrollReason, enrollOptions)
.collect { emit(it) } .collect { emit(it) }
} }
@@ -137,9 +139,10 @@ class FingerprintEnrollViewModel(
val biometricEnvironment = settingsApplication.biometricEnvironment val biometricEnvironment = settingsApplication.biometricEnvironment
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollViewModel( FingerprintEnrollViewModel(
biometricEnvironment!!.fingerprintManagerInteractor,
provider[FingerprintGatekeeperViewModel::class], provider[FingerprintGatekeeperViewModel::class],
provider[FingerprintNavigationViewModel::class], provider[FingerprintNavigationViewModel::class],
biometricEnvironment!!.createSensorPropertiesInteractor(),
biometricEnvironment!!.createFingerprintEnrollInteractor(),
) )
} }
} }

View File

@@ -24,7 +24,7 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -50,7 +50,7 @@ sealed interface GatekeeperInfo {
* in as a parameter to this class. * in as a parameter to this class.
*/ */
class FingerprintGatekeeperViewModel( class FingerprintGatekeeperViewModel(
private val fingerprintManagerInteractor: FingerprintManagerInteractor private val generateChallengeInteractor: GenerateChallengeInteractor
) : ViewModel() { ) : ViewModel() {
private var _gatekeeperInfo: MutableStateFlow<GatekeeperInfo?> = MutableStateFlow(null) private var _gatekeeperInfo: MutableStateFlow<GatekeeperInfo?> = MutableStateFlow(null)
@@ -78,7 +78,7 @@ class FingerprintGatekeeperViewModel(
_gatekeeperInfo.update { GatekeeperInfo.Invalid } _gatekeeperInfo.update { GatekeeperInfo.Invalid }
} else { } else {
viewModelScope.launch { viewModelScope.launch {
val res = fingerprintManagerInteractor.generateChallenge(theGatekeeperPasswordHandle!!) val res = generateChallengeInteractor.generateChallenge(theGatekeeperPasswordHandle!!)
_gatekeeperInfo.update { GatekeeperInfo.GatekeeperPasswordInfo(res.second, res.first) } _gatekeeperInfo.update { GatekeeperInfo.GatekeeperPasswordInfo(res.second, res.first) }
if (shouldStartTimer) { if (shouldStartTimer) {
startTimeout() startTimeout()
@@ -119,7 +119,7 @@ class FingerprintGatekeeperViewModel(
val settingsApplication = val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment val biometricEnvironment = settingsApplication.biometricEnvironment
FingerprintGatekeeperViewModel(biometricEnvironment!!.fingerprintManagerInteractor) FingerprintGatekeeperViewModel(biometricEnvironment!!.createGenerateChallengeInteractor())
} }
} }
} }

View File

@@ -23,7 +23,7 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Finish import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Finish
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep
@@ -46,7 +46,7 @@ import kotlinx.coroutines.flow.update
* fragments/viewmodels that want to consume these events. It should provide no additional * fragments/viewmodels that want to consume these events. It should provide no additional
* functionality beyond what is available in [FingerprintNavigationStep]. * functionality beyond what is available in [FingerprintNavigationStep].
*/ */
class FingerprintNavigationViewModel(fingerprintManagerInteractor: FingerprintManagerInteractor) : class FingerprintNavigationViewModel(sensorInteractor: SensorInteractor) :
ViewModel() { ViewModel() {
private val _flowInternal: MutableStateFlow<FingerprintFlow?> = MutableStateFlow(null) private val _flowInternal: MutableStateFlow<FingerprintFlow?> = MutableStateFlow(null)
@@ -55,7 +55,7 @@ class FingerprintNavigationViewModel(fingerprintManagerInteractor: FingerprintMa
combine( combine(
_flowInternal, _flowInternal,
_hasConfirmedDeviceCredential, _hasConfirmedDeviceCredential,
fingerprintManagerInteractor.sensorPropertiesInternal, sensorInteractor.sensorPropertiesInternal,
) { flow, hasConfirmed, sensorType -> ) { flow, hasConfirmed, sensorType ->
if (flow == null || sensorType == null) { if (flow == null || sensorType == null) {
return@combine null return@combine null
@@ -144,7 +144,7 @@ class FingerprintNavigationViewModel(fingerprintManagerInteractor: FingerprintMa
val settingsApplication = val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment val biometricEnvironment = settingsApplication.biometricEnvironment
FingerprintNavigationViewModel(biometricEnvironment!!.fingerprintManagerInteractor) FingerprintNavigationViewModel(biometricEnvironment!!.createSensorPropertiesInteractor())
} }
} }
} }

View File

@@ -35,19 +35,16 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceCategory import androidx.preference.PreferenceCategory
import com.android.internal.widget.LockPatternUtils
import com.android.settings.R import com.android.settings.R
import com.android.settings.SettingsApplication
import com.android.settings.Utils.SETTINGS_PACKAGE_NAME import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
import com.android.settings.biometrics.BiometricEnrollBase import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
import com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY import com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
@@ -223,35 +220,24 @@ class FingerprintSettingsV2Fragment :
val fingerprintSensorProvider = val fingerprintSensorProvider =
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope) FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher) val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
val fingerprintEnrollStateRepository =
FingerprintEnrollInteractorImpl(
requireContext().applicationContext,
fingerprintManager,
Settings,
)
val interactor =
FingerprintManagerInteractorImpl(
context.applicationContext,
backgroundDispatcher,
fingerprintManager,
fingerprintSensorProvider,
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
fingerprintEnrollStateRepository,
)
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN) val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
val challenge = intent.getLongExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, -1L) val challenge = intent.getLongExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, -1L)
val application = requireActivity().application as SettingsApplication
val environment =
application.biometricEnvironment
?: throw IllegalStateException("The biometric environment must be present")
navigationViewModel = navigationViewModel =
ViewModelProvider( ViewModelProvider(
this, this,
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory( FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
userId, userId,
interactor,
backgroundDispatcher, backgroundDispatcher,
token, token,
challenge, challenge,
environment.createFingerprintsEnrolledInteractor(),
environment.createGenerateChallengeInteractor(),
), ),
)[FingerprintSettingsNavigationViewModel::class.java] )[FingerprintSettingsNavigationViewModel::class.java]
@@ -260,9 +246,14 @@ class FingerprintSettingsV2Fragment :
this, this,
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory( FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
userId, userId,
interactor,
backgroundDispatcher, backgroundDispatcher,
navigationViewModel, navigationViewModel,
environment.createCanEnrollFingerprintsInteractor(),
environment.createSensorPropertiesInteractor(),
environment.createAuthenticateInteractor(),
environment.createRenameFingerprintInteractor(),
environment.createRemoveFingerprintInteractor(),
environment.createFingerprintsEnrolledInteractor(),
), ),
)[FingerprintSettingsViewModel::class.java] )[FingerprintSettingsViewModel::class.java]

View File

@@ -21,7 +21,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.BiometricEnrollBase import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -33,10 +34,11 @@ import kotlinx.coroutines.launch
/** A Viewmodel that represents the navigation of the FingerprintSettings activity. */ /** A Viewmodel that represents the navigation of the FingerprintSettings activity. */
class FingerprintSettingsNavigationViewModel( class FingerprintSettingsNavigationViewModel(
private val userId: Int, private val userId: Int,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
private val backgroundDispatcher: CoroutineDispatcher, private val backgroundDispatcher: CoroutineDispatcher,
tokenInit: ByteArray?, tokenInit: ByteArray?,
challengeInit: Long?, challengeInit: Long?,
private val enrolledFingerprintsInteractor: EnrolledFingerprintsInteractor,
private val generateChallengeInteractor: GenerateChallengeInteractor,
) : ViewModel() { ) : ViewModel() {
private var token = tokenInit private var token = tokenInit
@@ -52,7 +54,7 @@ class FingerprintSettingsNavigationViewModel(
_nextStep.update { LaunchConfirmDeviceCredential(userId) } _nextStep.update { LaunchConfirmDeviceCredential(userId) }
} else { } else {
viewModelScope.launch { viewModelScope.launch {
if (fingerprintManagerInteractor.enrolledFingerprints.last()?.isEmpty() == true) { if (enrolledFingerprintsInteractor.enrolledFingerprints.last()?.isEmpty() == true) {
_nextStep.update { EnrollFirstFingerprint(userId, null, challenge, token) } _nextStep.update { EnrollFirstFingerprint(userId, null, challenge, token) }
} else { } else {
showSettingsHelper() showSettingsHelper()
@@ -148,13 +150,13 @@ class FingerprintSettingsNavigationViewModel(
} }
private suspend fun launchEnrollNextStep(gateKeeperPasswordHandle: Long?) { private suspend fun launchEnrollNextStep(gateKeeperPasswordHandle: Long?) {
fingerprintManagerInteractor.enrolledFingerprints.collect { enrolledFingerprintsInteractor.enrolledFingerprints.collect {
if (it?.isEmpty() == true) { if (it?.isEmpty() == true) {
_nextStep.update { EnrollFirstFingerprint(userId, gateKeeperPasswordHandle, null, null) } _nextStep.update { EnrollFirstFingerprint(userId, gateKeeperPasswordHandle, null, null) }
} else { } else {
viewModelScope.launch(backgroundDispatcher) { viewModelScope.launch(backgroundDispatcher) {
val challengePair = val challengePair =
fingerprintManagerInteractor.generateChallenge(gateKeeperPasswordHandle!!) generateChallengeInteractor.generateChallenge(gateKeeperPasswordHandle!!)
challenge = challengePair.first challenge = challengePair.first
token = challengePair.second token = challengePair.second
@@ -174,10 +176,11 @@ class FingerprintSettingsNavigationViewModel(
class FingerprintSettingsNavigationModelFactory( class FingerprintSettingsNavigationModelFactory(
private val userId: Int, private val userId: Int,
private val interactor: FingerprintManagerInteractor,
private val backgroundDispatcher: CoroutineDispatcher, private val backgroundDispatcher: CoroutineDispatcher,
private val token: ByteArray?, private val token: ByteArray?,
private val challenge: Long?, private val challenge: Long?,
private val enrolledFingerprintsInteractor: EnrolledFingerprintsInteractor,
private val generateChallengeInteractor: GenerateChallengeInteractor,
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -185,10 +188,11 @@ class FingerprintSettingsNavigationViewModel(
return FingerprintSettingsNavigationViewModel( return FingerprintSettingsNavigationViewModel(
userId, userId,
interactor,
backgroundDispatcher, backgroundDispatcher,
token, token,
challenge, challenge,
enrolledFingerprintsInteractor,
generateChallengeInteractor,
) )
as T as T
} }

View File

@@ -21,7 +21,12 @@ import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RemoveFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RenameFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.FingerprintSensorType
@@ -49,9 +54,14 @@ private const val DEBUG = false
/** Models the UI state for fingerprint settings. */ /** Models the UI state for fingerprint settings. */
class FingerprintSettingsViewModel( class FingerprintSettingsViewModel(
private val userId: Int, private val userId: Int,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
private val backgroundDispatcher: CoroutineDispatcher, private val backgroundDispatcher: CoroutineDispatcher,
private val navigationViewModel: FingerprintSettingsNavigationViewModel, private val navigationViewModel: FingerprintSettingsNavigationViewModel,
private val canEnrollFingerprintsInteractor: CanEnrollFingerprintsInteractor,
private val sensorInteractor: SensorInteractor,
private val authenticateInteractor: AuthenitcateInteractor,
private val renameFingerprintInteractor: RenameFingerprintInteractor,
private val removeFingerprintInteractor: RemoveFingerprintInteractor,
private val enrolledFingerprintsInteractor: EnrolledFingerprintsInteractor,
) : ViewModel() { ) : ViewModel() {
private val _enrolledFingerprints: MutableStateFlow<List<FingerprintData>?> = private val _enrolledFingerprints: MutableStateFlow<List<FingerprintData>?> =
MutableStateFlow(null) MutableStateFlow(null)
@@ -62,19 +72,18 @@ class FingerprintSettingsViewModel(
/** Represents the stream of the information of "Add Fingerprint" preference. */ /** Represents the stream of the information of "Add Fingerprint" preference. */
val addFingerprintPrefInfo: Flow<Pair<Boolean, Int>> = val addFingerprintPrefInfo: Flow<Pair<Boolean, Int>> =
_enrolledFingerprints.filterOnlyWhenSettingsIsShown().transform { _enrolledFingerprints.filterOnlyWhenSettingsIsShown().combine(
emit( canEnrollFingerprintsInteractor.canEnrollFingerprints
Pair( ) { _, canEnrollFingerprints ->
fingerprintManagerInteractor.canEnrollFingerprints.first(), Pair(canEnrollFingerprints, canEnrollFingerprintsInteractor.maxFingerprintsEnrollable())
fingerprintManagerInteractor.maxEnrollableFingerprints.first(),
)
)
} }
/** Represents the stream of visibility of sfps preference. */ /** Represents the stream of visibility of sfps preference. */
val isSfpsPrefVisible: Flow<Boolean> = val isSfpsPrefVisible: Flow<Boolean> =
_enrolledFingerprints.filterOnlyWhenSettingsIsShown().transform { _enrolledFingerprints.filterOnlyWhenSettingsIsShown().combine(sensorInteractor.hasSideFps) {
emit(fingerprintManagerInteractor.hasSideFps() == true && !it.isNullOrEmpty()) fingerprints,
hasSideFps ->
hasSideFps && !fingerprints.isNullOrEmpty()
} }
private val _isShowingDialog: MutableStateFlow<PreferenceViewModel?> = MutableStateFlow(null) private val _isShowingDialog: MutableStateFlow<PreferenceViewModel?> = MutableStateFlow(null)
@@ -90,10 +99,10 @@ class FingerprintSettingsViewModel(
private val _consumerShouldAuthenticate: MutableStateFlow<Boolean> = MutableStateFlow(false) private val _consumerShouldAuthenticate: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val _fingerprintSensorType: Flow<FingerprintSensorType> = private val _fingerprintSensorType: Flow<FingerprintSensorType> =
fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType } sensorInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
private val _sensorNullOrEmpty: Flow<Boolean> = private val _sensorNullOrEmpty: Flow<Boolean> =
fingerprintManagerInteractor.sensorPropertiesInternal.map { it == null } sensorInteractor.sensorPropertiesInternal.map { it == null }
private val _isLockedOut: MutableStateFlow<FingerprintAuthAttemptModel.Error?> = private val _isLockedOut: MutableStateFlow<FingerprintAuthAttemptModel.Error?> =
MutableStateFlow(null) MutableStateFlow(null)
@@ -172,7 +181,7 @@ class FingerprintSettingsViewModel(
while (it && navigationViewModel.nextStep.value is ShowSettings) { while (it && navigationViewModel.nextStep.value is ShowSettings) {
Log.d(TAG, "canAuthenticate authing") Log.d(TAG, "canAuthenticate authing")
attemptingAuth() attemptingAuth()
when (val authAttempt = fingerprintManagerInteractor.authenticate()) { when (val authAttempt = authenticateInteractor.authenticate()) {
is FingerprintAuthAttemptModel.Success -> { is FingerprintAuthAttemptModel.Success -> {
onAuthSuccess(authAttempt) onAuthSuccess(authAttempt)
emit(authAttempt) emit(authAttempt)
@@ -243,7 +252,7 @@ class FingerprintSettingsViewModel(
/** A request to delete a fingerprint */ /** A request to delete a fingerprint */
fun deleteFingerprint(fp: FingerprintData) { fun deleteFingerprint(fp: FingerprintData) {
viewModelScope.launch(backgroundDispatcher) { viewModelScope.launch(backgroundDispatcher) {
if (fingerprintManagerInteractor.removeFingerprint(fp)) { if (removeFingerprintInteractor.removeFingerprint(fp)) {
updateEnrolledFingerprints() updateEnrolledFingerprints()
} }
} }
@@ -252,7 +261,7 @@ class FingerprintSettingsViewModel(
/** A request to rename a fingerprint */ /** A request to rename a fingerprint */
fun renameFingerprint(fp: FingerprintData, newName: String) { fun renameFingerprint(fp: FingerprintData, newName: String) {
viewModelScope.launch { viewModelScope.launch {
fingerprintManagerInteractor.renameFingerprint(fp, newName) renameFingerprintInteractor.renameFingerprint(fp, newName)
updateEnrolledFingerprints() updateEnrolledFingerprints()
} }
} }
@@ -271,7 +280,7 @@ class FingerprintSettingsViewModel(
} }
private suspend fun updateEnrolledFingerprints() { private suspend fun updateEnrolledFingerprints() {
_enrolledFingerprints.update { fingerprintManagerInteractor.enrolledFingerprints.first() } _enrolledFingerprints.update { enrolledFingerprintsInteractor.enrolledFingerprints.first() }
} }
/** Used to indicate whether the consumer of the view model is ready for authentication. */ /** Used to indicate whether the consumer of the view model is ready for authentication. */
@@ -288,9 +297,14 @@ class FingerprintSettingsViewModel(
class FingerprintSettingsViewModelFactory( class FingerprintSettingsViewModelFactory(
private val userId: Int, private val userId: Int,
private val interactor: FingerprintManagerInteractor,
private val backgroundDispatcher: CoroutineDispatcher, private val backgroundDispatcher: CoroutineDispatcher,
private val navigationViewModel: FingerprintSettingsNavigationViewModel, private val navigationViewModel: FingerprintSettingsNavigationViewModel,
private val canEnrollFingerprintsInteractor: CanEnrollFingerprintsInteractor,
private val sensorInteractor: SensorInteractor,
private val authenticateInteractor: AuthenitcateInteractor,
private val renameFingerprintInteractor: RenameFingerprintInteractor,
private val removeFingerprintInteractor: RemoveFingerprintInteractor,
private val enrolledFingerprintsInteractor: EnrolledFingerprintsInteractor,
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -298,9 +312,14 @@ class FingerprintSettingsViewModel(
return FingerprintSettingsViewModel( return FingerprintSettingsViewModel(
userId, userId,
interactor,
backgroundDispatcher, backgroundDispatcher,
navigationViewModel, navigationViewModel,
canEnrollFingerprintsInteractor,
sensorInteractor,
authenticateInteractor,
renameFingerprintInteractor,
removeFingerprintInteractor,
enrolledFingerprintsInteractor,
) )
as T as T
} }

View File

@@ -111,9 +111,10 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
var fingerprintEnrollViewModel = var fingerprintEnrollViewModel =
FingerprintEnrollViewModel( FingerprintEnrollViewModel(
fingerprintManagerInteractor,
gatekeeperViewModel, gatekeeperViewModel,
navigationViewModel, navigationViewModel,
fingerprintManagerInteractor,
fingerprintManagerInteractor,
) )
var fingerprintEnrollEnrollingViewModel = var fingerprintEnrollEnrollingViewModel =

View File

@@ -22,7 +22,14 @@ import android.hardware.biometrics.SensorProperties
import android.hardware.fingerprint.FingerprintEnrollOptions import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RemoveFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RenameFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
@@ -35,7 +42,15 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
/** Fake to be used by other classes to easily fake the FingerprintManager implementation. */ /** Fake to be used by other classes to easily fake the FingerprintManager implementation. */
class FakeFingerprintManagerInteractor : FingerprintManagerInteractor { class FakeFingerprintManagerInteractor :
AuthenitcateInteractor,
CanEnrollFingerprintsInteractor,
EnrolledFingerprintsInteractor,
EnrollFingerprintInteractor,
GenerateChallengeInteractor,
RemoveFingerprintInteractor,
RenameFingerprintInteractor,
SensorInteractor {
var enrollableFingerprints: Int = 5 var enrollableFingerprints: Int = 5
var enrolledFingerprintsInternal: MutableList<FingerprintData> = mutableListOf() var enrolledFingerprintsInternal: MutableList<FingerprintData> = mutableListOf()
@@ -67,19 +82,22 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
override val enrolledFingerprints: Flow<List<FingerprintData>> = flow { override val enrolledFingerprints: Flow<List<FingerprintData>> = flow {
emit(enrolledFingerprintsInternal) emit(enrolledFingerprintsInternal)
} }
override val canEnrollFingerprints: Flow<Boolean> = flow { override val canEnrollFingerprints: Flow<Boolean> = flow {
emit(enrolledFingerprintsInternal.size < enrollableFingerprints) emit(enrolledFingerprintsInternal.size < enrollableFingerprints)
} }
override val sensorPropertiesInternal: Flow<FingerprintSensor?> = flow { emit(sensorProp) } override fun maxFingerprintsEnrollable(): Int {
return enrollableFingerprints
}
override val maxEnrollableFingerprints: Flow<Int> = flow { emit(enrollableFingerprints) } override val sensorPropertiesInternal: Flow<FingerprintSensor?> = flow { emit(sensorProp) }
override val hasSideFps: Flow<Boolean> =
flowOf(sensorProp.sensorType == FingerprintSensorType.POWER_BUTTON)
override suspend fun enroll( override suspend fun enroll(
hardwareAuthToken: ByteArray?, hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason, enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState> = flowOf(*enrollStateViewModel.toTypedArray()) ): Flow<FingerEnrollState> = flowOf(*enrollStateViewModel.toTypedArray())
override suspend fun removeFingerprint(fp: FingerprintData): Boolean { override suspend fun removeFingerprint(fp: FingerprintData): Boolean {
@@ -92,7 +110,4 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
} }
} }
override suspend fun hasSideFps(): Boolean {
return sensorProp.sensorType == FingerprintSensorType.POWER_BUTTON
}
} }

View File

@@ -16,7 +16,6 @@
package com.android.settings.fingerprint2.domain.interactor package com.android.settings.fingerprint2.domain.interactor
import android.content.Context
import android.content.Intent import android.content.Intent
import android.hardware.biometrics.ComponentInfoInternal import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.biometrics.SensorLocationInternal import android.hardware.biometrics.SensorLocationInternal
@@ -30,23 +29,37 @@ import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.CancellationSignal import android.os.CancellationSignal
import android.os.Handler import android.os.Handler
import androidx.test.core.app.ApplicationProvider
import com.android.settings.biometrics.GatekeeperPasswordProvider import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSettingsRepositoryImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl import com.android.settings.biometrics.fingerprint2.data.repository.UserRepo
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.AuthenticateInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.CanEnrollFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollFingerprintInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrolledFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.GenerateChallengeInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.RemoveFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.RenameFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RemoveFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RenameFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.Default import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.password.ChooseLockSettingsHelper import com.android.settings.password.ChooseLockSettingsHelper
import com.android.systemui.biometrics.shared.model.FingerprintSensor import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.toFingerprintSensor import com.android.systemui.biometrics.shared.model.toFingerprintSensor
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.last import kotlinx.coroutines.flow.last
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -75,13 +88,28 @@ import org.mockito.stubbing.OngoingStubbing
class FingerprintManagerInteractorTest { class FingerprintManagerInteractorTest {
@JvmField @Rule var rule = MockitoJUnit.rule() @JvmField @Rule var rule = MockitoJUnit.rule()
private lateinit var underTest: FingerprintManagerInteractor private lateinit var enrolledFingerprintsInteractorUnderTest: EnrolledFingerprintsInteractor
private var context: Context = ApplicationProvider.getApplicationContext() private lateinit var generateChallengeInteractorUnderTest: GenerateChallengeInteractor
private lateinit var removeFingerprintsInteractorUnderTest: RemoveFingerprintInteractor
private lateinit var renameFingerprintsInteractorUnderTest: RenameFingerprintInteractor
private lateinit var authenticateInteractorImplUnderTest: AuthenticateInteractorImpl
private lateinit var canEnrollFingerprintsInteractorUnderTest: CanEnrollFingerprintsInteractor
private lateinit var enrollInteractorUnderTest: EnrollFingerprintInteractor
private val userId = 0
private var backgroundDispatcher = StandardTestDispatcher() private var backgroundDispatcher = StandardTestDispatcher()
@Mock private lateinit var fingerprintManager: FingerprintManager @Mock private lateinit var fingerprintManager: FingerprintManager
@Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider @Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider
private var testScope = TestScope(backgroundDispatcher) private var testScope = TestScope(backgroundDispatcher)
private var backgroundScope = testScope.backgroundScope
private val flow: FingerprintFlow = Default
private val maxFingerprints = 5
private val currUser = MutableStateFlow(0)
private val userRepo =
object : UserRepo {
override val currentUser: Flow<Int> = currUser
}
@Before @Before
fun setup() { fun setup() {
@@ -89,7 +117,7 @@ class FingerprintManagerInteractorTest {
FingerprintSensorPropertiesInternal( FingerprintSensorPropertiesInternal(
0 /* sensorId */, 0 /* sensorId */,
SensorProperties.STRENGTH_STRONG, SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */, maxFingerprints,
listOf<ComponentInfoInternal>(), listOf<ComponentInfoInternal>(),
FingerprintSensorProperties.TYPE_POWER_BUTTON, FingerprintSensorProperties.TYPE_POWER_BUTTON,
false /* halControlsIllumination */, false /* halControlsIllumination */,
@@ -97,20 +125,37 @@ class FingerprintManagerInteractorTest {
listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT), listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
) )
.toFingerprintSensor() .toFingerprintSensor()
val fingerprintSensorRepository = val fingerprintSensorRepository =
object : FingerprintSensorRepository { object : FingerprintSensorRepository {
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensor) override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensor)
override val hasSideFps: Flow<Boolean> = flowOf(false)
} }
underTest = val settingsRepository = FingerprintSettingsRepositoryImpl(maxFingerprints)
FingerprintManagerInteractorImpl( val fingerprintEnrollmentRepository =
context, FingerprintEnrollmentRepositoryImpl(
backgroundDispatcher,
fingerprintManager, fingerprintManager,
fingerprintSensorRepository, userRepo,
gateKeeperPasswordProvider, settingsRepository,
FingerprintEnrollInteractorImpl(context, fingerprintManager, Default), backgroundDispatcher,
backgroundScope,
) )
enrolledFingerprintsInteractorUnderTest =
EnrolledFingerprintsInteractorImpl(fingerprintManager, userId)
generateChallengeInteractorUnderTest =
GenerateChallengeInteractorImpl(fingerprintManager, userId, gateKeeperPasswordProvider)
removeFingerprintsInteractorUnderTest =
RemoveFingerprintsInteractorImpl(fingerprintManager, userId)
renameFingerprintsInteractorUnderTest =
RenameFingerprintsInteractorImpl(fingerprintManager, userId, backgroundDispatcher)
authenticateInteractorImplUnderTest = AuthenticateInteractorImpl(fingerprintManager, userId)
canEnrollFingerprintsInteractorUnderTest =
CanEnrollFingerprintsInteractorImpl(fingerprintEnrollmentRepository)
enrollInteractorUnderTest = EnrollFingerprintInteractorImpl(userId, fingerprintManager, flow)
} }
@Test @Test
@@ -119,7 +164,8 @@ class FingerprintManagerInteractorTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList()) whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
val emptyFingerprintList: List<Fingerprint> = emptyList() val emptyFingerprintList: List<Fingerprint> = emptyList()
assertThat(underTest.enrolledFingerprints.last()).isEqualTo(emptyFingerprintList) assertThat(enrolledFingerprintsInteractorUnderTest.enrolledFingerprints.last())
.isEqualTo(emptyFingerprintList)
} }
@Test @Test
@@ -129,7 +175,7 @@ class FingerprintManagerInteractorTest {
val fingerprintList: List<Fingerprint> = listOf(expected) val fingerprintList: List<Fingerprint> = listOf(expected)
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList) whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
val list = underTest.enrolledFingerprints.last() val list = enrolledFingerprintsInteractorUnderTest.enrolledFingerprints.last()
assertThat(list!!.size).isEqualTo(fingerprintList.size) assertThat(list!!.size).isEqualTo(fingerprintList.size)
val actual = list[0] val actual = list[0]
assertThat(actual.name).isEqualTo(expected.name) assertThat(actual.name).isEqualTo(expected.name)
@@ -138,24 +184,51 @@ class FingerprintManagerInteractorTest {
} }
@Test @Test
fun testCanEnrollFingerprint() = fun testCanEnrollFingerprintSucceeds() =
testScope.runTest { testScope.runTest {
val fingerprintList1: List<Fingerprint> = val fingerprintList: List<Fingerprint> =
listOf( listOf(
Fingerprint("Finger 1,", 2, 3L), Fingerprint("Finger 1", 2, 3L),
Fingerprint("Finger 2,", 3, 3L), Fingerprint("Finger 2", 3, 3L),
Fingerprint("Finger 3,", 4, 3L), Fingerprint("Finger 3", 4, 3L),
) )
val fingerprintList2: List<Fingerprint> = whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
fingerprintList1.plus(
listOf(Fingerprint("Finger 4,", 5, 3L), Fingerprint("Finger 5,", 6, 3L))
)
whenever(fingerprintManager.getEnrolledFingerprints(anyInt()))
.thenReturn(fingerprintList1)
.thenReturn(fingerprintList2)
assertThat(underTest.canEnrollFingerprints.last()).isTrue() var result: Boolean? = null
assertThat(underTest.canEnrollFingerprints.last()).isFalse() val job =
testScope.launch {
canEnrollFingerprintsInteractorUnderTest.canEnrollFingerprints.collect { result = it }
}
runCurrent()
job.cancelAndJoin()
assertThat(result).isTrue()
}
@Test
fun testCanEnrollFingerprintFails() =
testScope.runTest {
val fingerprintList: List<Fingerprint> =
listOf(
Fingerprint("Finger 1", 2, 3L),
Fingerprint("Finger 2", 3, 3L),
Fingerprint("Finger 3", 4, 3L),
Fingerprint("Finger 4", 5, 3L),
Fingerprint("Finger 5", 6, 3L),
)
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
var result: Boolean? = null
val job =
testScope.launch {
canEnrollFingerprintsInteractorUnderTest.canEnrollFingerprints.collect { result = it }
}
runCurrent()
job.cancelAndJoin()
assertThat(result).isFalse()
} }
@Test @Test
@@ -178,7 +251,8 @@ class FingerprintManagerInteractorTest {
argumentCaptor() argumentCaptor()
var result: Pair<Long, ByteArray?>? = null var result: Pair<Long, ByteArray?>? = null
val job = testScope.launch { result = underTest.generateChallenge(1L) } val job =
testScope.launch { result = generateChallengeInteractorUnderTest.generateChallenge(1L) }
runCurrent() runCurrent()
verify(fingerprintManager).generateChallenge(anyInt(), capture(generateChallengeCallback)) verify(fingerprintManager).generateChallenge(anyInt(), capture(generateChallengeCallback))
@@ -201,7 +275,10 @@ class FingerprintManagerInteractorTest {
var result: Boolean? = null var result: Boolean? = null
val job = val job =
testScope.launch { result = underTest.removeFingerprint(fingerprintViewModelToRemove) } testScope.launch {
result =
removeFingerprintsInteractorUnderTest.removeFingerprint(fingerprintViewModelToRemove)
}
runCurrent() runCurrent()
verify(fingerprintManager) verify(fingerprintManager)
@@ -224,7 +301,10 @@ class FingerprintManagerInteractorTest {
var result: Boolean? = null var result: Boolean? = null
val job = val job =
testScope.launch { result = underTest.removeFingerprint(fingerprintViewModelToRemove) } testScope.launch {
result =
removeFingerprintsInteractorUnderTest.removeFingerprint(fingerprintViewModelToRemove)
}
runCurrent() runCurrent()
verify(fingerprintManager) verify(fingerprintManager)
@@ -246,7 +326,7 @@ class FingerprintManagerInteractorTest {
testScope.runTest { testScope.runTest {
val fingerprintToRename = FingerprintData("Finger 2", 1, 2L) val fingerprintToRename = FingerprintData("Finger 2", 1, 2L)
underTest.renameFingerprint(fingerprintToRename, "Woo") renameFingerprintsInteractorUnderTest.renameFingerprint(fingerprintToRename, "Woo")
verify(fingerprintManager).rename(eq(fingerprintToRename.fingerId), anyInt(), safeEq("Woo")) verify(fingerprintManager).rename(eq(fingerprintToRename.fingerId), anyInt(), safeEq("Woo"))
} }
@@ -257,7 +337,7 @@ class FingerprintManagerInteractorTest {
val fingerprint = Fingerprint("Woooo", 100, 101L) val fingerprint = Fingerprint("Woooo", 100, 101L)
var result: FingerprintAuthAttemptModel? = null var result: FingerprintAuthAttemptModel? = null
val job = launch { result = underTest.authenticate() } val job = launch { result = authenticateInteractorImplUnderTest.authenticate() }
val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> = argumentCaptor() val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> = argumentCaptor()
@@ -284,7 +364,7 @@ class FingerprintManagerInteractorTest {
fun testAuth_lockout() = fun testAuth_lockout() =
testScope.runTest { testScope.runTest {
var result: FingerprintAuthAttemptModel? = null var result: FingerprintAuthAttemptModel? = null
val job = launch { result = underTest.authenticate() } val job = launch { result = authenticateInteractorImplUnderTest.authenticate() }
val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> = argumentCaptor() val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> = argumentCaptor()
@@ -314,7 +394,7 @@ class FingerprintManagerInteractorTest {
val token = byteArrayOf(5, 3, 2) val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null var result: FingerEnrollState? = null
val job = launch { val job = launch {
underTest enrollInteractorUnderTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build()) .enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it } .collect { result = it }
} }
@@ -343,7 +423,7 @@ class FingerprintManagerInteractorTest {
val token = byteArrayOf(5, 3, 2) val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null var result: FingerEnrollState? = null
val job = launch { val job = launch {
underTest enrollInteractorUnderTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build()) .enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it } .collect { result = it }
} }
@@ -372,7 +452,7 @@ class FingerprintManagerInteractorTest {
val token = byteArrayOf(5, 3, 2) val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null var result: FingerEnrollState? = null
val job = launch { val job = launch {
underTest enrollInteractorUnderTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build()) .enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it } .collect { result = it }
} }

View File

@@ -99,9 +99,10 @@ class FingerprintEnrollFindSensorViewModelV2Test {
backgroundViewModel.inForeground() backgroundViewModel.inForeground()
enrollViewModel = enrollViewModel =
FingerprintEnrollViewModel( FingerprintEnrollViewModel(
fakeFingerprintManagerInteractor,
gatekeeperViewModel, gatekeeperViewModel,
navigationViewModel, navigationViewModel,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
) )
accessibilityInteractor = accessibilityInteractor =
object : AccessibilityInteractor { object : AccessibilityInteractor {

View File

@@ -49,8 +49,7 @@ class RFPSIconTouchViewModelTest {
fun setup() { fun setup() {
Dispatchers.setMain(backgroundDispatcher) Dispatchers.setMain(backgroundDispatcher)
testScope = TestScope(backgroundDispatcher) testScope = TestScope(backgroundDispatcher)
rfpsIconTouchViewModel = rfpsIconTouchViewModel = RFPSIconTouchViewModel()
RFPSIconTouchViewModel()
} }
@After @After

View File

@@ -88,9 +88,10 @@ class FingerprintEnrollEnrollingViewModelTest {
backgroundViewModel.inForeground() backgroundViewModel.inForeground()
val fingerprintEnrollViewModel = val fingerprintEnrollViewModel =
FingerprintEnrollViewModel( FingerprintEnrollViewModel(
fakeFingerprintManagerInteractor,
gateKeeperViewModel, gateKeeperViewModel,
navigationViewModel, navigationViewModel,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
) )
enrollEnrollingViewModel = enrollEnrollingViewModel =
FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel) FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel)

View File

@@ -67,10 +67,11 @@ class FingerprintSettingsNavigationViewModelTest {
underTest = underTest =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory( FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId, defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher, backgroundDispatcher,
null, null,
null, null,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
) )
.create(FingerprintSettingsNavigationViewModel::class.java) .create(FingerprintSettingsNavigationViewModel::class.java)
} }
@@ -272,10 +273,11 @@ class FingerprintSettingsNavigationViewModelTest {
underTest = underTest =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory( FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId, defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher, backgroundDispatcher,
token, token,
challenge, challenge,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
) )
.create(FingerprintSettingsNavigationViewModel::class.java) .create(FingerprintSettingsNavigationViewModel::class.java)
@@ -299,10 +301,11 @@ class FingerprintSettingsNavigationViewModelTest {
underTest = underTest =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory( FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId, defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher, backgroundDispatcher,
token, token,
challenge, challenge,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
) )
.create(FingerprintSettingsNavigationViewModel::class.java) .create(FingerprintSettingsNavigationViewModel::class.java)
@@ -331,10 +334,11 @@ class FingerprintSettingsNavigationViewModelTest {
underTest = underTest =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory( FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId, defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher, backgroundDispatcher,
token, token,
challenge, challenge,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
) )
.create(FingerprintSettingsNavigationViewModel::class.java) .create(FingerprintSettingsNavigationViewModel::class.java)

View File

@@ -73,19 +73,25 @@ class FingerprintSettingsViewModelTest {
navigationViewModel = navigationViewModel =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory( FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId, defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher, backgroundDispatcher,
null, null,
null, null,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
) )
.create(FingerprintSettingsNavigationViewModel::class.java) .create(FingerprintSettingsNavigationViewModel::class.java)
underTest = underTest =
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory( FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId, defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher, backgroundDispatcher,
navigationViewModel, navigationViewModel,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
) )
.create(FingerprintSettingsViewModel::class.java) .create(FingerprintSettingsViewModel::class.java)
} }
@@ -114,14 +120,7 @@ class FingerprintSettingsViewModelTest {
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(FingerprintData("a", 1, 3L)) mutableListOf(FingerprintData("a", 1, 3L))
underTest = recreateSettingsViewModel()
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher,
navigationViewModel,
)
.create(FingerprintSettingsViewModel::class.java)
var authAttempt: FingerprintAuthAttemptModel? = null var authAttempt: FingerprintAuthAttemptModel? = null
val job = launch { underTest.authFlow.take(5).collectLatest { authAttempt = it } } val job = launch { underTest.authFlow.take(5).collectLatest { authAttempt = it } }
@@ -156,14 +155,7 @@ class FingerprintSettingsViewModelTest {
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(FingerprintData("a", 1, 3L)) mutableListOf(FingerprintData("a", 1, 3L))
underTest = recreateSettingsViewModel()
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher,
navigationViewModel,
)
.create(FingerprintSettingsViewModel::class.java)
var authAttempt: FingerprintAuthAttemptModel? = null var authAttempt: FingerprintAuthAttemptModel? = null
val job = launch { underTest.authFlow.take(5).collectLatest { authAttempt = it } } val job = launch { underTest.authFlow.take(5).collectLatest { authAttempt = it } }
@@ -198,14 +190,7 @@ class FingerprintSettingsViewModelTest {
val success = FingerprintAuthAttemptModel.Success(1) val success = FingerprintAuthAttemptModel.Success(1)
fakeFingerprintManagerInteractor.authenticateAttempt = success fakeFingerprintManagerInteractor.authenticateAttempt = success
underTest = recreateSettingsViewModel()
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher,
navigationViewModel,
)
.create(FingerprintSettingsViewModel::class.java)
var authAttempt: FingerprintAuthAttemptModel? = null var authAttempt: FingerprintAuthAttemptModel? = null
@@ -225,14 +210,7 @@ class FingerprintSettingsViewModelTest {
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(fingerprintToDelete) mutableListOf(fingerprintToDelete)
underTest = recreateSettingsViewModel()
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher,
navigationViewModel,
)
.create(FingerprintSettingsViewModel::class.java)
var dialog: PreferenceViewModel? = null var dialog: PreferenceViewModel? = null
val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } } val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } }
@@ -261,14 +239,7 @@ class FingerprintSettingsViewModelTest {
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(fingerprintToRename) mutableListOf(fingerprintToRename)
underTest = recreateSettingsViewModel()
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher,
navigationViewModel,
)
.create(FingerprintSettingsViewModel::class.java)
var dialog: PreferenceViewModel? = null var dialog: PreferenceViewModel? = null
val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } } val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } }
@@ -299,14 +270,7 @@ class FingerprintSettingsViewModelTest {
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(fingerprintToDelete) mutableListOf(fingerprintToDelete)
underTest = recreateSettingsViewModel()
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher,
navigationViewModel,
)
.create(FingerprintSettingsViewModel::class.java)
var dialog: PreferenceViewModel? = null var dialog: PreferenceViewModel? = null
val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } } val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } }
@@ -390,6 +354,22 @@ class FingerprintSettingsViewModelTest {
assertThat(authAttempt).isEqualTo(null) assertThat(authAttempt).isEqualTo(null)
} }
private fun recreateSettingsViewModel() {
underTest =
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
backgroundDispatcher,
navigationViewModel,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
fakeFingerprintManagerInteractor,
)
.create(FingerprintSettingsViewModel::class.java)
}
private fun setupAuth(): MutableList<FingerprintData> { private fun setupAuth(): MutableList<FingerprintData> {
fakeFingerprintManagerInteractor.sensorProp = fakeFingerprintManagerInteractor.sensorProp =
FingerprintSensorPropertiesInternal( FingerprintSensorPropertiesInternal(
@@ -409,14 +389,7 @@ class FingerprintSettingsViewModelTest {
val success = FingerprintAuthAttemptModel.Success(1) val success = FingerprintAuthAttemptModel.Success(1)
fakeFingerprintManagerInteractor.authenticateAttempt = success fakeFingerprintManagerInteractor.authenticateAttempt = success
underTest = recreateSettingsViewModel()
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
fakeFingerprintManagerInteractor,
backgroundDispatcher,
navigationViewModel,
)
.create(FingerprintSettingsViewModel::class.java)
return fingerprints return fingerprints
} }