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 */); 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 if (entry.length() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { 127 // to avoid accidental lockout, only count attempts that are long enough to be a 128 // real password. This may require some tweaking. 129 setPasswordEntryInputEnabled(true); 130 onPasswordChecked(false /* matched */, 0, false /* not valid - too short */); 131 return; 132 } 133 134 mPendingLockCheck = LockPatternChecker.checkPassword( 135 mLockPatternUtils, 136 entry, 137 KeyguardUpdateMonitor.getCurrentUser(), 138 new LockPatternChecker.OnCheckCallback() { 139 @Override 140 public void onChecked(boolean matched, int timeoutMs) { 141 setPasswordEntryInputEnabled(true); 142 mPendingLockCheck = null; 143 onPasswordChecked(matched, timeoutMs, true /* isValidPassword */); 144 } 145 }); 146 } 147 onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword)148 private void onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword) { 149 if (matched) { 150 mDismissing = true; 151 mCallback.reportUnlockAttempt(true, 0); 152 mCallback.dismiss(true); 153 } else { 154 if (isValidPassword) { 155 mCallback.reportUnlockAttempt(false, timeoutMs); 156 if (timeoutMs > 0) { 157 long deadline = mLockPatternUtils.setLockoutAttemptDeadline( 158 KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); 159 handleAttemptLockout(deadline); 160 } 161 } 162 if (timeoutMs == 0) { 163 mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); 164 } 165 } 166 resetPasswordText(true /* animate */); 167 } 168 resetPasswordText(boolean animate)169 protected abstract void resetPasswordText(boolean animate); getPasswordText()170 protected abstract String getPasswordText(); setPasswordEntryEnabled(boolean enabled)171 protected abstract void setPasswordEntryEnabled(boolean enabled); setPasswordEntryInputEnabled(boolean enabled)172 protected abstract void setPasswordEntryInputEnabled(boolean enabled); 173 174 // Prevent user from using the PIN/Password entry until scheduled deadline. handleAttemptLockout(long elapsedRealtimeDeadline)175 protected void handleAttemptLockout(long elapsedRealtimeDeadline) { 176 setPasswordEntryEnabled(false); 177 long elapsedRealtime = SystemClock.elapsedRealtime(); 178 new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { 179 180 @Override 181 public void onTick(long millisUntilFinished) { 182 int secondsRemaining = (int) (millisUntilFinished / 1000); 183 mSecurityMessageDisplay.setMessage( 184 R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining); 185 } 186 187 @Override 188 public void onFinish() { 189 mSecurityMessageDisplay.setMessage("", false); 190 resetState(); 191 } 192 }.start(); 193 } 194 onUserInput()195 protected void onUserInput() { 196 if (mCallback != null) { 197 mCallback.userActivity(); 198 } 199 mSecurityMessageDisplay.setMessage("", false); 200 } 201 202 @Override onKeyDown(int keyCode, KeyEvent event)203 public boolean onKeyDown(int keyCode, KeyEvent event) { 204 onUserInput(); 205 return false; 206 } 207 208 @Override needsInput()209 public boolean needsInput() { 210 return false; 211 } 212 213 @Override onPause()214 public void onPause() { 215 if (mPendingLockCheck != null) { 216 mPendingLockCheck.cancel(false); 217 mPendingLockCheck = null; 218 } 219 } 220 221 @Override onResume(int reason)222 public void onResume(int reason) { 223 reset(); 224 } 225 226 @Override getCallback()227 public KeyguardSecurityCallback getCallback() { 228 return mCallback; 229 } 230 231 @Override showPromptReason(int reason)232 public void showPromptReason(int reason) { 233 if (reason != PROMPT_REASON_NONE) { 234 int promtReasonStringRes = getPromtReasonStringRes(reason); 235 if (promtReasonStringRes != 0) { 236 mSecurityMessageDisplay.setMessage(promtReasonStringRes, 237 true /* important */); 238 } 239 } 240 } 241 242 @Override showMessage(String message, int color)243 public void showMessage(String message, int color) { 244 mSecurityMessageDisplay.setNextMessageColor(color); 245 mSecurityMessageDisplay.setMessage(message, true /* important */); 246 } 247 getPromtReasonStringRes(int reason)248 protected abstract int getPromtReasonStringRes(int reason); 249 250 // Cause a VIRTUAL_KEY vibration doHapticKeyClick()251 public void doHapticKeyClick() { 252 if (mEnableHaptics) { 253 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, 254 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING 255 | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); 256 } 257 } 258 259 @Override startDisappearAnimation(Runnable finishRunnable)260 public boolean startDisappearAnimation(Runnable finishRunnable) { 261 return false; 262 } 263 } 264 265