• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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