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