1 /*
2  * Copyright (C) 2022 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.keyguard
18 
19 import android.content.ContentResolver
20 import android.database.ContentObserver
21 import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT
22 import android.net.Uri
23 import android.os.Handler
24 import android.os.PowerManager
25 import android.os.PowerManager.WAKE_REASON_UNFOLD_DEVICE
26 import android.os.UserHandle
27 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
28 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
29 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
30 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
31 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
32 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
33 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
34 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
35 import android.util.Log
36 import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser
37 import com.android.systemui.Dumpable
38 import com.android.systemui.dagger.SysUISingleton
39 import com.android.systemui.dagger.qualifiers.Main
40 import com.android.systemui.dump.DumpManager
41 import com.android.systemui.util.settings.SecureSettings
42 import java.io.PrintWriter
43 import javax.inject.Inject
44 
45 /**
46  * Handles active unlock settings changes.
47  */
48 @SysUISingleton
49 class ActiveUnlockConfig @Inject constructor(
50     @Main private val handler: Handler,
51     private val secureSettings: SecureSettings,
52     private val contentResolver: ContentResolver,
53     dumpManager: DumpManager
54 ) : Dumpable {
55 
56     companion object {
57         const val TAG = "ActiveUnlockConfig"
58     }
59 
60     /**
61      * Indicates the origin for an active unlock request.
62      */
63     enum class ActiveUnlockRequestOrigin {
64         /**
65          * Trigger ActiveUnlock on wake ups that'd trigger FaceAuth, see [FaceWakeUpTriggersConfig]
66          */
67         WAKE,
68 
69         /**
70          * Trigger ActiveUnlock on unlock intents. This includes the bouncer showing or tapping on
71          * a notification. May also include wakeups: [wakeupsConsideredUnlockIntents].
72          */
73         UNLOCK_INTENT,
74 
75         /**
76          * Trigger ActiveUnlock on biometric failures. This may include soft errors depending on
77          * the other settings. See: [faceErrorsToTriggerBiometricFailOn],
78          * [faceAcquireInfoToTriggerBiometricFailOn].
79          */
80         BIOMETRIC_FAIL,
81 
82         /**
83          * Trigger ActiveUnlock when the assistant is triggered.
84          */
85         ASSISTANT,
86     }
87 
88     /**
89      * Biometric type options.
90      */
91     enum class BiometricType(val intValue: Int) {
92         NONE(0),
93         ANY_FACE(1),
94         ANY_FINGERPRINT(2),
95         UNDER_DISPLAY_FINGERPRINT(3),
96     }
97 
98     var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null
99     private var requestActiveUnlockOnWakeup = false
100     private var requestActiveUnlockOnUnlockIntent = false
101     private var requestActiveUnlockOnBioFail = false
102 
103     private var faceErrorsToTriggerBiometricFailOn = mutableSetOf<Int>()
104     private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>()
105     private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>()
106     private var wakeupsConsideredUnlockIntents = mutableSetOf<Int>()
107     private var wakeupsToForceDismissKeyguard = mutableSetOf<Int>()
108 
109     private val settingsObserver = object : ContentObserver(handler) {
110         private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
111         private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
112         private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
113         private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)
114         private val faceAcquireInfoUri =
115                 secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)
116         private val unlockIntentWhenBiometricEnrolledUri =
117                 secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
118         private val wakeupsConsideredUnlockIntentsUri =
119             secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS)
120         private val wakeupsToForceDismissKeyguardUri =
121             secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD)
122 
123         fun register() {
124             registerUri(
125                     listOf(
126                         wakeUri,
127                         unlockIntentUri,
128                         bioFailUri,
129                         faceErrorsUri,
130                         faceAcquireInfoUri,
131                         unlockIntentWhenBiometricEnrolledUri,
132                         wakeupsConsideredUnlockIntentsUri,
133                         wakeupsToForceDismissKeyguardUri,
134                     )
135             )
136 
137             onChange(true, ArrayList(), 0, getCurrentUser())
138         }
139 
140         private fun registerUri(uris: Collection<Uri>) {
141             for (uri in uris) {
142                 contentResolver.registerContentObserver(
143                         uri,
144                         false,
145                         this,
146                         UserHandle.USER_ALL)
147             }
148         }
149 
150         override fun onChange(
151             selfChange: Boolean,
152             uris: Collection<Uri>,
153             flags: Int,
154             userId: Int
155         ) {
156             if (getCurrentUser() != userId) {
157                 return
158             }
159 
160             if (selfChange || uris.contains(wakeUri)) {
161                 requestActiveUnlockOnWakeup = secureSettings.getIntForUser(
162                         ACTIVE_UNLOCK_ON_WAKE, 0, getCurrentUser()) == 1
163             }
164 
165             if (selfChange || uris.contains(unlockIntentUri)) {
166                 requestActiveUnlockOnUnlockIntent = secureSettings.getIntForUser(
167                         ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0, getCurrentUser()) == 1
168             }
169 
170             if (selfChange || uris.contains(bioFailUri)) {
171                 requestActiveUnlockOnBioFail = secureSettings.getIntForUser(
172                         ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1
173             }
174 
175             if (selfChange || uris.contains(faceErrorsUri)) {
176                 processStringArray(
177                         secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS,
178                                 getCurrentUser()),
179                         faceErrorsToTriggerBiometricFailOn,
180                         setOf(FACE_ERROR_TIMEOUT))
181             }
182 
183             if (selfChange || uris.contains(faceAcquireInfoUri)) {
184                 processStringArray(
185                         secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
186                                 getCurrentUser()),
187                         faceAcquireInfoToTriggerBiometricFailOn,
188                         emptySet())
189             }
190 
191             if (selfChange || uris.contains(unlockIntentWhenBiometricEnrolledUri)) {
192                 processStringArray(
193                         secureSettings.getStringForUser(
194                                 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
195                                 getCurrentUser()),
196                         onUnlockIntentWhenBiometricEnrolled,
197                         setOf(BiometricType.NONE.intValue))
198             }
199 
200             if (selfChange || uris.contains(wakeupsConsideredUnlockIntentsUri)) {
201                 processStringArray(
202                     secureSettings.getStringForUser(
203                         ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
204                         getCurrentUser()),
205                     wakeupsConsideredUnlockIntents,
206                     setOf(WAKE_REASON_UNFOLD_DEVICE))
207             }
208 
209             if (selfChange || uris.contains(wakeupsToForceDismissKeyguardUri)) {
210                 processStringArray(
211                     secureSettings.getStringForUser(
212                         ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
213                         getCurrentUser()),
214                     wakeupsToForceDismissKeyguard,
215                     setOf(WAKE_REASON_UNFOLD_DEVICE))
216             }
217         }
218 
219         /**
220          * Convert a pipe-separated set of integers into a set of ints.
221          * @param stringSetting expected input are integers delineated by a pipe. For example,
222          * it may look something like this: "1|5|3".
223          * @param out updates the "out" Set will the integers between the pipes.
224          * @param default If stringSetting is null, "out" will be populated with values in "default"
225          */
226         private fun processStringArray(
227             stringSetting: String?,
228             out: MutableSet<Int>,
229             default: Set<Int>
230         ) {
231             out.clear()
232             stringSetting?.let {
233                 for (code: String in stringSetting.split("|")) {
234                     if (code.isNotEmpty()) {
235                         try {
236                             out.add(code.toInt())
237                         } catch (e: NumberFormatException) {
238                             Log.e(TAG, "Passed an invalid setting=$code")
239                         }
240                     }
241                 }
242             } ?: out.addAll(default)
243         }
244     }
245 
246     init {
247         settingsObserver.register()
248         dumpManager.registerDumpable(this)
249     }
250 
251     /**
252      * If any active unlock triggers are enabled.
253      */
254     fun isActiveUnlockEnabled(): Boolean {
255         return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
256                 requestActiveUnlockOnBioFail
257     }
258 
259     /**
260      * Whether the face error code from {@link BiometricFaceConstants} should trigger
261      * active unlock on biometric failure.
262      */
263     fun shouldRequestActiveUnlockOnFaceError(errorCode: Int): Boolean {
264         return faceErrorsToTriggerBiometricFailOn.contains(errorCode)
265     }
266 
267     /**
268      * Whether the face acquireInfo from {@link BiometricFaceConstants} should trigger
269      * active unlock on biometric failure.
270      */
271     fun shouldRequestActiveUnlockOnFaceAcquireInfo(acquiredInfo: Int): Boolean {
272         return faceAcquireInfoToTriggerBiometricFailOn.contains(acquiredInfo)
273     }
274 
275     /**
276      * Whether the PowerManager wake reason is considered an unlock intent and should use origin
277      * [ActiveUnlockRequestOrigin.UNLOCK_INTENT] instead of [ActiveUnlockRequestOrigin.WAKE].
278      */
279     fun isWakeupConsideredUnlockIntent(pmWakeReason: Int): Boolean {
280         return wakeupsConsideredUnlockIntents.contains(pmWakeReason)
281     }
282 
283     /**
284      * Whether the PowerManager wake reason should force dismiss the keyguard if active
285      * unlock is successful.
286      */
287     fun shouldWakeupForceDismissKeyguard(pmWakeReason: Int): Boolean {
288         return wakeupsToForceDismissKeyguard.contains(pmWakeReason)
289     }
290 
291     /**
292      * Whether to trigger active unlock based on where the request is coming from and
293      * the current settings.
294      */
295     fun shouldAllowActiveUnlockFromOrigin(requestOrigin: ActiveUnlockRequestOrigin): Boolean {
296         return when (requestOrigin) {
297             ActiveUnlockRequestOrigin.WAKE -> requestActiveUnlockOnWakeup
298 
299             ActiveUnlockRequestOrigin.UNLOCK_INTENT ->
300                 requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup ||
301                         (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
302 
303             ActiveUnlockRequestOrigin.BIOMETRIC_FAIL ->
304                 requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent ||
305                         requestActiveUnlockOnWakeup
306 
307             ActiveUnlockRequestOrigin.ASSISTANT -> isActiveUnlockEnabled()
308         }
309     }
310 
311     private fun shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment(): Boolean {
312         if (!requestActiveUnlockOnBioFail) {
313             return false
314         }
315 
316         keyguardUpdateMonitor?.let {
317             val anyFaceEnrolled = it.isFaceEnrolled
318             val anyFingerprintEnrolled =
319                     it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())
320             val udfpsEnrolled = it.isUdfpsEnrolled
321 
322             if (!anyFaceEnrolled && !anyFingerprintEnrolled) {
323                 return onUnlockIntentWhenBiometricEnrolled.contains(BiometricType.NONE.intValue)
324             }
325 
326             if (!anyFaceEnrolled && anyFingerprintEnrolled) {
327                 return onUnlockIntentWhenBiometricEnrolled.contains(
328                         BiometricType.ANY_FINGERPRINT.intValue) ||
329                         (udfpsEnrolled && onUnlockIntentWhenBiometricEnrolled.contains(
330                                 BiometricType.UNDER_DISPLAY_FINGERPRINT.intValue))
331             }
332 
333             if (!anyFingerprintEnrolled && anyFaceEnrolled) {
334                 return onUnlockIntentWhenBiometricEnrolled.contains(BiometricType.ANY_FACE.intValue)
335             }
336         }
337 
338         return false
339     }
340 
341     override fun dump(pw: PrintWriter, args: Array<out String>) {
342         pw.println("Settings:")
343         pw.println("   requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
344         pw.println("   requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
345         pw.println("   requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
346 
347         val onUnlockIntentWhenBiometricEnrolledString =
348             onUnlockIntentWhenBiometricEnrolled.map {
349                 for (biometricType in BiometricType.values()) {
350                     if (biometricType.intValue == it) {
351                         return@map biometricType.name
352                     }
353                 }
354                 return@map "UNKNOWN"
355             }
356         pw.println("   requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" +
357                 "$onUnlockIntentWhenBiometricEnrolledString")
358         pw.println("   requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn")
359         pw.println("   requestActiveUnlockOnFaceAcquireInfo=" +
360                 "$faceAcquireInfoToTriggerBiometricFailOn")
361         pw.println("   activeUnlockWakeupsConsideredUnlockIntents=${
362             wakeupsConsideredUnlockIntents.map { PowerManager.wakeReasonToString(it) }
363         }")
364         pw.println("   activeUnlockFromWakeupsToAlwaysDismissKeyguard=${
365             wakeupsToForceDismissKeyguard.map { PowerManager.wakeReasonToString(it) }
366         }")
367 
368         pw.println("Current state:")
369         keyguardUpdateMonitor?.let {
370             pw.println("   shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
371                     "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
372             pw.println("   faceEnrolled=${it.isFaceEnrolled}")
373             pw.println("   fpEnrolled=${
374                     it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}")
375             pw.println("   udfpsEnrolled=${it.isUdfpsEnrolled}")
376         } ?: pw.println("   keyguardUpdateMonitor is uninitialized")
377     }
378 }