Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb23b975 authored by Joshua Mccloskey's avatar Joshua Mccloskey Committed by Android (Google) Code Review
Browse files

Merge "Split up FingerprintManagerInteractor" into main

parents 044d97dd 882e1c36
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package com.android.settings;

import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri;
import android.provider.Settings;
import android.util.FeatureFlagUtils;
@@ -74,9 +76,6 @@ public class SettingsApplication extends Application {

        // Set Spa environment.
        setSpaEnvironment();
        if (Flags.fingerprintV2Enrollment()) {
            mBiometricsEnvironment = new BiometricsEnvironment(this);
        }

        if (ActivityEmbeddingUtils.isSettingsSplitEnabled(this)
                && FeatureFlagUtils.isEnabled(this,
@@ -120,7 +119,20 @@ public class SettingsApplication extends Application {

    @Nullable
    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;

            } else {
                return null;
            }

        }
        return null;
    }

    @Override
+54 −33
Original line number Diff line number Diff line
@@ -16,12 +16,9 @@

package com.android.settings.biometrics.fingerprint2

import android.content.pm.PackageManager
import android.hardware.fingerprint.FingerprintManager
import android.os.ServiceManager.ServiceNotFoundException
import android.view.MotionEvent
import android.view.accessibility.AccessibilityManager
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
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.fingerprint2.data.repository.DebuggingRepository
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.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.domain.interactor.DebugTouchEventInteractorImpl
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.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.DebuggingInteractorImpl
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.EnrollFingerprintInteractorImpl
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.FingerprintEnrollInteractor
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.EnrolledFingerprintsInteractorImpl
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.FoldStateInteractor
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.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.UdfpsEnrollInteractor
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.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 java.util.concurrent.Executors
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 &
 * 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 backgroundDispatcher = executorService.asCoroutineDispatcher()
  private val applicationScope = MainScope()
  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 =
    FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, applicationScope)
  private val debuggingRepository: DebuggingRepository = DebuggingRepositoryImpl()
  private val udfpsDebugRepo = UdfpsEnrollDebugRepositoryImpl()

  /** For now, interactors are public to those with access to the [BiometricsEnvironment] class */
  val fingerprintEnrollInteractor: FingerprintEnrollInteractor by lazy {
    FingerprintEnrollInteractorImpl(context, fingerprintManager, Settings)
  }
  fun createSensorPropertiesInteractor(): SensorInteractor =
    SensorInteractorImpl(fingerprintSensorRepository)

  /** [FingerprintManagerInteractor] to be used to construct view models */
  val fingerprintManagerInteractor: FingerprintManagerInteractor by lazy {
    FingerprintManagerInteractorImpl(
      context,
      backgroundDispatcher,
      fingerprintManager,
      fingerprintSensorRepository,
      gateKeeperPasswordProvider,
      fingerprintEnrollInteractor,
    )
  }
  fun createCanEnrollFingerprintsInteractor(): CanEnrollFingerprintsInteractor =
    CanEnrollFingerprintsInteractorImpl(fingerprintEnrollmentRepository)

  fun createGenerateChallengeInteractor(): GenerateChallengeInteractor =
    GenerateChallengeInteractorImpl(fingerprintManager, context.userId, gateKeeperPasswordProvider)

  fun createFingerprintEnrollInteractor(): EnrollFingerprintInteractor =
    EnrollFingerprintInteractorImpl(context.userId, fingerprintManager, Settings)

  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 {
    AccessibilityInteractorImpl(
+98 −0
Original line number Diff line number Diff line
/*
 * 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()
  }
}
+10 −2
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.withContext
@@ -43,10 +45,13 @@ import kotlinx.coroutines.withContext
interface FingerprintSensorRepository {
  /** Get the [FingerprintSensor] */
  val fingerprintSensor: Flow<FingerprintSensor>

  /** Indicates if this device supports the side fingerprint sensor */
  val hasSideFps: Flow<Boolean>
}

class FingerprintSensorRepositoryImpl(
  fingerprintManager: FingerprintManager?,
  private val fingerprintManager: FingerprintManager,
  backgroundDispatcher: CoroutineDispatcher,
  activityScope: CoroutineScope,
) : FingerprintSensorRepository {
@@ -66,7 +71,7 @@ class FingerprintSensorRepositoryImpl(
            }
          }
        withContext(backgroundDispatcher) {
          fingerprintManager?.addAuthenticatorsRegisteredCallback(callback)
          fingerprintManager.addAuthenticatorsRegisteredCallback(callback)
        }
        awaitClose {}
      }
@@ -75,6 +80,9 @@ class FingerprintSensorRepositoryImpl(
  override val fingerprintSensor: Flow<FingerprintSensor> =
    fingerprintPropsInternal.transform { emit(it.toFingerprintSensor()) }

  override val hasSideFps: Flow<Boolean> =
    fingerprintSensor.flatMapLatest { flow { emit(fingerprintManager.isPowerbuttonFps()) } }

  companion object {

    private val DEFAULT_PROPS =
+32 −0
Original line number Diff line number Diff line
/*
 * 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
}
Loading