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