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