2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.password;
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;
43 import androidx.fragment.app.Fragment;
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;
53 import java.util.ArrayList;
55 public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
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,
69 public static class InternalActivity extends ConfirmLockPassword {
73 public Intent getIntent() {
74 Intent modIntent = new Intent(super.getIntent());
75 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
80 protected boolean isValidFragment(String fragmentName) {
81 if (ConfirmLockPasswordFragment.class.getName().equals(fragmentName)) return true;
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);
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;
111 // required constructor for fragments
112 public ConfirmLockPasswordFragment() {
117 public View onCreateView(LayoutInflater inflater, ViewGroup container,
118 Bundle savedInstanceState) {
119 final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
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,
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);
136 mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
137 if (mHeaderTextView == null) {
138 mHeaderTextView = view.findViewById(R.id.suc_layout_title);
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;
147 mImm = (InputMethodManager) getActivity().getSystemService(
148 Context.INPUT_METHOD_SERVICE);
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());
159 if (TextUtils.isEmpty(detailsMessage)) {
160 detailsMessage = getString(getDefaultDetails());
162 mHeaderTextView.setText(headerMessage);
163 mDetailsTextView.setText(detailsMessage);
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),
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());
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();
193 private int getDefaultHeader() {
195 return mIsAlpha ? R.string.lockpassword_confirm_your_password_header_frp
196 : R.string.lockpassword_confirm_your_pin_header_frp;
198 return mIsAlpha ? R.string.lockpassword_confirm_your_password_header
199 : R.string.lockpassword_confirm_your_pin_header;
202 private int getDefaultDetails() {
204 return mIsAlpha ? R.string.lockpassword_confirm_your_password_details_frp
205 : R.string.lockpassword_confirm_your_pin_details_frp;
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];
215 private int getErrorMessage() {
216 return mIsAlpha ? R.string.lockpassword_invalid_password
217 : R.string.lockpassword_invalid_pin;
221 protected int getLastTryErrorMessage(int 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;
233 throw new IllegalArgumentException("Unrecognized user type:" + userType);
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);
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);
254 result.add(mPasswordEntry);
255 result.add(mErrorTextView);
256 return result.toArray(new View[] {});
260 public void startEnterAnimation() {
261 super.startEnterAnimation();
262 mAppearAnimationUtils.startAnimation(getActiveViews(), this::updatePasswordEntry);
266 public void onPause() {
268 if (mCountdownTimer != null) {
269 mCountdownTimer.cancel();
270 mCountdownTimer = null;
272 mCredentialCheckResultTracker.setListener(null);
276 public int getMetricsCategory() {
277 return SettingsEnums.CONFIRM_LOCK_PASSWORD;
281 public void onResume() {
283 long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
285 mCredentialCheckResultTracker.clearResult();
286 handleAttemptLockout(deadline);
288 updatePasswordEntry();
289 mErrorTextView.setText("");
291 mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
293 mCredentialCheckResultTracker.setListener(this);
297 protected void authenticationSucceeded() {
298 mCredentialCheckResultTracker.setResult(true, new Intent(), 0, mEffectiveUserId);
301 private void updatePasswordEntry() {
302 final boolean isLockedOut =
303 mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId) != 0;
304 mPasswordEntry.setEnabled(!isLockedOut);
305 mPasswordEntryInputDisabler.setInputEnabled(!isLockedOut);
307 mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), 0 /*flags*/);
309 mPasswordEntry.scheduleShowSoftInput();
313 public void onWindowFocusChanged(boolean hasFocus) {
317 // Post to let window focus logic to finish to allow soft input show/hide properly.
318 mPasswordEntry.post(this::updatePasswordEntry);
321 private void handleNext() {
322 if (mPendingLockCheck != null || mDisappearing) {
326 final String pin = mPasswordEntry.getText().toString();
327 if (TextUtils.isEmpty(pin)) {
331 mPasswordEntryInputDisabler.setInputEnabled(false);
332 final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
333 ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
335 Intent intent = new Intent();
336 if (verifyChallenge) {
337 if (isInternalActivity()) {
338 startVerifyPassword(pin, intent);
342 startCheckPassword(pin, intent);
346 mCredentialCheckResultTracker.setResult(false, intent, 0, mEffectiveUserId);
349 private boolean isInternalActivity() {
350 return getActivity() instanceof ConfirmLockPassword.InternalActivity;
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() {
361 public void onVerified(byte[] token, int timeoutMs) {
362 mPendingLockCheck = null;
363 boolean matched = false;
366 if (mReturnCredentials) {
368 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
372 mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
373 localEffectiveUserId);
376 mPendingLockCheck = (localEffectiveUserId == localUserId)
377 ? LockPatternChecker.verifyPassword(
378 mLockPatternUtils, pin, challenge, localUserId, onVerifyCallback)
379 : LockPatternChecker.verifyTiedProfileChallenge(
380 mLockPatternUtils, pin, false, challenge, localUserId,
384 private void startCheckPassword(final String pin, final Intent intent) {
385 final int localEffectiveUserId = mEffectiveUserId;
386 mPendingLockCheck = LockPatternChecker.checkPassword(
389 localEffectiveUserId,
390 new LockPatternChecker.OnCheckCallback() {
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);
399 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
401 mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
402 localEffectiveUserId);
407 private void startDisappearAnimation(final Intent intent) {
411 mDisappearing = true;
413 final ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
414 // Bail if there is no active activity.
415 if (activity == null || activity.isFinishing()) {
418 if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
419 mDisappearAnimationUtils.startAnimation(getActiveViews(), () -> {
420 activity.setResult(RESULT_OK, intent);
422 activity.overridePendingTransition(
423 R.anim.confirm_credential_close_enter,
424 R.anim.confirm_credential_close_exit);
427 activity.setResult(RESULT_OK, intent);
432 private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs,
433 int effectiveUserId, boolean newResult) {
434 mPasswordEntryInputDisabler.setInputEnabled(true);
437 ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
438 mUserManager, mEffectiveUserId);
440 mBiometricManager.onConfirmDeviceCredentialSuccess();
441 startDisappearAnimation(intent);
442 ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
446 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
447 effectiveUserId, timeoutMs);
448 handleAttemptLockout(deadline);
450 showError(getErrorMessage(), CLEAR_WRONG_ATTEMPT_TIMEOUT_MS);
453 reportFailedAttempt();
459 public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
460 int effectiveUserId, boolean newResult) {
461 onPasswordChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
465 protected void onShowError() {
466 mPasswordEntry.setText(null);
469 private void handleAttemptLockout(long elapsedRealtimeDeadline) {
470 mCountdownTimer = new CountDownTimer(
471 elapsedRealtimeDeadline - SystemClock.elapsedRealtime(),
472 LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
475 public void onTick(long millisUntilFinished) {
476 final int secondsCountdown = (int) (millisUntilFinished / 1000);
478 R.string.lockpattern_too_many_failed_confirmation_attempts,
479 secondsCountdown), 0);
483 public void onFinish() {
484 updatePasswordEntry();
485 mErrorTextView.setText("");
487 mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
490 updatePasswordEntry();
493 public void onClick(View v) {
495 case R.id.next_button:
499 case R.id.cancel_button:
500 getActivity().setResult(RESULT_CANCELED);
501 getActivity().finish();
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) {