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