OSDN Git Service

Merge "Clear only keystore credential entires" into mnc-dev
[android-x86/packages-apps-Settings.git] / src / com / android / settings / ConfirmLockPattern.java
1 /*
2  * Copyright (C) 2008 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.settings;
18
19 import com.android.internal.logging.MetricsLogger;
20 import com.android.internal.widget.LockPatternUtils;
21 import com.android.internal.widget.LockPatternView;
22 import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
23 import com.android.internal.widget.LockPatternChecker;
24 import com.android.internal.widget.LockPatternView.Cell;
25
26 import android.annotation.Nullable;
27 import android.app.Activity;
28 import android.content.Intent;
29 import android.os.CountDownTimer;
30 import android.os.SystemClock;
31 import android.os.AsyncTask;
32 import android.os.Bundle;
33 import android.os.UserHandle;
34 import android.os.storage.StorageManager;
35 import android.view.MenuItem;
36 import android.widget.TextView;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.view.ViewGroup;
40
41 import java.util.List;
42
43 /**
44  * Launch this when you want the user to confirm their lock pattern.
45  *
46  * Sets an activity result of {@link Activity#RESULT_OK} when the user
47  * successfully confirmed their pattern.
48  */
49 public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
50
51     public static class InternalActivity extends ConfirmLockPattern {
52     }
53
54     private enum Stage {
55         NeedToUnlock,
56         NeedToUnlockWrong,
57         LockedOut
58     }
59
60     @Override
61     public Intent getIntent() {
62         Intent modIntent = new Intent(super.getIntent());
63         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPatternFragment.class.getName());
64         return modIntent;
65     }
66
67     @Override
68     protected boolean isValidFragment(String fragmentName) {
69         if (ConfirmLockPatternFragment.class.getName().equals(fragmentName)) return true;
70         return false;
71     }
72
73     public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment {
74
75         // how long we wait to clear a wrong pattern
76         private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
77
78         private static final String KEY_NUM_WRONG_ATTEMPTS = "num_wrong_attempts";
79
80         private LockPatternView mLockPatternView;
81         private LockPatternUtils mLockPatternUtils;
82         private AsyncTask<?, ?, ?> mPendingLockCheck;
83         private int mNumWrongConfirmAttempts;
84         private CountDownTimer mCountdownTimer;
85
86         private TextView mHeaderTextView;
87         private TextView mDetailsTextView;
88         private TextView mErrorTextView;
89         private View mLeftSpacerLandscape;
90         private View mRightSpacerLandscape;
91
92         // caller-supplied text for various prompts
93         private CharSequence mHeaderText;
94         private CharSequence mDetailsText;
95
96         // required constructor for fragments
97         public ConfirmLockPatternFragment() {
98
99         }
100
101         @Override
102         public void onCreate(Bundle savedInstanceState) {
103             super.onCreate(savedInstanceState);
104             mLockPatternUtils = new LockPatternUtils(getActivity());
105         }
106
107         @Override
108         public View onCreateView(LayoutInflater inflater, ViewGroup container,
109                 Bundle savedInstanceState) {
110             View view = inflater.inflate(R.layout.confirm_lock_pattern, null);
111             mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
112             mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
113             mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
114             mErrorTextView = (TextView) view.findViewById(R.id.errorText);
115             mLeftSpacerLandscape = view.findViewById(R.id.leftSpacer);
116             mRightSpacerLandscape = view.findViewById(R.id.rightSpacer);
117
118             // make it so unhandled touch events within the unlock screen go to the
119             // lock pattern view.
120             final LinearLayoutWithDefaultTouchRecepient topLayout
121                     = (LinearLayoutWithDefaultTouchRecepient) view.findViewById(R.id.topLayout);
122             topLayout.setDefaultTouchRecepient(mLockPatternView);
123
124             Intent intent = getActivity().getIntent();
125             if (intent != null) {
126                 mHeaderText = intent.getCharSequenceExtra(
127                         ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
128                 mDetailsText = intent.getCharSequenceExtra(
129                         ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
130             }
131
132             mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
133             mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
134             updateStage(Stage.NeedToUnlock);
135
136             if (savedInstanceState != null) {
137                 mNumWrongConfirmAttempts = savedInstanceState.getInt(KEY_NUM_WRONG_ATTEMPTS);
138             } else {
139                 // on first launch, if no lock pattern is set, then finish with
140                 // success (don't want user to get stuck confirming something that
141                 // doesn't exist).
142                 if (!mLockPatternUtils.isLockPatternEnabled(UserHandle.myUserId())) {
143                     getActivity().setResult(Activity.RESULT_OK);
144                     getActivity().finish();
145                 }
146             }
147             return view;
148         }
149
150         @Override
151         public void onSaveInstanceState(Bundle outState) {
152             // deliberately not calling super since we are managing this in full
153             outState.putInt(KEY_NUM_WRONG_ATTEMPTS, mNumWrongConfirmAttempts);
154         }
155
156         @Override
157         public void onPause() {
158             super.onPause();
159
160             if (mCountdownTimer != null) {
161                 mCountdownTimer.cancel();
162             }
163             if (mPendingLockCheck != null) {
164                 mPendingLockCheck.cancel(false);
165                 mPendingLockCheck = null;
166             }
167         }
168
169         @Override
170         protected int getMetricsCategory() {
171             return MetricsLogger.CONFIRM_LOCK_PATTERN;
172         }
173
174         @Override
175         public void onResume() {
176             super.onResume();
177
178             // if the user is currently locked out, enforce it.
179             long deadline = mLockPatternUtils.getLockoutAttemptDeadline(UserHandle.myUserId());
180             if (deadline != 0) {
181                 handleAttemptLockout(deadline);
182             } else if (!mLockPatternView.isEnabled()) {
183                 // The deadline has passed, but the timer was cancelled. Or the pending lock
184                 // check was cancelled. Need to clean up.
185                 mNumWrongConfirmAttempts = 0;
186                 updateStage(Stage.NeedToUnlock);
187             }
188         }
189
190         private void updateStage(Stage stage) {
191             switch (stage) {
192                 case NeedToUnlock:
193                     if (mHeaderText != null) {
194                         mHeaderTextView.setText(mHeaderText);
195                     } else {
196                         mHeaderTextView.setText(R.string.lockpassword_confirm_your_pattern_header);
197                     }
198                     if (mDetailsText != null) {
199                         mDetailsTextView.setText(mDetailsText);
200                     } else {
201                         mDetailsTextView.setText(
202                                 R.string.lockpassword_confirm_your_pattern_generic);
203                     }
204                     mErrorTextView.setText("");
205
206                     mLockPatternView.setEnabled(true);
207                     mLockPatternView.enableInput();
208                     mLockPatternView.clearPattern();
209                     break;
210                 case NeedToUnlockWrong:
211                     mErrorTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
212
213                     mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
214                     mLockPatternView.setEnabled(true);
215                     mLockPatternView.enableInput();
216                     break;
217                 case LockedOut:
218                     mLockPatternView.clearPattern();
219                     // enabled = false means: disable input, and have the
220                     // appearance of being disabled.
221                     mLockPatternView.setEnabled(false); // appearance of being disabled
222                     break;
223             }
224
225             // Always announce the header for accessibility. This is a no-op
226             // when accessibility is disabled.
227             mHeaderTextView.announceForAccessibility(mHeaderTextView.getText());
228         }
229
230         private Runnable mClearPatternRunnable = new Runnable() {
231             public void run() {
232                 mLockPatternView.clearPattern();
233             }
234         };
235
236         // clear the wrong pattern unless they have started a new one
237         // already
238         private void postClearPatternRunnable() {
239             mLockPatternView.removeCallbacks(mClearPatternRunnable);
240             mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
241         }
242
243         @Override
244         protected void authenticationSucceeded() {
245             Intent intent = new Intent();
246             getActivity().setResult(Activity.RESULT_OK, intent);
247             getActivity().finish();
248         }
249
250         @Override
251         public void onFingerprintIconVisibilityChanged(boolean visible) {
252             if (mLeftSpacerLandscape != null && mRightSpacerLandscape != null) {
253
254                 // In landscape, adjust spacing depending on fingerprint icon visibility.
255                 mLeftSpacerLandscape.setVisibility(visible ? View.GONE : View.VISIBLE);
256                 mRightSpacerLandscape.setVisibility(visible ? View.GONE : View.VISIBLE);
257             }
258         }
259
260         /**
261          * The pattern listener that responds according to a user confirming
262          * an existing lock pattern.
263          */
264         private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener
265                 = new LockPatternView.OnPatternListener()  {
266
267             public void onPatternStart() {
268                 mLockPatternView.removeCallbacks(mClearPatternRunnable);
269             }
270
271             public void onPatternCleared() {
272                 mLockPatternView.removeCallbacks(mClearPatternRunnable);
273             }
274
275             public void onPatternCellAdded(List<Cell> pattern) {
276
277             }
278
279             public void onPatternDetected(List<LockPatternView.Cell> pattern) {
280                 mLockPatternView.setEnabled(false);
281                 if (mPendingLockCheck != null) {
282                     mPendingLockCheck.cancel(false);
283                 }
284
285                 final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
286                         ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
287                 Intent intent = new Intent();
288                 if (verifyChallenge) {
289                     if (isInternalActivity()) {
290                         startVerifyPattern(pattern, intent);
291                         return;
292                     }
293                 } else {
294                     startCheckPattern(pattern, intent);
295                     return;
296                 }
297
298                 onPatternChecked(pattern, false, intent, 0);
299             }
300
301             private boolean isInternalActivity() {
302                 return getActivity() instanceof ConfirmLockPattern.InternalActivity;
303             }
304
305             private void startVerifyPattern(final List<LockPatternView.Cell> pattern,
306                     final Intent intent) {
307                 long challenge = getActivity().getIntent().getLongExtra(
308                         ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
309                 mPendingLockCheck = LockPatternChecker.verifyPattern(
310                         mLockPatternUtils,
311                         pattern,
312                         challenge,
313                         UserHandle.myUserId(),
314                         new LockPatternChecker.OnVerifyCallback() {
315                             @Override
316                             public void onVerified(byte[] token, int timeoutMs) {
317                                 mPendingLockCheck = null;
318                                 boolean matched = false;
319                                 if (token != null) {
320                                     matched = true;
321                                     intent.putExtra(
322                                             ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
323                                             token);
324                                 }
325                                 onPatternChecked(pattern, matched, intent, timeoutMs);
326                             }
327                         });
328             }
329
330             private void startCheckPattern(final List<LockPatternView.Cell> pattern,
331                     final Intent intent) {
332                 if (pattern.size() <= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
333                     onPatternChecked(pattern, false, intent, 0);
334                     return;
335                 }
336
337                 mPendingLockCheck = LockPatternChecker.checkPattern(
338                         mLockPatternUtils,
339                         pattern,
340                         UserHandle.myUserId(),
341                         new LockPatternChecker.OnCheckCallback() {
342                             @Override
343                             public void onChecked(boolean matched, int timeoutMs) {
344                                 mPendingLockCheck = null;
345                                 if (matched && isInternalActivity()) {
346                                     intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
347                                                     StorageManager.CRYPT_TYPE_PATTERN);
348                                     intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
349                                                     LockPatternUtils.patternToString(pattern));
350                                 }
351                                 onPatternChecked(pattern, matched, intent, timeoutMs);
352                             }
353                         });
354             }
355
356             private void onPatternChecked(List<LockPatternView.Cell> pattern,
357                     boolean matched, Intent intent, int timeoutMs) {
358                 mLockPatternView.setEnabled(true);
359                 if (matched) {
360                     getActivity().setResult(Activity.RESULT_OK, intent);
361                     getActivity().finish();
362                 } else {
363                     if (timeoutMs > 0) {
364                         long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
365                                 UserHandle.myUserId(), timeoutMs);
366                         handleAttemptLockout(deadline);
367                     } else {
368                         updateStage(Stage.NeedToUnlockWrong);
369                         postClearPatternRunnable();
370                     }
371                 }
372             }
373         };
374
375
376         private void handleAttemptLockout(long elapsedRealtimeDeadline) {
377             updateStage(Stage.LockedOut);
378             long elapsedRealtime = SystemClock.elapsedRealtime();
379             mCountdownTimer = new CountDownTimer(
380                     elapsedRealtimeDeadline - elapsedRealtime,
381                     LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
382
383                 @Override
384                 public void onTick(long millisUntilFinished) {
385                     final int secondsCountdown = (int) (millisUntilFinished / 1000);
386                     mErrorTextView.setText(getString(
387                             R.string.lockpattern_too_many_failed_confirmation_attempts,
388                             secondsCountdown));
389                 }
390
391                 @Override
392                 public void onFinish() {
393                     mNumWrongConfirmAttempts = 0;
394                     updateStage(Stage.NeedToUnlock);
395                 }
396             }.start();
397         }
398     }
399 }