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.app.StatusBarManager.SESSION_KEYGUARD
20 import android.hardware.biometrics.BiometricSourceType
21 import com.android.internal.annotations.VisibleForTesting
22 import com.android.internal.logging.UiEvent
23 import com.android.internal.logging.UiEventLogger
24 import com.android.internal.widget.LockPatternUtils
25 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
26 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
27 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
28 import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
29 import com.android.systemui.CoreStartable
30 import com.android.systemui.dagger.SysUISingleton
31 import com.android.systemui.log.SessionTracker
32 import java.io.PrintWriter
33 import javax.inject.Inject
34 
35 /**
36  * Logs events when primary authentication requirements change. Primary authentication is considered
37  * authentication using pin/pattern/password input.
38  *
39  * See [PrimaryAuthRequiredEvent] for all the events and their descriptions.
40  */
41 @SysUISingleton
42 class KeyguardBiometricLockoutLogger @Inject constructor(
43     private val uiEventLogger: UiEventLogger,
44     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
45     private val sessionTracker: SessionTracker
46 ) : CoreStartable {
47     private var fingerprintLockedOut = false
48     private var faceLockedOut = false
49     private var encryptedOrLockdown = false
50     private var unattendedUpdate = false
51     private var timeout = false
52 
53     override fun start() {
54         mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged(
55                 KeyguardUpdateMonitor.getCurrentUser())
56         keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback)
57     }
58 
59     private val mKeyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback =
60             object : KeyguardUpdateMonitorCallback() {
61         override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType) {
62             if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
63                 val lockedOut = keyguardUpdateMonitor.isFingerprintLockedOut
64                 if (lockedOut && !fingerprintLockedOut) {
65                     log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
66                 } else if (!lockedOut && fingerprintLockedOut) {
67                     log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
68                 }
69                 fingerprintLockedOut = lockedOut
70             } else if (biometricSourceType == BiometricSourceType.FACE) {
71                 val lockedOut = keyguardUpdateMonitor.isFaceLockedOut
72                 if (lockedOut && !faceLockedOut) {
73                     log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
74                 } else if (!lockedOut && faceLockedOut) {
75                     log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
76                 }
77                 faceLockedOut = lockedOut
78             }
79         }
80 
81         override fun onStrongAuthStateChanged(userId: Int) {
82             if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
83                 return
84             }
85             val strongAuthFlags = keyguardUpdateMonitor.strongAuthTracker
86                     .getStrongAuthForUser(userId)
87 
88             val newEncryptedOrLockdown = keyguardUpdateMonitor.isEncryptedOrLockdown(userId)
89             if (newEncryptedOrLockdown && !encryptedOrLockdown) {
90                 log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
91             }
92             encryptedOrLockdown = newEncryptedOrLockdown
93 
94             val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags)
95             if (newUnattendedUpdate && !unattendedUpdate) {
96                 log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
97             }
98             unattendedUpdate = newUnattendedUpdate
99 
100             val newTimeout = isStrongAuthTimeout(strongAuthFlags)
101             if (newTimeout && !timeout) {
102                 log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT)
103             }
104             timeout = newTimeout
105         }
106     }
107 
108     private fun isUnattendedUpdate(
109         @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int
110     ) = containsFlag(flags, STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
111 
112     private fun isStrongAuthTimeout(
113         @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int
114     ) = containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) ||
115             containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT)
116 
117     private fun log(event: PrimaryAuthRequiredEvent) =
118             uiEventLogger.log(event, sessionTracker.getSessionId(SESSION_KEYGUARD))
119 
120     override fun dump(pw: PrintWriter, args: Array<String>) {
121         pw.println("  mFingerprintLockedOut=$fingerprintLockedOut")
122         pw.println("  mFaceLockedOut=$faceLockedOut")
123         pw.println("  mIsEncryptedOrLockdown=$encryptedOrLockdown")
124         pw.println("  mIsUnattendedUpdate=$unattendedUpdate")
125         pw.println("  mIsTimeout=$timeout")
126     }
127 
128     /**
129      * Events pertaining to whether primary authentication (pin/pattern/password input) is required
130      * for device entry.
131      */
132     @VisibleForTesting
133     enum class PrimaryAuthRequiredEvent(private val mId: Int) : UiEventLogger.UiEventEnum {
134         @UiEvent(doc = "Fingerprint cannot be used to authenticate for device entry. This" +
135                 "can persist until the next primary auth or may timeout.")
136         PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT(924),
137 
138         @UiEvent(doc = "Fingerprint can be used to authenticate for device entry.")
139         PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET(925),
140 
141         @UiEvent(doc = "Face cannot be used to authenticate for device entry.")
142         PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT(926),
143 
144         @UiEvent(doc = "Face can be used to authenticate for device entry.")
145         PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET(927),
146 
147         @UiEvent(doc = "Device is encrypted (ie: after reboot) or device is locked down by DPM " +
148                 "or a manual user lockdown.")
149         PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN(928),
150 
151         @UiEvent(doc = "Primary authentication is required because it hasn't been used for a " +
152                 "time required by a device admin or because primary auth hasn't been used for a " +
153                 "time after a non-strong biometric (weak or convenience) is used to unlock the " +
154                 "device.")
155         PRIMARY_AUTH_REQUIRED_TIMEOUT(929),
156 
157         @UiEvent(doc = "Strong authentication is required to prepare for unattended upgrade.")
158         PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE(931);
159 
160         override fun getId(): Int {
161             return mId
162         }
163     }
164 
165     companion object {
166         private fun containsFlag(strongAuthFlags: Int, flagCheck: Int): Boolean {
167             return strongAuthFlags and flagCheck != 0
168         }
169     }
170 }
171