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 static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PASSWORD_HEADER;
20 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PIN_HEADER;
21 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE;
22 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE;
23 import static android.app.admin.DevicePolicyResources.UNDEFINED;
24 
25 import static com.android.settings.biometrics.GatekeeperPasswordProvider.containsGatekeeperPasswordHandle;
26 import static com.android.settings.biometrics.GatekeeperPasswordProvider.getGatekeeperPasswordHandle;
27 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
28 
29 import android.app.KeyguardManager;
30 import android.app.RemoteLockscreenValidationResult;
31 import android.app.admin.DevicePolicyManager;
32 import android.app.settings.SettingsEnums;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.graphics.Typeface;
36 import android.os.AsyncTask;
37 import android.os.Bundle;
38 import android.os.CountDownTimer;
39 import android.os.Handler;
40 import android.os.Looper;
41 import android.os.SystemClock;
42 import android.os.UserManager;
43 import android.text.Editable;
44 import android.text.InputType;
45 import android.text.TextUtils;
46 import android.util.Log;
47 import android.view.KeyEvent;
48 import android.view.LayoutInflater;
49 import android.view.View;
50 import android.view.View.OnClickListener;
51 import android.view.ViewGroup;
52 import android.view.animation.AnimationUtils;
53 import android.view.inputmethod.EditorInfo;
54 import android.view.inputmethod.InputMethodManager;
55 import android.widget.ImeAwareEditText;
56 import android.widget.TextView;
57 import android.widget.TextView.OnEditorActionListener;
58 
59 import androidx.annotation.Nullable;
60 import androidx.fragment.app.Fragment;
61 
62 import com.android.internal.widget.LockPatternChecker;
63 import com.android.internal.widget.LockPatternUtils;
64 import com.android.internal.widget.LockscreenCredential;
65 import com.android.internal.widget.TextViewInputDisabler;
66 import com.android.settings.R;
67 import com.android.settingslib.animation.AppearAnimationUtils;
68 import com.android.settingslib.animation.DisappearAnimationUtils;
69 
70 import java.util.ArrayList;
71 
72 public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
73 
74     // The index of the array is isStrongAuth << 1 + isAlpha.
75     private static final int[] DETAIL_TEXTS = new int[] {
76         R.string.lockpassword_confirm_your_pin_generic,
77         R.string.lockpassword_confirm_your_password_generic,
78         R.string.lockpassword_strong_auth_required_device_pin,
79         R.string.lockpassword_strong_auth_required_device_password,
80     };
81 
82     public static class InternalActivity extends ConfirmLockPassword {
83     }
84 
85     @Override
getIntent()86     public Intent getIntent() {
87         Intent modIntent = new Intent(super.getIntent());
88         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
89         return modIntent;
90     }
91 
92     @Override
isValidFragment(String fragmentName)93     protected boolean isValidFragment(String fragmentName) {
94         if (ConfirmLockPasswordFragment.class.getName().equals(fragmentName)) return true;
95         return false;
96     }
97 
98     @Override
onWindowFocusChanged(boolean hasFocus)99     public void onWindowFocusChanged(boolean hasFocus) {
100         super.onWindowFocusChanged(hasFocus);
101         Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_content);
102         if (fragment != null && fragment instanceof ConfirmLockPasswordFragment) {
103             ((ConfirmLockPasswordFragment) fragment).onWindowFocusChanged(hasFocus);
104         }
105     }
106 
107     public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
108             implements OnClickListener, OnEditorActionListener,
109             CredentialCheckResultTracker.Listener, SaveAndFinishWorker.Listener,
110             RemoteLockscreenValidationFragment.Listener {
111         private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
112         private ImeAwareEditText mPasswordEntry;
113         private TextViewInputDisabler mPasswordEntryInputDisabler;
114         private AsyncTask<?, ?, ?> mPendingLockCheck;
115         private CredentialCheckResultTracker mCredentialCheckResultTracker;
116         private boolean mDisappearing = false;
117         private CountDownTimer mCountdownTimer;
118         private boolean mIsAlpha;
119         private InputMethodManager mImm;
120         private AppearAnimationUtils mAppearAnimationUtils;
121         private DisappearAnimationUtils mDisappearAnimationUtils;
122         private boolean mIsManagedProfile;
123         private CharSequence mCheckBoxLabel;
124 
125         // required constructor for fragments
ConfirmLockPasswordFragment()126         public ConfirmLockPasswordFragment() {
127 
128         }
129 
130         @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)131         public View onCreateView(LayoutInflater inflater, ViewGroup container,
132                 Bundle savedInstanceState) {
133             final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
134                     mEffectiveUserId);
135 
136             ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
137             View view = inflater.inflate(
138                     activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.NORMAL
139                             ? R.layout.confirm_lock_password_normal
140                             : R.layout.confirm_lock_password,
141                     container,
142                     false);
143             mGlifLayout = view.findViewById(R.id.setup_wizard_layout);
144             mPasswordEntry = (ImeAwareEditText) view.findViewById(R.id.password_entry);
145             mPasswordEntry.setOnEditorActionListener(this);
146             // EditText inside ScrollView doesn't automatically get focus.
147             mPasswordEntry.requestFocus();
148             mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
149             mErrorTextView = (TextView) view.findViewById(R.id.errorText);
150 
151             if (mRemoteValidation) {
152                 mIsAlpha = mRemoteLockscreenValidationSession.getLockType()
153                         == KeyguardManager.PASSWORD;
154                 // ProgressBar visibility is set to GONE until interacted with.
155                 // Set progress bar to INVISIBLE, so the EditText does not get bumped down later.
156                 mGlifLayout.setProgressBarShown(false);
157             } else {
158                 mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
159                         || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
160                         || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
161                         || DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
162             }
163             mImm = (InputMethodManager) getActivity().getSystemService(
164                     Context.INPUT_METHOD_SERVICE);
165 
166             mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
167 
168             Intent intent = getActivity().getIntent();
169             if (intent != null) {
170                 CharSequence headerMessage = intent.getCharSequenceExtra(
171                         ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
172                 CharSequence detailsMessage = intent.getCharSequenceExtra(
173                         ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
174                 if (TextUtils.isEmpty(headerMessage) && mIsManagedProfile) {
175                     headerMessage = mDevicePolicyManager.getOrganizationNameForUser(mUserId);
176                 }
177                 if (TextUtils.isEmpty(headerMessage)) {
178                     headerMessage = getDefaultHeader();
179                 }
180                 if (TextUtils.isEmpty(detailsMessage)) {
181                     detailsMessage = getDefaultDetails();
182                 }
183                 mGlifLayout.setHeaderText(headerMessage);
184 
185                 if (mIsManagedProfile) {
186                     mGlifLayout.getDescriptionTextView().setVisibility(View.GONE);
187                 } else {
188                     mGlifLayout.setDescriptionText(detailsMessage);
189                 }
190                 mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
191             }
192             int currentType = mPasswordEntry.getInputType();
193             if (mIsAlpha) {
194                 mPasswordEntry.setInputType(currentType);
195                 mPasswordEntry.setContentDescription(
196                         getContext().getString(R.string.unlock_set_unlock_password_title));
197             } else {
198                 mPasswordEntry.setInputType(
199                         InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
200                 mPasswordEntry.setContentDescription(
201                         getContext().getString(R.string.unlock_set_unlock_pin_title));
202             }
203             // Can't set via XML since setInputType resets the fontFamily to null
204             mPasswordEntry.setTypeface(Typeface.create(
205                     getContext().getString(com.android.internal.R.string.config_headlineFontFamily),
206                     Typeface.NORMAL));
207             mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
208                     220, 2f /* translationScale */, 1f /* delayScale*/,
209                     AnimationUtils.loadInterpolator(getContext(),
210                             android.R.interpolator.linear_out_slow_in));
211             mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
212                     110, 1f /* translationScale */,
213                     0.5f /* delayScale */, AnimationUtils.loadInterpolator(
214                             getContext(), android.R.interpolator.fast_out_linear_in));
215             setAccessibilityTitle(mGlifLayout.getHeaderText());
216 
217             mCredentialCheckResultTracker = (CredentialCheckResultTracker) getFragmentManager()
218                     .findFragmentByTag(FRAGMENT_TAG_CHECK_LOCK_RESULT);
219             if (mCredentialCheckResultTracker == null) {
220                 mCredentialCheckResultTracker = new CredentialCheckResultTracker();
221                 getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
222                         FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
223             }
224 
225             return view;
226         }
227 
228         @Override
onViewCreated(View view, @Nullable Bundle savedInstanceState)229         public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
230             super.onViewCreated(view, savedInstanceState);
231             if (mRemoteValidation) {
232                 if (mCheckBox != null) {
233                     mCheckBox.setText(TextUtils.isEmpty(mCheckBoxLabel)
234                             ? getDefaultCheckboxLabel()
235                             : mCheckBoxLabel);
236                 }
237                 if (mCancelButton != null && TextUtils.isEmpty(mAlternateButtonText)) {
238                     mCancelButton.setText(mIsAlpha
239                             ? R.string.lockpassword_forgot_password
240                             : R.string.lockpassword_forgot_pin);
241                 }
242                 updateRemoteLockscreenValidationViews();
243             }
244 
245             if (mForgotButton != null) {
246                 mForgotButton.setText(mIsAlpha
247                         ? R.string.lockpassword_forgot_password
248                         : R.string.lockpassword_forgot_pin);
249             }
250         }
251 
252         @Override
onDestroy()253         public void onDestroy() {
254             super.onDestroy();
255             if (mPasswordEntry != null) {
256                 mPasswordEntry.setText(null);
257             }
258             // Force a garbage collection to remove remnant of user password shards from memory.
259             // Execute this with a slight delay to allow the activity lifecycle to complete and
260             // the instance to become gc-able.
261             new Handler(Looper.myLooper()).postDelayed(() -> {
262                 System.gc();
263                 System.runFinalization();
264                 System.gc();
265             }, 5000);
266         }
267 
getDefaultHeader()268         private String getDefaultHeader() {
269             if (mFrp) {
270                 return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_header_frp)
271                         : getString(R.string.lockpassword_confirm_your_pin_header_frp);
272             }
273             if (mRepairMode) {
274                 return mIsAlpha
275                         ? getString(R.string.lockpassword_confirm_repair_mode_password_header)
276                         : getString(R.string.lockpassword_confirm_repair_mode_pin_header);
277             }
278             if (mRemoteValidation) {
279                 return getString(R.string.lockpassword_remote_validation_header);
280             }
281             if (mIsManagedProfile) {
282                 if (mIsAlpha) {
283                     return mDevicePolicyManager.getResources().getString(
284                             CONFIRM_WORK_PROFILE_PASSWORD_HEADER,
285                             () -> getString(
286                                     R.string.lockpassword_confirm_your_work_password_header));
287                 }
288                 return mDevicePolicyManager.getResources().getString(
289                         CONFIRM_WORK_PROFILE_PIN_HEADER,
290                         () -> getString(R.string.lockpassword_confirm_your_work_pin_header));
291             }
292             return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_header)
293                     : getString(R.string.lockpassword_confirm_your_pin_header);
294         }
295 
getDefaultDetails()296         private String getDefaultDetails() {
297             if (mFrp) {
298                 return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_details_frp)
299                         : getString(R.string.lockpassword_confirm_your_pin_details_frp);
300             }
301             if (mRepairMode) {
302                 return mIsAlpha
303                         ? getString(R.string.lockpassword_confirm_repair_mode_password_details)
304                         : getString(R.string.lockpassword_confirm_repair_mode_pin_details);
305             }
306             if (mRemoteValidation) {
307                 return getContext().getString(mIsAlpha
308                         ? R.string.lockpassword_remote_validation_password_details
309                         : R.string.lockpassword_remote_validation_pin_details);
310             }
311             boolean isStrongAuthRequired = isStrongAuthRequired();
312             // Map boolean flags to an index by isStrongAuth << 1 + isAlpha.
313             int index = ((isStrongAuthRequired ? 1 : 0) << 1) + (mIsAlpha ? 1 : 0);
314             return getString(DETAIL_TEXTS[index]);
315         }
316 
getDefaultCheckboxLabel()317         private String getDefaultCheckboxLabel() {
318             if (mRemoteValidation) {
319                 return getString(mIsAlpha
320                         ? R.string.lockpassword_remote_validation_set_password_as_screenlock
321                         : R.string.lockpassword_remote_validation_set_pin_as_screenlock);
322             }
323             throw new IllegalStateException(
324                     "Trying to get default checkbox label for illegal flow");
325         }
326 
getErrorMessage()327         private int getErrorMessage() {
328             return mIsAlpha ? R.string.lockpassword_invalid_password
329                     : R.string.lockpassword_invalid_pin;
330         }
331 
332         @Override
getLastTryOverrideErrorMessageId(int userType)333         protected String getLastTryOverrideErrorMessageId(int userType) {
334             if (userType == USER_TYPE_MANAGED_PROFILE) {
335                 return mIsAlpha ?  WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE
336                         : WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE;
337             }
338 
339             return UNDEFINED;
340         }
341 
342         @Override
getLastTryDefaultErrorMessage(int userType)343         protected int getLastTryDefaultErrorMessage(int userType) {
344             switch (userType) {
345                 case USER_TYPE_PRIMARY:
346                     return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_device
347                             : R.string.lock_last_pin_attempt_before_wipe_device;
348                 case USER_TYPE_MANAGED_PROFILE:
349                     return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_profile
350                             : R.string.lock_last_pin_attempt_before_wipe_profile;
351                 case USER_TYPE_SECONDARY:
352                     return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_user
353                             : R.string.lock_last_pin_attempt_before_wipe_user;
354                 default:
355                     throw new IllegalArgumentException("Unrecognized user type:" + userType);
356             }
357         }
358 
359         @Override
prepareEnterAnimation()360         public void prepareEnterAnimation() {
361             super.prepareEnterAnimation();
362             mGlifLayout.getHeaderTextView().setAlpha(0f);
363             mGlifLayout.getDescriptionTextView().setAlpha(0f);
364             mCancelButton.setAlpha(0f);
365             if (mForgotButton != null) {
366                 mForgotButton.setAlpha(0f);
367             }
368             mPasswordEntry.setAlpha(0f);
369             mErrorTextView.setAlpha(0f);
370         }
371 
getActiveViews()372         private View[] getActiveViews() {
373             ArrayList<View> result = new ArrayList<>();
374             result.add(mGlifLayout.getHeaderTextView());
375             result.add(mGlifLayout.getDescriptionTextView());
376             if (mCancelButton.getVisibility() == View.VISIBLE) {
377                 result.add(mCancelButton);
378             }
379             if (mForgotButton != null) {
380                 result.add(mForgotButton);
381             }
382             result.add(mPasswordEntry);
383             result.add(mErrorTextView);
384             return result.toArray(new View[] {});
385         }
386 
387         @Override
startEnterAnimation()388         public void startEnterAnimation() {
389             super.startEnterAnimation();
390             mAppearAnimationUtils.startAnimation(getActiveViews(), this::updatePasswordEntry);
391         }
392 
393         @Override
onPause()394         public void onPause() {
395             super.onPause();
396             if (mCountdownTimer != null) {
397                 mCountdownTimer.cancel();
398                 mCountdownTimer = null;
399             }
400             mCredentialCheckResultTracker.setListener(null);
401             if (mRemoteLockscreenValidationFragment != null) {
402                 mRemoteLockscreenValidationFragment.setListener(null, /* handler= */ null);
403             }
404         }
405 
406         @Override
getMetricsCategory()407         public int getMetricsCategory() {
408             return SettingsEnums.CONFIRM_LOCK_PASSWORD;
409         }
410 
411         @Override
onResume()412         public void onResume() {
413             super.onResume();
414             long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
415             if (deadline != 0) {
416                 mCredentialCheckResultTracker.clearResult();
417                 handleAttemptLockout(deadline);
418             } else {
419                 updatePasswordEntry();
420                 mErrorTextView.setText("");
421                 updateErrorMessage(
422                         mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
423             }
424             mCredentialCheckResultTracker.setListener(this);
425             if (mRemoteLockscreenValidationFragment != null) {
426                 mRemoteLockscreenValidationFragment.setListener(this, mHandler);
427             }
428         }
429 
430         @Override
authenticationSucceeded()431         protected void authenticationSucceeded() {
432             mCredentialCheckResultTracker.setResult(true, new Intent(), 0, mEffectiveUserId);
433         }
434 
updatePasswordEntry()435         private void updatePasswordEntry() {
436             final boolean isLockedOut =
437                     mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId) != 0;
438             final boolean isRemoteLockscreenValidationInProgress =
439                     mRemoteLockscreenValidationFragment != null
440                     && mRemoteLockscreenValidationFragment.isRemoteValidationInProgress();
441             boolean shouldEnableInput = !isLockedOut && !isRemoteLockscreenValidationInProgress;
442             mPasswordEntry.setEnabled(shouldEnableInput);
443             mPasswordEntryInputDisabler.setInputEnabled(shouldEnableInput);
444             if (shouldEnableInput) {
445                 mPasswordEntry.scheduleShowSoftInput();
446                 mPasswordEntry.requestFocus();
447             } else {
448                 mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), /* flags= */0);
449             }
450         }
451 
onWindowFocusChanged(boolean hasFocus)452         public void onWindowFocusChanged(boolean hasFocus) {
453             if (!hasFocus) {
454                 return;
455             }
456             // Post to let window focus logic to finish to allow soft input show/hide properly.
457             mPasswordEntry.post(this::updatePasswordEntry);
458         }
459 
handleNext()460         private void handleNext() {
461             if (mPendingLockCheck != null || mDisappearing) {
462                 return;
463             }
464 
465             // TODO(b/120484642): This is a point of entry for passwords from the UI
466             final Editable passwordText = mPasswordEntry.getText();
467             if (TextUtils.isEmpty(passwordText)) {
468                 return;
469             }
470             final LockscreenCredential credential = mIsAlpha
471                     ? LockscreenCredential.createPassword(passwordText)
472                     : LockscreenCredential.createPin(passwordText);
473 
474             mPasswordEntryInputDisabler.setInputEnabled(false);
475 
476             if (mRemoteValidation) {
477                 validateGuess(credential);
478                 updateRemoteLockscreenValidationViews();
479                 updatePasswordEntry();
480                 return;
481             }
482 
483             Intent intent = new Intent();
484             // TODO(b/161956762): Sanitize this
485             if (mReturnGatekeeperPassword) {
486                 if (isInternalActivity()) {
487                     startVerifyPassword(credential, intent,
488                             LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE);
489                     return;
490                 }
491             } else if (mForceVerifyPath)  {
492                 if (isInternalActivity()) {
493                     final int flags = mRequestWriteRepairModePassword
494                             ? LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW : 0;
495                     startVerifyPassword(credential, intent, flags);
496                     return;
497                 }
498             } else {
499                 startCheckPassword(credential, intent);
500                 return;
501             }
502 
503             mCredentialCheckResultTracker.setResult(false, intent, 0, mEffectiveUserId);
504         }
505 
isInternalActivity()506         private boolean isInternalActivity() {
507             return getActivity() instanceof ConfirmLockPassword.InternalActivity;
508         }
509 
startVerifyPassword(LockscreenCredential credential, final Intent intent, @LockPatternUtils.VerifyFlag int flags)510         private void startVerifyPassword(LockscreenCredential credential, final Intent intent,
511                 @LockPatternUtils.VerifyFlag int flags) {
512             final int localEffectiveUserId = mEffectiveUserId;
513             final int localUserId = mUserId;
514             final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
515                 mPendingLockCheck = null;
516                 final boolean matched = response.isMatched();
517                 if (matched && mReturnCredentials) {
518                     if ((flags & LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0) {
519                         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
520                                 response.getGatekeeperPasswordHandle());
521                     } else {
522                         intent.putExtra(
523                                 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
524                                 response.getGatekeeperHAT());
525                     }
526                 }
527                 mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
528                         localEffectiveUserId);
529             };
530             mPendingLockCheck = (localEffectiveUserId == localUserId)
531                     ? LockPatternChecker.verifyCredential(mLockPatternUtils, credential,
532                             localUserId, flags, onVerifyCallback)
533                     : LockPatternChecker.verifyTiedProfileChallenge(mLockPatternUtils, credential,
534                             localUserId, flags, onVerifyCallback);
535         }
536 
startCheckPassword(final LockscreenCredential credential, final Intent intent)537         private void startCheckPassword(final LockscreenCredential credential,
538                 final Intent intent) {
539             final int localEffectiveUserId = mEffectiveUserId;
540             mPendingLockCheck = LockPatternChecker.checkCredential(
541                     mLockPatternUtils,
542                     credential,
543                     localEffectiveUserId,
544                     new LockPatternChecker.OnCheckCallback() {
545                         @Override
546                         public void onChecked(boolean matched, int timeoutMs) {
547                             mPendingLockCheck = null;
548                             if (matched && isInternalActivity() && mReturnCredentials) {
549                                 intent.putExtra(
550                                         ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, credential);
551                             }
552                             mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
553                                     localEffectiveUserId);
554                         }
555                     });
556         }
557 
startDisappearAnimation(final Intent intent)558         private void startDisappearAnimation(final Intent intent) {
559             ConfirmDeviceCredentialUtils.hideImeImmediately(
560                     getActivity().getWindow().getDecorView());
561 
562             if (mDisappearing) {
563                 return;
564             }
565             mDisappearing = true;
566 
567             final ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
568             // Bail if there is no active activity.
569             if (activity == null || activity.isFinishing()) {
570                 return;
571             }
572             if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
573                 mDisappearAnimationUtils.startAnimation(getActiveViews(), () -> {
574                     activity.setResult(RESULT_OK, intent);
575                     activity.finish();
576                     activity.overridePendingTransition(
577                             R.anim.confirm_credential_close_enter,
578                             R.anim.confirm_credential_close_exit);
579                 });
580             } else {
581                 activity.setResult(RESULT_OK, intent);
582                 activity.finish();
583             }
584         }
585 
onPasswordChecked(boolean matched, Intent intent, int timeoutMs, int effectiveUserId, boolean newResult)586         private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs,
587                 int effectiveUserId, boolean newResult) {
588             mPasswordEntryInputDisabler.setInputEnabled(true);
589             if (matched) {
590                 if (newResult) {
591                     ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
592                             mUserManager, mDevicePolicyManager, mEffectiveUserId,
593                             /* isStrongAuth */ true);
594                 }
595                 startDisappearAnimation(intent);
596                 ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
597             } else {
598                 if (timeoutMs > 0) {
599                     refreshLockScreen();
600                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
601                             effectiveUserId, timeoutMs);
602                     handleAttemptLockout(deadline);
603                 } else {
604                     showError(getErrorMessage(), CLEAR_WRONG_ATTEMPT_TIMEOUT_MS);
605                 }
606                 if (newResult) {
607                     reportFailedAttempt();
608                 }
609             }
610         }
611 
612         @Override
onRemoteLockscreenValidationResult( RemoteLockscreenValidationResult result)613         public void onRemoteLockscreenValidationResult(
614                 RemoteLockscreenValidationResult result) {
615             switch (result.getResultCode()) {
616                 case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
617                     if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
618                             .getLockscreenCredential() != null) {
619                         Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
620                         SaveAndFinishWorker saveAndFinishWorker = new SaveAndFinishWorker();
621                         getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
622                                 .commit();
623                         getFragmentManager().executePendingTransactions();
624                         saveAndFinishWorker
625                                 .setListener(this)
626                                 .setRequestGatekeeperPasswordHandle(true);
627                         saveAndFinishWorker.start(
628                                 mLockPatternUtils,
629                                 mRemoteLockscreenValidationFragment.getLockscreenCredential(),
630                                 /* currentCredential= */ null,
631                                 mEffectiveUserId);
632                     } else {
633                         mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
634                                 /* timeoutMs= */ 0, mEffectiveUserId);
635                     }
636                     return;
637                 case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
638                     mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
639                             /* timeoutMs= */ 0, mEffectiveUserId);
640                     break;
641                 case RemoteLockscreenValidationResult.RESULT_LOCKOUT:
642                     mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
643                             (int) result.getTimeoutMillis(), mEffectiveUserId);
644                     break;
645                 case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
646                 case RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED:
647                     onRemoteLockscreenValidationFailure(String.format(
648                             "Cannot continue remote lockscreen validation. ResultCode=%d",
649                             result.getResultCode()));
650                     break;
651             }
652             updateRemoteLockscreenValidationViews();
653             updatePasswordEntry();
654             mRemoteLockscreenValidationFragment.clearLockscreenCredential();
655         }
656 
657         @Override
onCredentialChecked(boolean matched, Intent intent, int timeoutMs, int effectiveUserId, boolean newResult)658         public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
659                 int effectiveUserId, boolean newResult) {
660             onPasswordChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
661         }
662 
663         @Override
onShowError()664         protected void onShowError() {
665             mPasswordEntry.setText(null);
666         }
667 
handleAttemptLockout(long elapsedRealtimeDeadline)668         private void handleAttemptLockout(long elapsedRealtimeDeadline) {
669             clearResetErrorRunnable();
670             mCountdownTimer = new CountDownTimer(
671                     elapsedRealtimeDeadline - SystemClock.elapsedRealtime(),
672                     LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
673 
674                 @Override
675                 public void onTick(long millisUntilFinished) {
676                     final int secondsCountdown = (int) (millisUntilFinished / 1000);
677                     showError(getString(
678                             R.string.lockpattern_too_many_failed_confirmation_attempts,
679                             secondsCountdown), 0);
680                 }
681 
682                 @Override
683                 public void onFinish() {
684                     updatePasswordEntry();
685                     mErrorTextView.setText("");
686                     updateErrorMessage(
687                             mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
688                 }
689             }.start();
690             updatePasswordEntry();
691         }
692 
onClick(View v)693         public void onClick(View v) {
694             if (v.getId() == R.id.next_button) {
695                 handleNext();
696             } else if (v.getId() == R.id.cancel_button) {
697                 getActivity().setResult(RESULT_CANCELED);
698                 getActivity().finish();
699             }
700         }
701 
702         // {@link OnEditorActionListener} methods.
onEditorAction(TextView v, int actionId, KeyEvent event)703         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
704             // Check if this was the result of hitting the enter or "done" key
705             if (actionId == EditorInfo.IME_NULL
706                     || actionId == EditorInfo.IME_ACTION_DONE
707                     || actionId == EditorInfo.IME_ACTION_NEXT) {
708                 handleNext();
709                 return true;
710             }
711             return false;
712         }
713 
714         /**
715          * Callback for when the current device's lockscreen was set to the guess used for
716          * remote lockscreen validation.
717          */
718         @Override
onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData)719         public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
720             Log.i(TAG, "Device lockscreen has been set to remote device's lockscreen.");
721             mRemoteLockscreenValidationFragment.clearLockscreenCredential();
722 
723             Intent result = new Intent();
724             if (mRemoteValidation && containsGatekeeperPasswordHandle(resultData)) {
725                 result.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGatekeeperPasswordHandle(resultData));
726             }
727             mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
728                     /* timeoutMs= */ 0, mEffectiveUserId);
729         }
730     }
731 }
732