• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.password;
18 
19 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PATTERN_HEADER;
20 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE;
21 import static android.app.admin.DevicePolicyResources.UNDEFINED;
22 
23 import static com.android.settings.biometrics.GatekeeperPasswordProvider.containsGatekeeperPasswordHandle;
24 import static com.android.settings.biometrics.GatekeeperPasswordProvider.getGatekeeperPasswordHandle;
25 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
26 
27 import android.annotation.SuppressLint;
28 import android.app.Activity;
29 import android.app.KeyguardManager;
30 import android.app.RemoteLockscreenValidationResult;
31 import android.app.settings.SettingsEnums;
32 import android.content.Intent;
33 import android.os.AsyncTask;
34 import android.os.Bundle;
35 import android.os.CountDownTimer;
36 import android.os.SystemClock;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.text.TextUtils;
40 import android.util.Log;
41 import android.view.LayoutInflater;
42 import android.view.MotionEvent;
43 import android.view.View;
44 import android.view.ViewGroup;
45 import android.view.animation.AnimationUtils;
46 import android.view.animation.Interpolator;
47 import android.widget.TextView;
48 
49 import androidx.annotation.Nullable;
50 
51 import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
52 import com.android.internal.widget.LockPatternChecker;
53 import com.android.internal.widget.LockPatternUtils;
54 import com.android.internal.widget.LockPatternView;
55 import com.android.internal.widget.LockPatternView.Cell;
56 import com.android.internal.widget.LockscreenCredential;
57 import com.android.settings.R;
58 import com.android.settings.SetupRedactionInterstitial;
59 import com.android.settings.Utils;
60 import com.android.settingslib.animation.AppearAnimationCreator;
61 import com.android.settingslib.animation.AppearAnimationUtils;
62 import com.android.settingslib.animation.DisappearAnimationUtils;
63 
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.List;
67 
68 /**
69  * Launch this when you want the user to confirm their lock pattern.
70  *
71  * Sets an activity result of {@link Activity#RESULT_OK} when the user
72  * successfully confirmed their pattern.
73  */
74 public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
75 
76     public static class InternalActivity extends ConfirmLockPattern {
77     }
78 
79     private enum Stage {
80         NeedToUnlock,
81         NeedToUnlockWrong,
82         LockedOut
83     }
84 
85     @Override
getIntent()86     public Intent getIntent() {
87         Intent modIntent = new Intent(super.getIntent());
88         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPatternFragment.class.getName());
89         return modIntent;
90     }
91 
92     @Override
isValidFragment(String fragmentName)93     protected boolean isValidFragment(String fragmentName) {
94         if (ConfirmLockPatternFragment.class.getName().equals(fragmentName)) return true;
95         return false;
96     }
97 
98     public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
99             implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener,
100             SaveAndFinishWorker.Listener, RemoteLockscreenValidationFragment.Listener {
101 
102         private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
103 
104         private LockPatternView mLockPatternView;
105         private AsyncTask<?, ?, ?> mPendingLockCheck;
106         private CredentialCheckResultTracker mCredentialCheckResultTracker;
107         private boolean mDisappearing = false;
108         private CountDownTimer mCountdownTimer;
109 
110         private View mSudContent;
111 
112         // caller-supplied text for various prompts
113         private CharSequence mHeaderText;
114         private CharSequence mDetailsText;
115         private CharSequence mCheckBoxLabel;
116 
117         private AppearAnimationUtils mAppearAnimationUtils;
118         private DisappearAnimationUtils mDisappearAnimationUtils;
119 
120         private boolean mIsManagedProfile;
121 
122         // required constructor for fragments
ConfirmLockPatternFragment()123         public ConfirmLockPatternFragment() {
124 
125         }
126 
127         @SuppressLint("ClickableViewAccessibility")
128         @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)129         public View onCreateView(LayoutInflater inflater, ViewGroup container,
130                 Bundle savedInstanceState) {
131             ConfirmLockPattern activity = (ConfirmLockPattern) getActivity();
132             View view = inflater.inflate(
133                     activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.NORMAL
134                             ? R.layout.confirm_lock_pattern_normal
135                             : R.layout.confirm_lock_pattern,
136                     container,
137                     false);
138             mGlifLayout = view.findViewById(R.id.setup_wizard_layout);
139             mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
140             mErrorTextView = (TextView) view.findViewById(R.id.errorText);
141             // TODO(b/243008023) Workaround for Glif layout on 2 panel choose lock settings.
142             mSudContent = mGlifLayout.findViewById(
143                     com.google.android.setupdesign.R.id.sud_layout_content);
144             mSudContent.setPadding(mSudContent.getPaddingLeft(), 0, mSudContent.getPaddingRight(),
145                     0);
146             mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
147 
148             // make it so unhandled touch events within the unlock screen go to the
149             // lock pattern view.
150             final LinearLayoutWithDefaultTouchRecepient topLayout
151                     = (LinearLayoutWithDefaultTouchRecepient) view.findViewById(R.id.topLayout);
152             topLayout.setDefaultTouchRecepient(mLockPatternView);
153 
154             Intent intent = getActivity().getIntent();
155             if (intent != null) {
156                 mHeaderText = intent.getCharSequenceExtra(
157                         ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
158                 mDetailsText = intent.getCharSequenceExtra(
159                         ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
160                 mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
161             }
162             if (TextUtils.isEmpty(mHeaderText) && mIsManagedProfile) {
163                 mHeaderText = mDevicePolicyManager.getOrganizationNameForUser(mUserId);
164             }
165 
166             mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
167                     mEffectiveUserId));
168             mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
169             mLockPatternView.setOnTouchListener((v, event) -> {
170                 if (event.getAction() == MotionEvent.ACTION_DOWN) {
171                     v.getParent().requestDisallowInterceptTouchEvent(true);
172                 }
173                 return false;
174             });
175             updateStage(Stage.NeedToUnlock);
176 
177             if (savedInstanceState == null) {
178                 // on first launch, if no lock pattern is set, then finish with
179                 // success (don't want user to get stuck confirming something that
180                 // doesn't exist).
181                 // Don't do this check for FRP though, because the pattern is not stored
182                 // in a way that isLockPatternEnabled is aware of for that case.
183                 // TODO(roosa): This block should no longer be needed since we removed the
184                 //              ability to disable the pattern in L. Remove this block after
185                 //              ensuring it's safe to do so. (Note that ConfirmLockPassword
186                 //              doesn't have this).
187                 if (!mFrp && !mRemoteValidation && !mRepairMode
188                         && !mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
189                     getActivity().setResult(Activity.RESULT_OK);
190                     getActivity().finish();
191                 }
192             }
193             mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
194                     AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 2f /* translationScale */,
195                     1.3f /* delayScale */, AnimationUtils.loadInterpolator(
196                     getContext(), android.R.interpolator.linear_out_slow_in));
197             mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
198                     125, 4f /* translationScale */,
199                     0.3f /* delayScale */, AnimationUtils.loadInterpolator(
200                     getContext(), android.R.interpolator.fast_out_linear_in),
201                     new AppearAnimationUtils.RowTranslationScaler() {
202                         @Override
203                         public float getRowTranslationScale(int row, int numRows) {
204                             return (float)(numRows - row) / numRows;
205                         }
206                     });
207             setAccessibilityTitle(mGlifLayout.getHeaderText());
208 
209             mCredentialCheckResultTracker = (CredentialCheckResultTracker) getFragmentManager()
210                     .findFragmentByTag(FRAGMENT_TAG_CHECK_LOCK_RESULT);
211             if (mCredentialCheckResultTracker == null) {
212                 mCredentialCheckResultTracker = new CredentialCheckResultTracker();
213                 getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
214                         FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
215             }
216 
217             if (mRemoteValidation) {
218                 // ProgressBar visibility is set to GONE until interacted with.
219                 // Set progress bar to INVISIBLE, so the pattern does not get bumped down later.
220                 mGlifLayout.setProgressBarShown(false);
221                 // Lock pattern is generally not visible until the user has set a lockscreen for the
222                 // first time. For a new user, this means that the pattern will always be hidden.
223                 // Despite this prerequisite, we want to show the pattern anyway for this flow.
224                 mLockPatternView.setInStealthMode(false);
225             }
226 
227             return view;
228         }
229 
230         @Override
onViewCreated(View view, @Nullable Bundle savedInstanceState)231         public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
232             super.onViewCreated(view, savedInstanceState);
233             if (mRemoteValidation) {
234                 if (mCheckBox != null) {
235                     mCheckBox.setText(TextUtils.isEmpty(mCheckBoxLabel)
236                             ? getDefaultCheckboxLabel()
237                             : mCheckBoxLabel);
238                 }
239                 if (mCancelButton != null && TextUtils.isEmpty(mAlternateButtonText)) {
240                     mCancelButton.setText(R.string.lockpassword_forgot_pattern);
241                 }
242                 updateRemoteLockscreenValidationViews();
243             }
244 
245             if (mForgotButton != null) {
246                 mForgotButton.setText(R.string.lockpassword_forgot_pattern);
247             }
248         }
249 
250         @Override
onSaveInstanceState(Bundle outState)251         public void onSaveInstanceState(Bundle outState) {
252             // deliberately not calling super since we are managing this in full
253         }
254 
255         @Override
onPause()256         public void onPause() {
257             super.onPause();
258 
259             if (mCountdownTimer != null) {
260                 mCountdownTimer.cancel();
261             }
262             mCredentialCheckResultTracker.setListener(null);
263             if (mRemoteLockscreenValidationFragment != null) {
264                 mRemoteLockscreenValidationFragment.setListener(null, /* handler= */ null);
265             }
266         }
267 
268         @Override
getMetricsCategory()269         public int getMetricsCategory() {
270             return SettingsEnums.CONFIRM_LOCK_PATTERN;
271         }
272 
273         @Override
onResume()274         public void onResume() {
275             super.onResume();
276 
277             // if the user is currently locked out, enforce it.
278             long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
279             if (deadline != 0) {
280                 mCredentialCheckResultTracker.clearResult();
281                 handleAttemptLockout(deadline);
282             } else if (!mLockPatternView.isEnabled()) {
283                 // The deadline has passed, but the timer was cancelled. Or the pending lock
284                 // check was cancelled. Need to clean up.
285                 updateStage(Stage.NeedToUnlock);
286             }
287             mCredentialCheckResultTracker.setListener(this);
288             if (mRemoteLockscreenValidationFragment != null) {
289                 mRemoteLockscreenValidationFragment.setListener(this, mHandler);
290                 if (mRemoteLockscreenValidationFragment.isRemoteValidationInProgress()) {
291                     mLockPatternView.setEnabled(false);
292                 }
293             }
294         }
295 
296         @Override
onShowError()297         protected void onShowError() {
298         }
299 
300         @Override
prepareEnterAnimation()301         public void prepareEnterAnimation() {
302             super.prepareEnterAnimation();
303             mGlifLayout.getHeaderTextView().setAlpha(0f);
304             mCancelButton.setAlpha(0f);
305             if (mForgotButton != null) {
306                 mForgotButton.setAlpha(0f);
307             }
308             mLockPatternView.setAlpha(0f);
309             mGlifLayout.getDescriptionTextView().setAlpha(0f);
310         }
311 
getDefaultDetails()312         private String getDefaultDetails() {
313             if (mFrp) {
314                 return getString(R.string.lockpassword_confirm_your_pattern_details_frp);
315             }
316             if (mRepairMode) {
317                 return getString(R.string.lockpassword_confirm_repair_mode_pattern_details);
318             }
319             if (mRemoteValidation) {
320                 return getString(
321                         R.string.lockpassword_remote_validation_pattern_details);
322             }
323             final boolean isStrongAuthRequired = isStrongAuthRequired();
324             return isStrongAuthRequired
325                     ? getString(R.string.lockpassword_strong_auth_required_device_pattern)
326                     : getString(R.string.lockpassword_confirm_your_pattern_generic);
327         }
328 
getActiveViews()329         private Object[][] getActiveViews() {
330             ArrayList<ArrayList<Object>> result = new ArrayList<>();
331             result.add(new ArrayList<>(Collections.singletonList(mGlifLayout.getHeaderTextView())));
332             result.add(new ArrayList<>(
333                     Collections.singletonList(mGlifLayout.getDescriptionTextView())));
334             if (mCancelButton.getVisibility() == View.VISIBLE) {
335                 result.add(new ArrayList<>(Collections.singletonList(mCancelButton)));
336             }
337             if (mForgotButton != null) {
338                 result.add(new ArrayList<>(Collections.singletonList(mForgotButton)));
339             }
340             LockPatternView.CellState[][] cellStates = mLockPatternView.getCellStates();
341             for (int i = 0; i < cellStates.length; i++) {
342                 ArrayList<Object> row = new ArrayList<>();
343                 for (int j = 0; j < cellStates[i].length; j++) {
344                     row.add(cellStates[i][j]);
345                 }
346                 result.add(row);
347             }
348             Object[][] resultArr = new Object[result.size()][cellStates[0].length];
349             for (int i = 0; i < result.size(); i++) {
350                 ArrayList<Object> row = result.get(i);
351                 for (int j = 0; j < row.size(); j++) {
352                     resultArr[i][j] = row.get(j);
353                 }
354             }
355             return resultArr;
356         }
357 
358         @Override
startEnterAnimation()359         public void startEnterAnimation() {
360             super.startEnterAnimation();
361             mLockPatternView.setAlpha(1f);
362             mAppearAnimationUtils.startAnimation2d(getActiveViews(), null, this);
363         }
364 
updateStage(Stage stage)365         private void updateStage(Stage stage) {
366             switch (stage) {
367                 case NeedToUnlock:
368                     if (mHeaderText != null) {
369                         mGlifLayout.setHeaderText(mHeaderText);
370                     } else {
371                         mGlifLayout.setHeaderText(getDefaultHeader());
372                     }
373 
374                     CharSequence detailsText =
375                             mDetailsText == null ? getDefaultDetails() : mDetailsText;
376 
377                     if (mIsManagedProfile) {
378                         mGlifLayout.getDescriptionTextView().setVisibility(View.GONE);
379                     } else {
380                         mGlifLayout.setDescriptionText(detailsText);
381                     }
382 
383                     mErrorTextView.setText("");
384                     updateErrorMessage(
385                             mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
386 
387                     mLockPatternView.setEnabled(true);
388                     mLockPatternView.enableInput();
389                     mLockPatternView.clearPattern();
390                     break;
391                 case NeedToUnlockWrong:
392                     showError(R.string.lockpattern_need_to_unlock_wrong,
393                             CLEAR_WRONG_ATTEMPT_TIMEOUT_MS);
394 
395                     mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
396                     mLockPatternView.setEnabled(true);
397                     mLockPatternView.enableInput();
398                     break;
399                 case LockedOut:
400                     mLockPatternView.clearPattern();
401                     // enabled = false means: disable input, and have the
402                     // appearance of being disabled.
403                     mLockPatternView.setEnabled(false); // appearance of being disabled
404                     break;
405             }
406 
407             // Always announce the header for accessibility. This is a no-op
408             // when accessibility is disabled.
409             mGlifLayout.getHeaderTextView().announceForAccessibility(mGlifLayout.getHeaderText());
410         }
411 
getDefaultHeader()412         private String getDefaultHeader() {
413             if (mFrp) {
414                 return getString(R.string.lockpassword_confirm_your_pattern_header_frp);
415             }
416             if (mRepairMode) {
417                 return getString(R.string.lockpassword_confirm_repair_mode_pattern_header);
418             }
419             if (mRemoteValidation) {
420                 return getString(R.string.lockpassword_remote_validation_header);
421             }
422             if (mIsManagedProfile) {
423                 return mDevicePolicyManager.getResources().getString(
424                         CONFIRM_WORK_PROFILE_PATTERN_HEADER,
425                         () -> getString(R.string.lockpassword_confirm_your_work_pattern_header));
426             }
427             if (android.multiuser.Flags.showCustomUnlockTitleInsidePrivateProfile()
428                     && Utils.isPrivateProfile(mEffectiveUserId, getActivity())
429                     && !UserManager.get(getActivity())
430                     .isQuietModeEnabled(UserHandle.of(mEffectiveUserId))) {
431                 return getString(R.string.private_space_confirm_your_pattern_header);
432             }
433 
434             return getString(R.string.lockpassword_confirm_your_pattern_header);
435         }
436 
getDefaultCheckboxLabel()437         private String getDefaultCheckboxLabel() {
438             if (mRemoteValidation) {
439                 return getString(R.string.lockpassword_remote_validation_set_pattern_as_screenlock);
440             }
441             throw new IllegalStateException(
442                     "Trying to get default checkbox label for illegal flow");
443         }
444 
445         private Runnable mClearPatternRunnable = new Runnable() {
446             public void run() {
447                 mLockPatternView.clearPattern();
448             }
449         };
450 
451         // clear the wrong pattern unless they have started a new one
452         // already
postClearPatternRunnable()453         private void postClearPatternRunnable() {
454             mLockPatternView.removeCallbacks(mClearPatternRunnable);
455             mLockPatternView.postDelayed(mClearPatternRunnable, CLEAR_WRONG_ATTEMPT_TIMEOUT_MS);
456         }
457 
458         @Override
authenticationSucceeded()459         protected void authenticationSucceeded() {
460             mCredentialCheckResultTracker.setResult(true, new Intent(), 0, mEffectiveUserId);
461         }
462 
startDisappearAnimation(final Intent intent)463         private void startDisappearAnimation(final Intent intent) {
464             if (mDisappearing) {
465                 return;
466             }
467             mDisappearing = true;
468 
469             final ConfirmLockPattern activity = (ConfirmLockPattern) getActivity();
470             // Bail if there is no active activity.
471             if (activity == null || activity.isFinishing()) {
472                 return;
473             }
474             if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
475                 mLockPatternView.clearPattern();
476                 mDisappearAnimationUtils.startAnimation2d(getActiveViews(),
477                         () -> {
478                             activity.setResult(RESULT_OK, intent);
479                             activity.finish();
480                             activity.overridePendingTransition(
481                                     R.anim.confirm_credential_close_enter,
482                                     R.anim.confirm_credential_close_exit);
483                         }, this);
484             } else {
485                 activity.setResult(RESULT_OK, intent);
486                 activity.finish();
487             }
488         }
489 
490         /**
491          * The pattern listener that responds according to a user confirming
492          * an existing lock pattern.
493          */
494         private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener
495                 = new LockPatternView.OnPatternListener() {
496 
497             public void onPatternStart() {
498                 mLockPatternView.removeCallbacks(mClearPatternRunnable);
499             }
500 
501             public void onPatternCleared() {
502                 mLockPatternView.removeCallbacks(mClearPatternRunnable);
503             }
504 
505             public void onPatternCellAdded(List<Cell> pattern) {
506 
507             }
508 
509             public void onPatternDetected(List<LockPatternView.Cell> pattern) {
510                 if (mPendingLockCheck != null || mDisappearing) {
511                     return;
512                 }
513 
514                 mLockPatternView.setEnabled(false);
515 
516                 final LockscreenCredential credential = LockscreenCredential.createPattern(pattern);
517 
518                 if (mRemoteValidation) {
519                     validateGuess(credential);
520                     updateRemoteLockscreenValidationViews();
521                     return;
522                 }
523 
524                 // TODO(b/161956762): Sanitize this
525                 Intent intent = new Intent();
526                 if (mReturnGatekeeperPassword) {
527                     if (isInternalActivity()) {
528                         startVerifyPattern(credential, intent,
529                                 LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE);
530                         return;
531                     }
532                 } else if (mForceVerifyPath) {
533                     if (isInternalActivity()) {
534                         final int flags = mRequestWriteRepairModePassword
535                                 ? LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW : 0;
536                         startVerifyPattern(credential, intent, flags);
537                         return;
538                     }
539                 } else {
540                     startCheckPattern(credential, intent);
541                     return;
542                 }
543 
544                 mCredentialCheckResultTracker.setResult(false, intent, 0, mEffectiveUserId);
545             }
546 
547             private boolean isInternalActivity() {
548                 return getActivity() instanceof ConfirmLockPattern.InternalActivity;
549             }
550 
551             private void startVerifyPattern(final LockscreenCredential pattern,
552                     final Intent intent, @LockPatternUtils.VerifyFlag int flags) {
553                 final int localEffectiveUserId = mEffectiveUserId;
554                 final int localUserId = mUserId;
555                 final LockPatternChecker.OnVerifyCallback onVerifyCallback =
556                     (response, timeoutMs) -> {
557                         mPendingLockCheck = null;
558                         final boolean matched = response.isMatched();
559                         if (matched && mReturnCredentials) {
560                             if ((flags & LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0) {
561                                 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
562                                         response.getGatekeeperPasswordHandle());
563                             } else {
564                                 intent.putExtra(
565                                         ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
566                                         response.getGatekeeperHAT());
567                             }
568                         }
569                         mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
570                                 localEffectiveUserId);
571                 };
572                 mPendingLockCheck = (localEffectiveUserId == localUserId)
573                         ? LockPatternChecker.verifyCredential(
574                                 mLockPatternUtils, pattern, localUserId, flags,
575                                 onVerifyCallback)
576                         : LockPatternChecker.verifyTiedProfileChallenge(
577                                 mLockPatternUtils, pattern, localUserId, flags,
578                                 onVerifyCallback);
579             }
580 
581             private void startCheckPattern(final LockscreenCredential pattern,
582                     final Intent intent) {
583                 if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
584                     // Pattern size is less than the minimum, do not count it as an fail attempt.
585                     onPatternChecked(false, intent, 0, mEffectiveUserId, false /* newResult */);
586                     return;
587                 }
588 
589                 final int localEffectiveUserId = mEffectiveUserId;
590                 mPendingLockCheck = LockPatternChecker.checkCredential(
591                         mLockPatternUtils,
592                         pattern,
593                         localEffectiveUserId,
594                         new LockPatternChecker.OnCheckCallback() {
595                             @Override
596                             public void onChecked(boolean matched, int timeoutMs) {
597                                 mPendingLockCheck = null;
598                                 if (matched && isInternalActivity() && mReturnCredentials) {
599                                     intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
600                                                     pattern);
601                                 }
602                                 mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
603                                         localEffectiveUserId);
604                             }
605                         });
606             }
607         };
608 
onPatternChecked(boolean matched, Intent intent, int timeoutMs, int effectiveUserId, boolean newResult)609         private void onPatternChecked(boolean matched, Intent intent, int timeoutMs,
610                 int effectiveUserId, boolean newResult) {
611             mLockPatternView.setEnabled(true);
612             if (matched) {
613                 if (newResult) {
614                     ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
615                             mUserManager, mDevicePolicyManager, mEffectiveUserId,
616                             /* isStrongAuth */ true);
617                 }
618                 startDisappearAnimation(intent);
619                 ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
620             } else {
621                 if (timeoutMs > 0) {
622                     refreshLockScreen();
623                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
624                             effectiveUserId, timeoutMs);
625                     handleAttemptLockout(deadline);
626                 } else {
627                     updateStage(Stage.NeedToUnlockWrong);
628                     postClearPatternRunnable();
629                 }
630                 if (newResult) {
631                     reportFailedAttempt();
632                 }
633             }
634         }
635 
636         @Override
onRemoteLockscreenValidationResult( RemoteLockscreenValidationResult result)637         public void onRemoteLockscreenValidationResult(
638                 RemoteLockscreenValidationResult result) {
639             switch (result.getResultCode()) {
640                 case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
641                     if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
642                             .getLockscreenCredential() != null) {
643                         Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
644                         SaveAndFinishWorker saveAndFinishWorker = new SaveAndFinishWorker();
645                         getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
646                                 .commit();
647                         getFragmentManager().executePendingTransactions();
648                         saveAndFinishWorker
649                                 .setListener(this)
650                                 .setRequestGatekeeperPasswordHandle(true);
651                         saveAndFinishWorker.start(
652                                 mLockPatternUtils,
653                                 mRemoteLockscreenValidationFragment.getLockscreenCredential(),
654                                 /* currentCredential= */ null,
655                                 mEffectiveUserId);
656                     } else {
657                         mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
658                                 /* timeoutMs= */ 0, mEffectiveUserId);
659                     }
660                     return;
661                 case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
662                     mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
663                             /* timeoutMs= */ 0, mEffectiveUserId);
664                     break;
665                 case RemoteLockscreenValidationResult.RESULT_LOCKOUT:
666                     mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
667                             (int) result.getTimeoutMillis(), mEffectiveUserId);
668                     break;
669                 case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
670                 case RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED:
671                     onRemoteLockscreenValidationFailure(String.format(
672                             "Cannot continue remote lockscreen validation. ResultCode=%d",
673                             result.getResultCode()));
674                     break;
675             }
676             updateRemoteLockscreenValidationViews();
677             mRemoteLockscreenValidationFragment.clearLockscreenCredential();
678         }
679 
680         @Override
onCredentialChecked(boolean matched, Intent intent, int timeoutMs, int effectiveUserId, boolean newResult)681         public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
682                 int effectiveUserId, boolean newResult) {
683             onPatternChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
684         }
685 
686         @Override
getLastTryOverrideErrorMessageId(int userType)687         protected String getLastTryOverrideErrorMessageId(int userType) {
688             if (userType == USER_TYPE_MANAGED_PROFILE) {
689                 return WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE;
690             }
691 
692             return UNDEFINED;
693         }
694 
695         @Override
getLastTryDefaultErrorMessage(int userType)696         protected int getLastTryDefaultErrorMessage(int userType) {
697             switch (userType) {
698                 case USER_TYPE_PRIMARY:
699                     return R.string.lock_last_pattern_attempt_before_wipe_device;
700                 case USER_TYPE_MANAGED_PROFILE:
701                     return R.string.lock_last_pattern_attempt_before_wipe_profile;
702                 case USER_TYPE_SECONDARY:
703                     return R.string.lock_last_pattern_attempt_before_wipe_user;
704                 default:
705                     throw new IllegalArgumentException("Unrecognized user type:" + userType);
706             }
707         }
708 
handleAttemptLockout(long elapsedRealtimeDeadline)709         private void handleAttemptLockout(long elapsedRealtimeDeadline) {
710             clearResetErrorRunnable();
711             updateStage(Stage.LockedOut);
712             long elapsedRealtime = SystemClock.elapsedRealtime();
713             mCountdownTimer = new CountDownTimer(
714                     elapsedRealtimeDeadline - elapsedRealtime,
715                     LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
716 
717                 @Override
718                 public void onTick(long millisUntilFinished) {
719                     final int secondsCountdown = (int) (millisUntilFinished / 1000);
720                     mErrorTextView.setText(getString(
721                             R.string.lockpattern_too_many_failed_confirmation_attempts,
722                             secondsCountdown));
723                 }
724 
725                 @Override
726                 public void onFinish() {
727                     updateStage(Stage.NeedToUnlock);
728                 }
729             }.start();
730         }
731 
732         @Override
createAnimation(Object obj, long delay, long duration, float translationY, final boolean appearing, Interpolator interpolator, final Runnable finishListener)733         public void createAnimation(Object obj, long delay,
734                 long duration, float translationY, final boolean appearing,
735                 Interpolator interpolator,
736                 final Runnable finishListener) {
737             if (obj instanceof LockPatternView.CellState) {
738                 final LockPatternView.CellState animatedCell = (LockPatternView.CellState) obj;
739                 mLockPatternView.startCellStateAnimation(animatedCell,
740                         1f, appearing ? 1f : 0f, /* alpha */
741                         appearing ? translationY : 0f, /* startTranslation */
742                         appearing ? 0f : translationY, /* endTranslation */
743                         appearing ? 0f : 1f, 1f /* scale */,
744                         delay, duration, interpolator, finishListener);
745             } else {
746                 mAppearAnimationUtils.createAnimation((View) obj, delay, duration, translationY,
747                         appearing, interpolator, finishListener);
748             }
749         }
750 
751         /**
752          * Callback for when the current device's lockscreen to the guess used for
753          * remote lockscreen validation.
754          */
755         @Override
onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData)756         public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
757             Log.i(TAG, "Device lockscreen has been set to remote device's lockscreen.");
758             mRemoteLockscreenValidationFragment.clearLockscreenCredential();
759 
760             Intent result = new Intent();
761             if (mRemoteValidation && containsGatekeeperPasswordHandle(resultData)) {
762                 result.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGatekeeperPasswordHandle(resultData));
763                 SetupRedactionInterstitial.setEnabled(getContext(), true);
764             }
765             mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
766                     /* timeoutMs= */ 0, mEffectiveUserId);
767         }
768     }
769 }
770