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