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