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