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