Files
app_Settings/src/com/android/settings/ConfirmLockPassword.java
Daniel U 6655e1b6f5 Hint user that fingerprint cannot unlock FBE keys
Show a hint text to user noting that pattern/PIN/password is
required when decrypting the credential based storage when file
based encryption is turned on.

The hint text is the same as that of the device unlock screen after
device reboot.

Bug: 27964055
Change-Id: I0d5a493bab69eae5ce4742bd07d4851387863cac
2016-04-08 15:23:46 +01:00

511 lines
21 KiB
Java

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.text.InputType;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import java.util.ArrayList;
public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
public static class InternalActivity extends ConfirmLockPassword {
}
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
return modIntent;
}
@Override
protected boolean isValidFragment(String fragmentName) {
if (ConfirmLockPasswordFragment.class.getName().equals(fragmentName)) return true;
return false;
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Fragment fragment = getFragmentManager().findFragmentById(R.id.main_content);
if (fragment != null && fragment instanceof ConfirmLockPasswordFragment) {
((ConfirmLockPasswordFragment)fragment).onWindowFocusChanged(hasFocus);
}
}
public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
implements OnClickListener, OnEditorActionListener,
CredentialCheckResultTracker.Listener {
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryInputDisabler;
private AsyncTask<?, ?, ?> mPendingLockCheck;
private CredentialCheckResultTracker mCredentialCheckResultTracker;
private boolean mDisappearing = false;
private TextView mHeaderTextView;
private TextView mDetailsTextView;
private CountDownTimer mCountdownTimer;
private boolean mIsAlpha;
private InputMethodManager mImm;
private boolean mUsingFingerprint = false;
private AppearAnimationUtils mAppearAnimationUtils;
private DisappearAnimationUtils mDisappearAnimationUtils;
private boolean mBlockImm;
// required constructor for fragments
public ConfirmLockPasswordFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
mEffectiveUserId);
View view = inflater.inflate(R.layout.confirm_lock_password, null);
mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
// Strong auth is required when the user is locked.
// Currently a user does not get locked again until the device restarts. Show the
// hint text as "device has just been restarted".
mStrongAuthRequiredTextView = (TextView) view.findViewById(R.id.strongAuthRequiredText);
if (mIsAlpha) {
mStrongAuthRequiredTextView.setText(
R.string.lockpassword_strong_auth_required_reason_restart_password);
} else {
mStrongAuthRequiredTextView.setText(
R.string.lockpassword_strong_auth_required_reason_restart_pin);
}
mImm = (InputMethodManager) getActivity().getSystemService(
Context.INPUT_METHOD_SERVICE);
Intent intent = getActivity().getIntent();
if (intent != null) {
CharSequence headerMessage = intent.getCharSequenceExtra(
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
CharSequence detailsMessage = intent.getCharSequenceExtra(
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
if (TextUtils.isEmpty(headerMessage)) {
headerMessage = getString(getDefaultHeader());
}
if (TextUtils.isEmpty(detailsMessage)) {
detailsMessage = getString(getDefaultDetails());
}
mHeaderTextView.setText(headerMessage);
mDetailsTextView.setText(detailsMessage);
}
int currentType = mPasswordEntry.getInputType();
mPasswordEntry.setInputType(mIsAlpha ? currentType
: (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
220, 2f /* translationScale */, 1f /* delayScale*/,
AnimationUtils.loadInterpolator(getContext(),
android.R.interpolator.linear_out_slow_in));
mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
110, 1f /* translationScale */,
0.5f /* delayScale */, AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.fast_out_linear_in));
setAccessibilityTitle(mHeaderTextView.getText());
mCredentialCheckResultTracker = (CredentialCheckResultTracker) getFragmentManager()
.findFragmentByTag(FRAGMENT_TAG_CHECK_LOCK_RESULT);
if (mCredentialCheckResultTracker == null) {
mCredentialCheckResultTracker = new CredentialCheckResultTracker();
getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
}
return view;
}
private int getDefaultHeader() {
return mIsAlpha ? R.string.lockpassword_confirm_your_password_header
: R.string.lockpassword_confirm_your_pin_header;
}
private int getDefaultDetails() {
boolean isProfile = Utils.isManagedProfile(
UserManager.get(getActivity()), mEffectiveUserId);
if (mIsAlpha) {
return isProfile ? R.string.lockpassword_confirm_your_password_generic_profile
: R.string.lockpassword_confirm_your_password_generic;
} else {
return isProfile ? R.string.lockpassword_confirm_your_pin_generic_profile
: R.string.lockpassword_confirm_your_pin_generic;
}
}
private int getErrorMessage() {
return mIsAlpha ? R.string.lockpassword_invalid_password
: R.string.lockpassword_invalid_pin;
}
@Override
protected int getLastTryErrorMessage() {
return mIsAlpha ? R.string.lock_profile_wipe_warning_content_password
: R.string.lock_profile_wipe_warning_content_pin;
}
@Override
public void prepareEnterAnimation() {
super.prepareEnterAnimation();
mHeaderTextView.setAlpha(0f);
mDetailsTextView.setAlpha(0f);
mCancelButton.setAlpha(0f);
mPasswordEntry.setAlpha(0f);
mFingerprintIcon.setAlpha(0f);
mBlockImm = true;
}
private View[] getActiveViews() {
ArrayList<View> result = new ArrayList<>();
result.add(mHeaderTextView);
result.add(mDetailsTextView);
if (mCancelButton.getVisibility() == View.VISIBLE) {
result.add(mCancelButton);
}
result.add(mPasswordEntry);
if (mFingerprintIcon.getVisibility() == View.VISIBLE) {
result.add(mFingerprintIcon);
}
return result.toArray(new View[] {});
}
@Override
public void startEnterAnimation() {
super.startEnterAnimation();
mAppearAnimationUtils.startAnimation(getActiveViews(), new Runnable() {
@Override
public void run() {
mBlockImm = false;
resetState();
}
});
}
@Override
public void onPause() {
super.onPause();
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
mCountdownTimer = null;
}
mCredentialCheckResultTracker.setListener(null);
}
@Override
protected int getMetricsCategory() {
return MetricsEvent.CONFIRM_LOCK_PASSWORD;
}
@Override
public void onResume() {
super.onResume();
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
if (deadline != 0) {
mCredentialCheckResultTracker.clearResult();
handleAttemptLockout(deadline);
} else {
resetState();
}
mCredentialCheckResultTracker.setListener(this);
}
@Override
protected void authenticationSucceeded() {
mCredentialCheckResultTracker.setResult(true, new Intent(), 0, mEffectiveUserId);
}
@Override
public void onFingerprintIconVisibilityChanged(boolean visible) {
mUsingFingerprint = visible;
}
private void resetState() {
if (mBlockImm) return;
mPasswordEntry.setEnabled(true);
mPasswordEntryInputDisabler.setInputEnabled(true);
if (shouldAutoShowSoftKeyboard()) {
mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
}
}
private boolean shouldAutoShowSoftKeyboard() {
return mPasswordEntry.isEnabled() && !mUsingFingerprint;
}
public void onWindowFocusChanged(boolean hasFocus) {
if (!hasFocus || mBlockImm) {
return;
}
// Post to let window focus logic to finish to allow soft input show/hide properly.
mPasswordEntry.post(new Runnable() {
@Override
public void run() {
if (shouldAutoShowSoftKeyboard()) {
resetState();
return;
}
mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(),
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
});
}
private void handleNext() {
if (mPendingLockCheck != null || mDisappearing) {
return;
}
mPasswordEntryInputDisabler.setInputEnabled(false);
final String pin = mPasswordEntry.getText().toString();
final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
Intent intent = new Intent();
if (verifyChallenge) {
if (isInternalActivity()) {
startVerifyPassword(pin, intent);
return;
}
} else {
startCheckPassword(pin, intent);
return;
}
mCredentialCheckResultTracker.setResult(false, intent, 0, mEffectiveUserId);
}
private boolean isInternalActivity() {
return getActivity() instanceof ConfirmLockPassword.InternalActivity;
}
private void startVerifyPassword(final String pin, final Intent intent) {
long challenge = getActivity().getIntent().getLongExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
final int localEffectiveUserId = mEffectiveUserId;
final int localUserId = mUserId;
final LockPatternChecker.OnVerifyCallback onVerifyCallback =
new LockPatternChecker.OnVerifyCallback() {
@Override
public void onVerified(byte[] token, int timeoutMs) {
mPendingLockCheck = null;
boolean matched = false;
if (token != null) {
matched = true;
intent.putExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
token);
}
mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
localUserId);
}
};
mPendingLockCheck = (localEffectiveUserId == localUserId)
? LockPatternChecker.verifyPassword(
mLockPatternUtils, pin, challenge, localUserId, onVerifyCallback)
: LockPatternChecker.verifyTiedProfileChallenge(
mLockPatternUtils, pin, false, challenge, localUserId,
onVerifyCallback);
}
private void startCheckPassword(final String pin, final Intent intent) {
final int localEffectiveUserId = mEffectiveUserId;
mPendingLockCheck = LockPatternChecker.checkPassword(
mLockPatternUtils,
pin,
localEffectiveUserId,
new LockPatternChecker.OnCheckCallback() {
@Override
public void onChecked(boolean matched, int timeoutMs) {
mPendingLockCheck = null;
if (matched && isInternalActivity()) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
: StorageManager.CRYPT_TYPE_PIN);
intent.putExtra(
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
}
mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
localEffectiveUserId);
}
});
}
private void startDisappearAnimation(final Intent intent) {
if (mDisappearing) {
return;
}
mDisappearing = true;
if (getActivity().getThemeResId() == R.style.Theme_ConfirmDeviceCredentialsDark) {
mDisappearAnimationUtils.startAnimation(getActiveViews(), new Runnable() {
@Override
public void run() {
// Bail if there is no active activity.
if (getActivity() == null || getActivity().isFinishing()) {
return;
}
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
getActivity().overridePendingTransition(
R.anim.confirm_credential_close_enter,
R.anim.confirm_credential_close_exit);
}
});
} else {
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
}
}
private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs,
int effectiveUserId, boolean newResult) {
mPasswordEntryInputDisabler.setInputEnabled(true);
if (matched) {
if (newResult) {
reportSuccessfullAttempt();
}
startDisappearAnimation(intent);
checkForPendingIntent();
} else {
if (timeoutMs > 0) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
effectiveUserId, timeoutMs);
handleAttemptLockout(deadline);
} else {
showError(getErrorMessage(), ERROR_MESSAGE_TIMEOUT);
}
if (newResult) {
reportFailedAttempt();
}
}
}
@Override
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
int effectiveUserId, boolean newResult) {
onPasswordChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
}
@Override
protected void onShowError() {
mPasswordEntry.setText(null);
}
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
long elapsedRealtime = SystemClock.elapsedRealtime();
mPasswordEntry.setEnabled(false);
mCountdownTimer = new CountDownTimer(
elapsedRealtimeDeadline - elapsedRealtime,
LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
@Override
public void onTick(long millisUntilFinished) {
final int secondsCountdown = (int) (millisUntilFinished / 1000);
showError(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts,
secondsCountdown), 0);
}
@Override
public void onFinish() {
resetState();
mErrorTextView.setText("");
if (isProfileChallenge()) {
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
mEffectiveUserId));
}
}
}.start();
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.next_button:
handleNext();
break;
case R.id.cancel_button:
getActivity().setResult(RESULT_CANCELED);
getActivity().finish();
break;
}
}
// {@link OnEditorActionListener} methods.
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter or "done" key
if (actionId == EditorInfo.IME_NULL
|| actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_NEXT) {
handleNext();
return true;
}
return false;
}
}
}