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