1 /* 2 * Copyright (C) 2012 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 22 import android.content.Context; 23 import android.os.AsyncTask; 24 import android.os.CountDownTimer; 25 import android.os.SystemClock; 26 import android.util.AttributeSet; 27 import android.view.HapticFeedbackConstants; 28 import android.view.KeyEvent; 29 import android.view.View; 30 import android.widget.LinearLayout; 31 32 import com.android.internal.util.LatencyTracker; 33 import com.android.internal.widget.LockPatternChecker; 34 import com.android.internal.widget.LockPatternUtils; 35 36 /** 37 * Base class for PIN and password unlock screens. 38 */ 39 public abstract class KeyguardAbsKeyInputView extends LinearLayout 40 implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback { 41 protected KeyguardSecurityCallback mCallback; 42 protected LockPatternUtils mLockPatternUtils; 43 protected AsyncTask<?, ?, ?> mPendingLockCheck; 44 protected SecurityMessageDisplay mSecurityMessageDisplay; 45 protected View mEcaView; 46 protected boolean mEnableHaptics; 47 private boolean mDismissing; 48 private CountDownTimer mCountdownTimer = null; 49 50 // To avoid accidental lockout due to events while the device in in the pocket, ignore 51 // any passwords with length less than or equal to this length. 52 protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; 53 KeyguardAbsKeyInputView(Context context)54 public KeyguardAbsKeyInputView(Context context) { 55 this(context, null); 56 } 57 KeyguardAbsKeyInputView(Context context, AttributeSet attrs)58 public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) { 59 super(context, attrs); 60 } 61 62 @Override setKeyguardCallback(KeyguardSecurityCallback callback)63 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 64 mCallback = callback; 65 } 66 67 @Override setLockPatternUtils(LockPatternUtils utils)68 public void setLockPatternUtils(LockPatternUtils utils) { 69 mLockPatternUtils = utils; 70 mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled(); 71 } 72 73 @Override reset()74 public void reset() { 75 // start fresh 76 mDismissing = false; 77 resetPasswordText(false /* animate */, false /* announce */); 78 // if the user is currently locked out, enforce it. 79 long deadline = mLockPatternUtils.getLockoutAttemptDeadline( 80 KeyguardUpdateMonitor.getCurrentUser()); 81 if (shouldLockout(deadline)) { 82 handleAttemptLockout(deadline); 83 } else { 84 resetState(); 85 } 86 } 87 88 // Allow subclasses to override this behavior shouldLockout(long deadline)89 protected boolean shouldLockout(long deadline) { 90 return deadline != 0; 91 } 92 getPasswordTextViewId()93 protected abstract int getPasswordTextViewId(); resetState()94 protected abstract void resetState(); 95 96 @Override onFinishInflate()97 protected void onFinishInflate() { 98 mLockPatternUtils = new LockPatternUtils(mContext); 99 mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this); 100 mEcaView = findViewById(R.id.keyguard_selector_fade_container); 101 102 EmergencyButton button = findViewById(R.id.emergency_call_button); 103 if (button != null) { 104 button.setCallback(this); 105 } 106 } 107 108 @Override onEmergencyButtonClickedWhenInCall()109 public void onEmergencyButtonClickedWhenInCall() { 110 mCallback.reset(); 111 } 112 113 /* 114 * Override this if you have a different string for "wrong password" 115 * 116 * Note that PIN/PUK have their own implementation of verifyPasswordAndUnlock and so don't need this 117 */ getWrongPasswordStringId()118 protected int getWrongPasswordStringId() { 119 return R.string.kg_wrong_password; 120 } 121 verifyPasswordAndUnlock()122 protected void verifyPasswordAndUnlock() { 123 if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. 124 125 final String entry = getPasswordText(); 126 setPasswordEntryInputEnabled(false); 127 if (mPendingLockCheck != null) { 128 mPendingLockCheck.cancel(false); 129 } 130 131 final int userId = KeyguardUpdateMonitor.getCurrentUser(); 132 if (entry.length() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { 133 // to avoid accidental lockout, only count attempts that are long enough to be a 134 // real password. This may require some tweaking. 135 setPasswordEntryInputEnabled(true); 136 onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */); 137 return; 138 } 139 140 if (LatencyTracker.isEnabled(mContext)) { 141 LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); 142 LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); 143 } 144 mPendingLockCheck = LockPatternChecker.checkPassword( 145 mLockPatternUtils, 146 entry, 147 userId, 148 new LockPatternChecker.OnCheckCallback() { 149 150 @Override 151 public void onEarlyMatched() { 152 if (LatencyTracker.isEnabled(mContext)) { 153 LatencyTracker.getInstance(mContext).onActionEnd( 154 ACTION_CHECK_CREDENTIAL); 155 } 156 onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, 157 true /* isValidPassword */); 158 } 159 160 @Override 161 public void onChecked(boolean matched, int timeoutMs) { 162 if (LatencyTracker.isEnabled(mContext)) { 163 LatencyTracker.getInstance(mContext).onActionEnd( 164 ACTION_CHECK_CREDENTIAL_UNLOCKED); 165 } 166 setPasswordEntryInputEnabled(true); 167 mPendingLockCheck = null; 168 if (!matched) { 169 onPasswordChecked(userId, false /* matched */, timeoutMs, 170 true /* isValidPassword */); 171 } 172 } 173 174 @Override 175 public void onCancelled() { 176 // We already got dismissed with the early matched callback, so we cancelled 177 // the check. However, we still need to note down the latency. 178 if (LatencyTracker.isEnabled(mContext)) { 179 LatencyTracker.getInstance(mContext).onActionEnd( 180 ACTION_CHECK_CREDENTIAL_UNLOCKED); 181 } 182 } 183 }); 184 } 185 onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword)186 private void onPasswordChecked(int userId, boolean matched, int timeoutMs, 187 boolean isValidPassword) { 188 boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; 189 if (matched) { 190 mCallback.reportUnlockAttempt(userId, true, 0); 191 if (dismissKeyguard) { 192 mDismissing = true; 193 mCallback.dismiss(true, userId); 194 } 195 } else { 196 if (isValidPassword) { 197 mCallback.reportUnlockAttempt(userId, false, timeoutMs); 198 if (timeoutMs > 0) { 199 long deadline = mLockPatternUtils.setLockoutAttemptDeadline( 200 userId, timeoutMs); 201 handleAttemptLockout(deadline); 202 } 203 } 204 if (timeoutMs == 0) { 205 mSecurityMessageDisplay.setMessage(getWrongPasswordStringId()); 206 } 207 } 208 resetPasswordText(true /* animate */, !matched /* announce deletion if no match */); 209 } 210 resetPasswordText(boolean animate, boolean announce)211 protected abstract void resetPasswordText(boolean animate, boolean announce); getPasswordText()212 protected abstract String getPasswordText(); setPasswordEntryEnabled(boolean enabled)213 protected abstract void setPasswordEntryEnabled(boolean enabled); setPasswordEntryInputEnabled(boolean enabled)214 protected abstract void setPasswordEntryInputEnabled(boolean enabled); 215 216 // Prevent user from using the PIN/Password entry until scheduled deadline. handleAttemptLockout(long elapsedRealtimeDeadline)217 protected void handleAttemptLockout(long elapsedRealtimeDeadline) { 218 setPasswordEntryEnabled(false); 219 long elapsedRealtime = SystemClock.elapsedRealtime(); 220 long secondsInFuture = (long) Math.ceil( 221 (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); 222 mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { 223 224 @Override 225 public void onTick(long millisUntilFinished) { 226 int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0); 227 mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString( 228 R.plurals.kg_too_many_failed_attempts_countdown, 229 secondsRemaining, secondsRemaining)); 230 } 231 232 @Override 233 public void onFinish() { 234 mSecurityMessageDisplay.setMessage(""); 235 resetState(); 236 } 237 }.start(); 238 } 239 onUserInput()240 protected void onUserInput() { 241 if (mCallback != null) { 242 mCallback.userActivity(); 243 } 244 mSecurityMessageDisplay.setMessage(""); 245 } 246 247 @Override onKeyDown(int keyCode, KeyEvent event)248 public boolean onKeyDown(int keyCode, KeyEvent event) { 249 // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN. 250 // We don't want to consider it valid user input because the UI 251 // will already respond to the event. 252 if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 253 onUserInput(); 254 } 255 return false; 256 } 257 258 @Override needsInput()259 public boolean needsInput() { 260 return false; 261 } 262 263 @Override onPause()264 public void onPause() { 265 if (mCountdownTimer != null) { 266 mCountdownTimer.cancel(); 267 mCountdownTimer = null; 268 } 269 if (mPendingLockCheck != null) { 270 mPendingLockCheck.cancel(false); 271 mPendingLockCheck = null; 272 } 273 reset(); 274 } 275 276 @Override onResume(int reason)277 public void onResume(int reason) { 278 } 279 280 @Override getCallback()281 public KeyguardSecurityCallback getCallback() { 282 return mCallback; 283 } 284 285 @Override showPromptReason(int reason)286 public void showPromptReason(int reason) { 287 if (reason != PROMPT_REASON_NONE) { 288 int promtReasonStringRes = getPromptReasonStringRes(reason); 289 if (promtReasonStringRes != 0) { 290 mSecurityMessageDisplay.setMessage(promtReasonStringRes); 291 } 292 } 293 } 294 295 @Override showMessage(CharSequence message, int color)296 public void showMessage(CharSequence message, int color) { 297 mSecurityMessageDisplay.setNextMessageColor(color); 298 mSecurityMessageDisplay.setMessage(message); 299 } 300 getPromptReasonStringRes(int reason)301 protected abstract int getPromptReasonStringRes(int reason); 302 303 // Cause a VIRTUAL_KEY vibration doHapticKeyClick()304 public void doHapticKeyClick() { 305 if (mEnableHaptics) { 306 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, 307 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING 308 | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); 309 } 310 } 311 312 @Override startDisappearAnimation(Runnable finishRunnable)313 public boolean startDisappearAnimation(Runnable finishRunnable) { 314 return false; 315 } 316 } 317 318