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 141 @Override 142 public void onEarlyMatched() { 143 onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, 144 true /* isValidPassword */); 145 } 146 147 @Override 148 public void onChecked(boolean matched, int timeoutMs) { 149 setPasswordEntryInputEnabled(true); 150 mPendingLockCheck = null; 151 if (!matched) { 152 onPasswordChecked(userId, false /* matched */, timeoutMs, 153 true /* isValidPassword */); 154 } 155 } 156 }); 157 } 158 onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword)159 private void onPasswordChecked(int userId, boolean matched, int timeoutMs, 160 boolean isValidPassword) { 161 boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; 162 if (matched) { 163 mCallback.reportUnlockAttempt(userId, true, 0); 164 if (dismissKeyguard) { 165 mDismissing = true; 166 mCallback.dismiss(true); 167 } 168 } else { 169 if (isValidPassword) { 170 mCallback.reportUnlockAttempt(userId, false, timeoutMs); 171 if (timeoutMs > 0) { 172 long deadline = mLockPatternUtils.setLockoutAttemptDeadline( 173 userId, timeoutMs); 174 handleAttemptLockout(deadline); 175 } 176 } 177 if (timeoutMs == 0) { 178 mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); 179 } 180 } 181 resetPasswordText(true /* animate */, !matched /* announce deletion if no match */); 182 } 183 resetPasswordText(boolean animate, boolean announce)184 protected abstract void resetPasswordText(boolean animate, boolean announce); getPasswordText()185 protected abstract String getPasswordText(); setPasswordEntryEnabled(boolean enabled)186 protected abstract void setPasswordEntryEnabled(boolean enabled); setPasswordEntryInputEnabled(boolean enabled)187 protected abstract void setPasswordEntryInputEnabled(boolean enabled); 188 189 // Prevent user from using the PIN/Password entry until scheduled deadline. handleAttemptLockout(long elapsedRealtimeDeadline)190 protected void handleAttemptLockout(long elapsedRealtimeDeadline) { 191 setPasswordEntryEnabled(false); 192 long elapsedRealtime = SystemClock.elapsedRealtime(); 193 new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { 194 195 @Override 196 public void onTick(long millisUntilFinished) { 197 int secondsRemaining = (int) (millisUntilFinished / 1000); 198 mSecurityMessageDisplay.setMessage( 199 R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining); 200 } 201 202 @Override 203 public void onFinish() { 204 mSecurityMessageDisplay.setMessage("", false); 205 resetState(); 206 } 207 }.start(); 208 } 209 onUserInput()210 protected void onUserInput() { 211 if (mCallback != null) { 212 mCallback.userActivity(); 213 } 214 mSecurityMessageDisplay.setMessage("", false); 215 } 216 217 @Override onKeyDown(int keyCode, KeyEvent event)218 public boolean onKeyDown(int keyCode, KeyEvent event) { 219 onUserInput(); 220 return false; 221 } 222 223 @Override needsInput()224 public boolean needsInput() { 225 return false; 226 } 227 228 @Override onPause()229 public void onPause() { 230 if (mPendingLockCheck != null) { 231 mPendingLockCheck.cancel(false); 232 mPendingLockCheck = null; 233 } 234 } 235 236 @Override onResume(int reason)237 public void onResume(int reason) { 238 reset(); 239 } 240 241 @Override getCallback()242 public KeyguardSecurityCallback getCallback() { 243 return mCallback; 244 } 245 246 @Override showPromptReason(int reason)247 public void showPromptReason(int reason) { 248 if (reason != PROMPT_REASON_NONE) { 249 int promtReasonStringRes = getPromtReasonStringRes(reason); 250 if (promtReasonStringRes != 0) { 251 mSecurityMessageDisplay.setMessage(promtReasonStringRes, 252 true /* important */); 253 } 254 } 255 } 256 257 @Override showMessage(String message, int color)258 public void showMessage(String message, int color) { 259 mSecurityMessageDisplay.setNextMessageColor(color); 260 mSecurityMessageDisplay.setMessage(message, true /* important */); 261 } 262 getPromtReasonStringRes(int reason)263 protected abstract int getPromtReasonStringRes(int reason); 264 265 // Cause a VIRTUAL_KEY vibration doHapticKeyClick()266 public void doHapticKeyClick() { 267 if (mEnableHaptics) { 268 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, 269 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING 270 | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); 271 } 272 } 273 274 @Override startDisappearAnimation(Runnable finishRunnable)275 public boolean startDisappearAnimation(Runnable finishRunnable) { 276 return false; 277 } 278 } 279 280