1 /*
2  * Copyright (C) 2020 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 static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
20 import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
21 import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
22 
23 import android.annotation.CallSuper;
24 import android.content.res.ColorStateList;
25 import android.os.AsyncTask;
26 import android.os.CountDownTimer;
27 import android.os.SystemClock;
28 import android.view.KeyEvent;
29 
30 import com.android.internal.util.LatencyTracker;
31 import com.android.internal.widget.LockPatternChecker;
32 import com.android.internal.widget.LockPatternUtils;
33 import com.android.internal.widget.LockscreenCredential;
34 import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
35 import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
36 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
37 import com.android.systemui.R;
38 import com.android.systemui.classifier.FalsingClassifier;
39 import com.android.systemui.classifier.FalsingCollector;
40 
41 public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
42         extends KeyguardInputViewController<T> {
43     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
44     private final LockPatternUtils mLockPatternUtils;
45     private final LatencyTracker mLatencyTracker;
46     private final FalsingCollector mFalsingCollector;
47     private final EmergencyButtonController mEmergencyButtonController;
48     private CountDownTimer mCountdownTimer;
49     protected KeyguardMessageAreaController mMessageAreaController;
50     private boolean mDismissing;
51     protected AsyncTask<?, ?, ?> mPendingLockCheck;
52     protected boolean mResumed;
53 
54     private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
55         // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
56         // We don't want to consider it valid user input because the UI
57         // will already respond to the event.
58         if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
59             onUserInput();
60         }
61         return false;
62     };
63 
64     private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
65         @Override
66         public void onEmergencyButtonClickedWhenInCall() {
67             getKeyguardSecurityCallback().reset();
68         }
69     };
70 
KeyguardAbsKeyInputViewController(T view, KeyguardUpdateMonitor keyguardUpdateMonitor, SecurityMode securityMode, LockPatternUtils lockPatternUtils, KeyguardSecurityCallback keyguardSecurityCallback, KeyguardMessageAreaController.Factory messageAreaControllerFactory, LatencyTracker latencyTracker, FalsingCollector falsingCollector, EmergencyButtonController emergencyButtonController)71     protected KeyguardAbsKeyInputViewController(T view,
72             KeyguardUpdateMonitor keyguardUpdateMonitor,
73             SecurityMode securityMode,
74             LockPatternUtils lockPatternUtils,
75             KeyguardSecurityCallback keyguardSecurityCallback,
76             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
77             LatencyTracker latencyTracker, FalsingCollector falsingCollector,
78             EmergencyButtonController emergencyButtonController) {
79         super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
80         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
81         mLockPatternUtils = lockPatternUtils;
82         mLatencyTracker = latencyTracker;
83         mFalsingCollector = falsingCollector;
84         mEmergencyButtonController = emergencyButtonController;
85         KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
86         mMessageAreaController = messageAreaControllerFactory.create(kma);
87     }
88 
resetState()89     abstract void resetState();
90 
91     @Override
onInit()92     public void onInit() {
93         super.onInit();
94         mMessageAreaController.init();
95     }
96 
97     @Override
onViewAttached()98     protected void onViewAttached() {
99         super.onViewAttached();
100         mView.setKeyDownListener(mKeyDownListener);
101         mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
102         mEmergencyButtonController.setEmergencyButtonCallback(mEmergencyButtonCallback);
103     }
104 
105     @Override
reset()106     public void reset() {
107         // start fresh
108         mDismissing = false;
109         mView.resetPasswordText(false /* animate */, false /* announce */);
110         // if the user is currently locked out, enforce it.
111         long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
112                 KeyguardUpdateMonitor.getCurrentUser());
113         if (shouldLockout(deadline)) {
114             handleAttemptLockout(deadline);
115         } else {
116             resetState();
117         }
118     }
119 
120     @CallSuper
121     @Override
reloadColors()122     public void reloadColors() {
123         super.reloadColors();
124         mMessageAreaController.reloadColors();
125     }
126 
127     @Override
needsInput()128     public boolean needsInput() {
129         return false;
130     }
131 
132     @Override
showMessage(CharSequence message, ColorStateList colorState)133     public void showMessage(CharSequence message, ColorStateList colorState) {
134         if (colorState != null) {
135             mMessageAreaController.setNextMessageColor(colorState);
136         }
137         mMessageAreaController.setMessage(message);
138     }
139 
140     // Allow subclasses to override this behavior
shouldLockout(long deadline)141     protected boolean shouldLockout(long deadline) {
142         return deadline != 0;
143     }
144 
145     // Prevent user from using the PIN/Password entry until scheduled deadline.
handleAttemptLockout(long elapsedRealtimeDeadline)146     protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
147         mView.setPasswordEntryEnabled(false);
148         long elapsedRealtime = SystemClock.elapsedRealtime();
149         long secondsInFuture = (long) Math.ceil(
150                 (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
151         mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
152 
153             @Override
154             public void onTick(long millisUntilFinished) {
155                 int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
156                 mMessageAreaController.setMessage(mView.getResources().getQuantityString(
157                         R.plurals.kg_too_many_failed_attempts_countdown,
158                         secondsRemaining, secondsRemaining));
159             }
160 
161             @Override
162             public void onFinish() {
163                 mMessageAreaController.setMessage("");
164                 resetState();
165             }
166         }.start();
167     }
168 
onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword)169     void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
170         boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
171         if (matched) {
172             getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
173             if (dismissKeyguard) {
174                 mDismissing = true;
175                 mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
176                 getKeyguardSecurityCallback().dismiss(true, userId);
177             }
178         } else {
179             if (isValidPassword) {
180                 getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
181                 if (timeoutMs > 0) {
182                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
183                             userId, timeoutMs);
184                     handleAttemptLockout(deadline);
185                 }
186             }
187             if (timeoutMs == 0) {
188                 mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
189             }
190             mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
191         }
192     }
193 
verifyPasswordAndUnlock()194     protected void verifyPasswordAndUnlock() {
195         if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
196 
197         final LockscreenCredential password = mView.getEnteredCredential();
198         mView.setPasswordEntryInputEnabled(false);
199         if (mPendingLockCheck != null) {
200             mPendingLockCheck.cancel(false);
201         }
202 
203         final int userId = KeyguardUpdateMonitor.getCurrentUser();
204         if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
205             // to avoid accidental lockout, only count attempts that are long enough to be a
206             // real password. This may require some tweaking.
207             mView.setPasswordEntryInputEnabled(true);
208             onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
209             password.zeroize();
210             return;
211         }
212 
213         mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
214         mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
215 
216         mKeyguardUpdateMonitor.setCredentialAttempted();
217         mPendingLockCheck = LockPatternChecker.checkCredential(
218                 mLockPatternUtils,
219                 password,
220                 userId,
221                 new LockPatternChecker.OnCheckCallback() {
222 
223                     @Override
224                     public void onEarlyMatched() {
225                         mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
226 
227                         onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
228                                 true /* isValidPassword */);
229                         password.zeroize();
230                     }
231 
232                     @Override
233                     public void onChecked(boolean matched, int timeoutMs) {
234                         mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
235                         mView.setPasswordEntryInputEnabled(true);
236                         mPendingLockCheck = null;
237                         if (!matched) {
238                             onPasswordChecked(userId, false /* matched */, timeoutMs,
239                                     true /* isValidPassword */);
240                         }
241                         password.zeroize();
242                     }
243 
244                     @Override
245                     public void onCancelled() {
246                         // We already got dismissed with the early matched callback, so we cancelled
247                         // the check. However, we still need to note down the latency.
248                         mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
249                         password.zeroize();
250                     }
251                 });
252     }
253 
254     @Override
showPromptReason(int reason)255     public void showPromptReason(int reason) {
256         if (reason != PROMPT_REASON_NONE) {
257             int promtReasonStringRes = mView.getPromptReasonStringRes(reason);
258             if (promtReasonStringRes != 0) {
259                 mMessageAreaController.setMessage(promtReasonStringRes);
260             }
261         }
262     }
263 
onUserInput()264     protected void onUserInput() {
265         mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.passed(0.6));
266         getKeyguardSecurityCallback().userActivity();
267         getKeyguardSecurityCallback().onUserInput();
268         mMessageAreaController.setMessage("");
269     }
270 
271     @Override
onResume(int reason)272     public void onResume(int reason) {
273         mResumed = true;
274     }
275 
276     @Override
onPause()277     public void onPause() {
278         mResumed = false;
279 
280         if (mCountdownTimer != null) {
281             mCountdownTimer.cancel();
282             mCountdownTimer = null;
283         }
284         if (mPendingLockCheck != null) {
285             mPendingLockCheck.cancel(false);
286             mPendingLockCheck = null;
287         }
288         reset();
289     }
290 }
291