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