OSDN Git Service

CDCA is plumbed through BP
[android-x86/packages-apps-Settings.git] / src / com / android / settings / password / ConfirmLockPassword.java
1 /*
2  * Copyright (C) 2010 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.password;
18
19 import android.app.admin.DevicePolicyManager;
20 import android.app.settings.SettingsEnums;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.graphics.Typeface;
24 import android.os.AsyncTask;
25 import android.os.Bundle;
26 import android.os.CountDownTimer;
27 import android.os.SystemClock;
28 import android.os.UserManager;
29 import android.os.storage.StorageManager;
30 import android.text.InputType;
31 import android.text.TextUtils;
32 import android.view.KeyEvent;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.view.View.OnClickListener;
36 import android.view.ViewGroup;
37 import android.view.animation.AnimationUtils;
38 import android.view.inputmethod.EditorInfo;
39 import android.view.inputmethod.InputMethodManager;
40 import android.widget.TextView;
41 import android.widget.TextView.OnEditorActionListener;
42
43 import androidx.fragment.app.Fragment;
44
45 import com.android.internal.widget.LockPatternChecker;
46 import com.android.internal.widget.LockPatternUtils;
47 import com.android.internal.widget.TextViewInputDisabler;
48 import com.android.settings.R;
49 import com.android.settings.widget.ImeAwareEditText;
50 import com.android.settingslib.animation.AppearAnimationUtils;
51 import com.android.settingslib.animation.DisappearAnimationUtils;
52
53 import java.util.ArrayList;
54
55 public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
56
57     // The index of the array is isStrongAuth << 2 + isProfile << 1 + isAlpha.
58     private static final int[] DETAIL_TEXTS = new int[] {
59         R.string.lockpassword_confirm_your_pin_generic,
60         R.string.lockpassword_confirm_your_password_generic,
61         R.string.lockpassword_confirm_your_pin_generic_profile,
62         R.string.lockpassword_confirm_your_password_generic_profile,
63         R.string.lockpassword_strong_auth_required_device_pin,
64         R.string.lockpassword_strong_auth_required_device_password,
65         R.string.lockpassword_strong_auth_required_work_pin,
66         R.string.lockpassword_strong_auth_required_work_password,
67     };
68
69     public static class InternalActivity extends ConfirmLockPassword {
70     }
71
72     @Override
73     public Intent getIntent() {
74         Intent modIntent = new Intent(super.getIntent());
75         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
76         return modIntent;
77     }
78
79     @Override
80     protected boolean isValidFragment(String fragmentName) {
81         if (ConfirmLockPasswordFragment.class.getName().equals(fragmentName)) return true;
82         return false;
83     }
84
85     @Override
86     public void onWindowFocusChanged(boolean hasFocus) {
87         super.onWindowFocusChanged(hasFocus);
88         Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_content);
89         if (fragment != null && fragment instanceof ConfirmLockPasswordFragment) {
90             ((ConfirmLockPasswordFragment)fragment).onWindowFocusChanged(hasFocus);
91         }
92     }
93
94     public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
95             implements OnClickListener, OnEditorActionListener,
96             CredentialCheckResultTracker.Listener {
97         private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
98         private ImeAwareEditText mPasswordEntry;
99         private TextViewInputDisabler mPasswordEntryInputDisabler;
100         private AsyncTask<?, ?, ?> mPendingLockCheck;
101         private CredentialCheckResultTracker mCredentialCheckResultTracker;
102         private boolean mDisappearing = false;
103         private TextView mHeaderTextView;
104         private TextView mDetailsTextView;
105         private CountDownTimer mCountdownTimer;
106         private boolean mIsAlpha;
107         private InputMethodManager mImm;
108         private AppearAnimationUtils mAppearAnimationUtils;
109         private DisappearAnimationUtils mDisappearAnimationUtils;
110
111         // required constructor for fragments
112         public ConfirmLockPasswordFragment() {
113
114         }
115
116         @Override
117         public View onCreateView(LayoutInflater inflater, ViewGroup container,
118                 Bundle savedInstanceState) {
119             final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
120                     mEffectiveUserId);
121
122             ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
123             View view = inflater.inflate(
124                     activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.NORMAL
125                             ? R.layout.confirm_lock_password_normal
126                             : R.layout.confirm_lock_password,
127                     container,
128                     false);
129
130             mPasswordEntry = (ImeAwareEditText) view.findViewById(R.id.password_entry);
131             mPasswordEntry.setOnEditorActionListener(this);
132             // EditText inside ScrollView doesn't automatically get focus.
133             mPasswordEntry.requestFocus();
134             mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
135
136             mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
137             if (mHeaderTextView == null) {
138                 mHeaderTextView = view.findViewById(R.id.suc_layout_title);
139             }
140             mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
141             mErrorTextView = (TextView) view.findViewById(R.id.errorText);
142             mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
143                     || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
144                     || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
145                     || DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
146
147             mImm = (InputMethodManager) getActivity().getSystemService(
148                     Context.INPUT_METHOD_SERVICE);
149
150             Intent intent = getActivity().getIntent();
151             if (intent != null) {
152                 CharSequence headerMessage = intent.getCharSequenceExtra(
153                         ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
154                 CharSequence detailsMessage = intent.getCharSequenceExtra(
155                         ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
156                 if (TextUtils.isEmpty(headerMessage)) {
157                     headerMessage = getString(getDefaultHeader());
158                 }
159                 if (TextUtils.isEmpty(detailsMessage)) {
160                     detailsMessage = getString(getDefaultDetails());
161                 }
162                 mHeaderTextView.setText(headerMessage);
163                 mDetailsTextView.setText(detailsMessage);
164             }
165             int currentType = mPasswordEntry.getInputType();
166             mPasswordEntry.setInputType(mIsAlpha ? currentType
167                     : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
168             // Can't set via XML since setInputType resets the fontFamily to null
169             mPasswordEntry.setTypeface(Typeface.create(
170                     getContext().getString(com.android.internal.R.string.config_headlineFontFamily),
171                     Typeface.NORMAL));
172             mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
173                     220, 2f /* translationScale */, 1f /* delayScale*/,
174                     AnimationUtils.loadInterpolator(getContext(),
175                             android.R.interpolator.linear_out_slow_in));
176             mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
177                     110, 1f /* translationScale */,
178                     0.5f /* delayScale */, AnimationUtils.loadInterpolator(
179                             getContext(), android.R.interpolator.fast_out_linear_in));
180             setAccessibilityTitle(mHeaderTextView.getText());
181
182             mCredentialCheckResultTracker = (CredentialCheckResultTracker) getFragmentManager()
183                     .findFragmentByTag(FRAGMENT_TAG_CHECK_LOCK_RESULT);
184             if (mCredentialCheckResultTracker == null) {
185                 mCredentialCheckResultTracker = new CredentialCheckResultTracker();
186                 getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
187                         FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
188             }
189
190             return view;
191         }
192
193         private int getDefaultHeader() {
194             if (mFrp) {
195                 return mIsAlpha ? R.string.lockpassword_confirm_your_password_header_frp
196                         : R.string.lockpassword_confirm_your_pin_header_frp;
197             }
198             return mIsAlpha ? R.string.lockpassword_confirm_your_password_header
199                     : R.string.lockpassword_confirm_your_pin_header;
200         }
201
202         private int getDefaultDetails() {
203             if (mFrp) {
204                 return mIsAlpha ? R.string.lockpassword_confirm_your_password_details_frp
205                         : R.string.lockpassword_confirm_your_pin_details_frp;
206             }
207             boolean isStrongAuthRequired = isStrongAuthRequired();
208             boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
209             // Map boolean flags to an index by isStrongAuth << 2 + isProfile << 1 + isAlpha.
210             int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((isProfile ? 1 : 0) << 1)
211                     + (mIsAlpha ? 1 : 0);
212             return DETAIL_TEXTS[index];
213         }
214
215         private int getErrorMessage() {
216             return mIsAlpha ? R.string.lockpassword_invalid_password
217                     : R.string.lockpassword_invalid_pin;
218         }
219
220         @Override
221         protected int getLastTryErrorMessage(int userType) {
222             switch (userType) {
223                 case USER_TYPE_PRIMARY:
224                     return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_device
225                             : R.string.lock_last_pin_attempt_before_wipe_device;
226                 case USER_TYPE_MANAGED_PROFILE:
227                     return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_profile
228                             : R.string.lock_last_pin_attempt_before_wipe_profile;
229                 case USER_TYPE_SECONDARY:
230                     return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_user
231                             : R.string.lock_last_pin_attempt_before_wipe_user;
232                 default:
233                     throw new IllegalArgumentException("Unrecognized user type:" + userType);
234             }
235         }
236
237         @Override
238         public void prepareEnterAnimation() {
239             super.prepareEnterAnimation();
240             mHeaderTextView.setAlpha(0f);
241             mDetailsTextView.setAlpha(0f);
242             mCancelButton.setAlpha(0f);
243             mPasswordEntry.setAlpha(0f);
244             mErrorTextView.setAlpha(0f);
245         }
246
247         private View[] getActiveViews() {
248             ArrayList<View> result = new ArrayList<>();
249             result.add(mHeaderTextView);
250             result.add(mDetailsTextView);
251             if (mCancelButton.getVisibility() == View.VISIBLE) {
252                 result.add(mCancelButton);
253             }
254             result.add(mPasswordEntry);
255             result.add(mErrorTextView);
256             return result.toArray(new View[] {});
257         }
258
259         @Override
260         public void startEnterAnimation() {
261             super.startEnterAnimation();
262             mAppearAnimationUtils.startAnimation(getActiveViews(), this::updatePasswordEntry);
263         }
264
265         @Override
266         public void onPause() {
267             super.onPause();
268             if (mCountdownTimer != null) {
269                 mCountdownTimer.cancel();
270                 mCountdownTimer = null;
271             }
272             mCredentialCheckResultTracker.setListener(null);
273         }
274
275         @Override
276         public int getMetricsCategory() {
277             return SettingsEnums.CONFIRM_LOCK_PASSWORD;
278         }
279
280         @Override
281         public void onResume() {
282             super.onResume();
283             long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
284             if (deadline != 0) {
285                 mCredentialCheckResultTracker.clearResult();
286                 handleAttemptLockout(deadline);
287             } else {
288                 updatePasswordEntry();
289                 mErrorTextView.setText("");
290                 updateErrorMessage(
291                         mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
292             }
293             mCredentialCheckResultTracker.setListener(this);
294         }
295
296         @Override
297         protected void authenticationSucceeded() {
298             mCredentialCheckResultTracker.setResult(true, new Intent(), 0, mEffectiveUserId);
299         }
300
301         private void updatePasswordEntry() {
302             final boolean isLockedOut =
303                     mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId) != 0;
304             mPasswordEntry.setEnabled(!isLockedOut);
305             mPasswordEntryInputDisabler.setInputEnabled(!isLockedOut);
306             if (isLockedOut) {
307                 mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), 0 /*flags*/);
308             } else {
309                 mPasswordEntry.scheduleShowSoftInput();
310             }
311         }
312
313         public void onWindowFocusChanged(boolean hasFocus) {
314             if (!hasFocus) {
315                 return;
316             }
317             // Post to let window focus logic to finish to allow soft input show/hide properly.
318             mPasswordEntry.post(this::updatePasswordEntry);
319         }
320
321         private void handleNext() {
322             if (mPendingLockCheck != null || mDisappearing) {
323                 return;
324             }
325
326             final String pin = mPasswordEntry.getText().toString();
327             if (TextUtils.isEmpty(pin)) {
328                 return;
329             }
330
331             mPasswordEntryInputDisabler.setInputEnabled(false);
332             final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
333                     ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
334
335             Intent intent = new Intent();
336             if (verifyChallenge)  {
337                 if (isInternalActivity()) {
338                     startVerifyPassword(pin, intent);
339                     return;
340                 }
341             } else {
342                 startCheckPassword(pin, intent);
343                 return;
344             }
345
346             mCredentialCheckResultTracker.setResult(false, intent, 0, mEffectiveUserId);
347         }
348
349         private boolean isInternalActivity() {
350             return getActivity() instanceof ConfirmLockPassword.InternalActivity;
351         }
352
353         private void startVerifyPassword(final String pin, final Intent intent) {
354             long challenge = getActivity().getIntent().getLongExtra(
355                     ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
356             final int localEffectiveUserId = mEffectiveUserId;
357             final int localUserId = mUserId;
358             final LockPatternChecker.OnVerifyCallback onVerifyCallback =
359                     new LockPatternChecker.OnVerifyCallback() {
360                         @Override
361                         public void onVerified(byte[] token, int timeoutMs) {
362                             mPendingLockCheck = null;
363                             boolean matched = false;
364                             if (token != null) {
365                                 matched = true;
366                                 if (mReturnCredentials) {
367                                     intent.putExtra(
368                                             ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
369                                             token);
370                                 }
371                             }
372                             mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
373                                     localEffectiveUserId);
374                         }
375             };
376             mPendingLockCheck = (localEffectiveUserId == localUserId)
377                     ? LockPatternChecker.verifyPassword(
378                             mLockPatternUtils, pin, challenge, localUserId, onVerifyCallback)
379                     : LockPatternChecker.verifyTiedProfileChallenge(
380                             mLockPatternUtils, pin, false, challenge, localUserId,
381                             onVerifyCallback);
382         }
383
384         private void startCheckPassword(final String pin, final Intent intent) {
385             final int localEffectiveUserId = mEffectiveUserId;
386             mPendingLockCheck = LockPatternChecker.checkPassword(
387                     mLockPatternUtils,
388                     pin,
389                     localEffectiveUserId,
390                     new LockPatternChecker.OnCheckCallback() {
391                         @Override
392                         public void onChecked(boolean matched, int timeoutMs) {
393                             mPendingLockCheck = null;
394                             if (matched && isInternalActivity() && mReturnCredentials) {
395                                 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
396                                                 mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
397                                                          : StorageManager.CRYPT_TYPE_PIN);
398                                 intent.putExtra(
399                                         ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
400                             }
401                             mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
402                                     localEffectiveUserId);
403                         }
404                     });
405         }
406
407         private void startDisappearAnimation(final Intent intent) {
408             if (mDisappearing) {
409                 return;
410             }
411             mDisappearing = true;
412
413             final ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
414             // Bail if there is no active activity.
415             if (activity == null || activity.isFinishing()) {
416                 return;
417             }
418             if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
419                 mDisappearAnimationUtils.startAnimation(getActiveViews(), () -> {
420                     activity.setResult(RESULT_OK, intent);
421                     activity.finish();
422                     activity.overridePendingTransition(
423                             R.anim.confirm_credential_close_enter,
424                             R.anim.confirm_credential_close_exit);
425                 });
426             } else {
427                 activity.setResult(RESULT_OK, intent);
428                 activity.finish();
429             }
430         }
431
432         private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs,
433                 int effectiveUserId, boolean newResult) {
434             mPasswordEntryInputDisabler.setInputEnabled(true);
435             if (matched) {
436                 if (newResult) {
437                     ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
438                             mUserManager, mEffectiveUserId);
439                 }
440                 mBiometricManager.onConfirmDeviceCredentialSuccess();
441                 startDisappearAnimation(intent);
442                 ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
443             } else {
444                 if (timeoutMs > 0) {
445                     refreshLockScreen();
446                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
447                             effectiveUserId, timeoutMs);
448                     handleAttemptLockout(deadline);
449                 } else {
450                     showError(getErrorMessage(), CLEAR_WRONG_ATTEMPT_TIMEOUT_MS);
451                 }
452                 if (newResult) {
453                     reportFailedAttempt();
454                 }
455             }
456         }
457
458         @Override
459         public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
460                 int effectiveUserId, boolean newResult) {
461             onPasswordChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
462         }
463
464         @Override
465         protected void onShowError() {
466             mPasswordEntry.setText(null);
467         }
468
469         private void handleAttemptLockout(long elapsedRealtimeDeadline) {
470             mCountdownTimer = new CountDownTimer(
471                     elapsedRealtimeDeadline - SystemClock.elapsedRealtime(),
472                     LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
473
474                 @Override
475                 public void onTick(long millisUntilFinished) {
476                     final int secondsCountdown = (int) (millisUntilFinished / 1000);
477                     showError(getString(
478                             R.string.lockpattern_too_many_failed_confirmation_attempts,
479                             secondsCountdown), 0);
480                 }
481
482                 @Override
483                 public void onFinish() {
484                     updatePasswordEntry();
485                     mErrorTextView.setText("");
486                     updateErrorMessage(
487                             mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
488                 }
489             }.start();
490             updatePasswordEntry();
491         }
492
493         public void onClick(View v) {
494             switch (v.getId()) {
495                 case R.id.next_button:
496                     handleNext();
497                     break;
498
499                 case R.id.cancel_button:
500                     getActivity().setResult(RESULT_CANCELED);
501                     getActivity().finish();
502                     break;
503             }
504         }
505
506         // {@link OnEditorActionListener} methods.
507         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
508             // Check if this was the result of hitting the enter or "done" key
509             if (actionId == EditorInfo.IME_NULL
510                     || actionId == EditorInfo.IME_ACTION_DONE
511                     || actionId == EditorInfo.IME_ACTION_NEXT) {
512                 handleNext();
513                 return true;
514             }
515             return false;
516         }
517     }
518 }