1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.biometrics.data.repository
18 
19 import android.Manifest.permission.USE_BIOMETRIC_INTERNAL
20 import android.annotation.RequiresPermission
21 import android.hardware.biometrics.ComponentInfoInternal
22 import android.hardware.biometrics.SensorLocationInternal
23 import android.hardware.biometrics.SensorProperties
24 import android.hardware.fingerprint.FingerprintManager
25 import android.hardware.fingerprint.FingerprintSensorProperties
26 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
27 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
28 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
29 import com.android.systemui.biometrics.shared.model.SensorStrength
30 import com.android.systemui.biometrics.shared.model.toSensorStrength
31 import com.android.systemui.biometrics.shared.model.toSensorType
32 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
33 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
34 import com.android.systemui.dagger.SysUISingleton
35 import com.android.systemui.dagger.qualifiers.Application
36 import javax.inject.Inject
37 import kotlinx.coroutines.CoroutineScope
38 import kotlinx.coroutines.channels.awaitClose
39 import kotlinx.coroutines.flow.Flow
40 import kotlinx.coroutines.flow.SharingStarted
41 import kotlinx.coroutines.flow.StateFlow
42 import kotlinx.coroutines.flow.map
43 import kotlinx.coroutines.flow.stateIn
44 
45 /**
46  * A repository for the global state of FingerprintProperty.
47  *
48  * There is never more than one instance of the FingerprintProperty at any given time.
49  */
50 interface FingerprintPropertyRepository {
51 
52     /** The id of fingerprint sensor. */
53     val sensorId: Flow<Int>
54 
55     /** The security strength of sensor (convenience, weak, strong). */
56     val strength: Flow<SensorStrength>
57 
58     /** The types of fingerprint sensor (rear, ultrasonic, optical, etc.). */
59     val sensorType: Flow<FingerprintSensorType>
60 
61     /** The sensor location relative to each physical display. */
62     val sensorLocations: Flow<Map<String, SensorLocationInternal>>
63 }
64 
65 @SysUISingleton
66 class FingerprintPropertyRepositoryImpl
67 @Inject
68 constructor(
69     @Application private val applicationScope: CoroutineScope,
70     private val fingerprintManager: FingerprintManager?,
71 ) : FingerprintPropertyRepository {
72 
73     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
74     private val props: StateFlow<FingerprintSensorPropertiesInternal> =
75         conflatedCallbackFlow {
76                 val callback =
77                     object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
78                         override fun onAllAuthenticatorsRegistered(
79                             sensors: List<FingerprintSensorPropertiesInternal>
80                         ) {
81                             if (sensors.isEmpty()) {
82                                 trySendWithFailureLogging(
83                                     DEFAULT_PROPS,
84                                     TAG,
85                                     "no registered sensors, use default props"
86                                 )
87                             } else {
88                                 trySendWithFailureLogging(
89                                     sensors[0],
90                                     TAG,
91                                     "update properties on authenticators registered"
92                                 )
93                             }
94                         }
95                     }
96                 fingerprintManager?.addAuthenticatorsRegisteredCallback(callback)
97                 awaitClose {}
98             }
99             .stateIn(
100                 applicationScope,
101                 started = SharingStarted.Eagerly,
102                 initialValue = DEFAULT_PROPS,
103             )
104 
105     override val sensorId: Flow<Int> = props.map { it.sensorId }
106 
107     override val strength: Flow<SensorStrength> = props.map { it.sensorStrength.toSensorStrength() }
108 
109     override val sensorType: Flow<FingerprintSensorType> =
110         props.map { it.sensorType.toSensorType() }
111 
112     override val sensorLocations: Flow<Map<String, SensorLocationInternal>> =
113         props.map {
114             it.allLocations.associateBy { sensorLocationInternal ->
115                 sensorLocationInternal.displayId
116             }
117         }
118 
119     companion object {
120         private const val TAG = "FingerprintPropertyRepositoryImpl"
121         private val DEFAULT_PROPS =
122             FingerprintSensorPropertiesInternal(
123                 -1 /* sensorId */,
124                 SensorProperties.STRENGTH_CONVENIENCE,
125                 0 /* maxEnrollmentsPerUser */,
126                 listOf<ComponentInfoInternal>(),
127                 FingerprintSensorProperties.TYPE_UNKNOWN,
128                 false /* halControlsIllumination */,
129                 true /* resetLockoutRequiresHardwareAuthToken */,
130                 listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT)
131             )
132     }
133 }
134