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