diff --git a/res/values/strings.xml b/res/values/strings.xml index e6831175347..8b5f6339912 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1130,6 +1130,21 @@ Change unlock password + + Try again. Attempt %1$d of %2$d. + + Last try + + If you enter an incorrect work pattern on this attempt, your work profile and associated data will be removed from this device. + + If you enter an incorrect work PIN on this attempt, your work profile and associated data will be removed from this device. + + If you enter an incorrect work password on this attempt, your work profile and associated data will be removed from this device. + + Too many incorrect attempts. Your work profile and associated data will be removed from this device. + + Dismiss + Password must be at least %d characters diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java index 40e3103bf37..ad1d2ebd2d6 100644 --- a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java +++ b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java @@ -20,10 +20,13 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityOptions; +import android.app.AlertDialog; import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.IntentSender; import android.graphics.Point; @@ -31,7 +34,9 @@ import android.graphics.PorterDuff; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Handler; import android.os.RemoteException; +import android.os.UserHandle; import android.os.UserManager; import android.view.View; import android.view.ViewGroup; @@ -40,6 +45,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.widget.LockPatternUtils; import com.android.settings.fingerprint.FingerprintUiHelper; /** @@ -65,6 +71,9 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr protected Button mCancelButton; protected ImageView mFingerprintIcon; protected int mEffectiveUserId; + protected LockPatternUtils mLockPatternUtils; + protected TextView mErrorTextView; + protected final Handler mHandler = new Handler(); @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -74,6 +83,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr // Only take this argument into account if it belongs to the current profile. Intent intent = getActivity().getIntent(); mEffectiveUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras()); + mLockPatternUtils = new LockPatternUtils(getActivity()); } @Override @@ -109,6 +119,10 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr if (mAllowFpAuthentication) { mFingerprintHelper.startListening(); } + if (isProfileChallenge()) { + updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts( + mEffectiveUserId)); + } } protected void setAccessibilityTitle(CharSequence suplementalText) { @@ -200,4 +214,87 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr screenSize.y)); } } + + protected boolean isProfileChallenge() { + return UserHandle.myUserId() != mEffectiveUserId; + } + + protected void reportSuccessfullAttempt() { + if (isProfileChallenge()) { + mLockPatternUtils.reportSuccessfulPasswordAttempt(mEffectiveUserId); + } + } + + protected void reportFailedAttempt() { + if (isProfileChallenge()) { + // + 1 for this attempt. + updateErrorMessage( + mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId) + 1); + mLockPatternUtils.reportFailedPasswordAttempt(mEffectiveUserId); + } + } + + protected void updateErrorMessage(int numAttempts) { + final int maxAttempts = + mLockPatternUtils.getMaximumFailedPasswordsForWipe(mEffectiveUserId); + if (maxAttempts > 0 && numAttempts > 0) { + int remainingAttempts = maxAttempts - numAttempts; + if (remainingAttempts == 1) { + // Last try + final String title = getActivity().getString( + R.string.lock_profile_wipe_warning_title); + final String message = getActivity().getString(getLastTryErrorMessage()); + showDialog(title, message, android.R.string.ok, false /* dismiss */); + } else if (remainingAttempts <= 0) { + // Profile is wiped + final String message = getActivity().getString(R.string.lock_profile_wipe_content); + showDialog(null, message, R.string.lock_profile_wipe_dismiss, true /* dismiss */); + } + if (mErrorTextView != null) { + final String message = getActivity().getString(R.string.lock_profile_wipe_attempts, + numAttempts, maxAttempts); + showError(message, 0); + } + } + } + + protected abstract int getLastTryErrorMessage(); + + private final Runnable mResetErrorRunnable = new Runnable() { + @Override + public void run() { + mErrorTextView.setText(""); + } + }; + + protected void showError(CharSequence msg, long timeout) { + mErrorTextView.setText(msg); + onShowError(); + mHandler.removeCallbacks(mResetErrorRunnable); + if (timeout != 0) { + mHandler.postDelayed(mResetErrorRunnable, timeout); + } + } + + protected abstract void onShowError(); + + protected void showError(int msg, long timeout) { + showError(getText(msg), timeout); + } + + private void showDialog(String title, String message, int buttonString, final boolean dismiss) { + final AlertDialog dialog = new AlertDialog.Builder(getActivity()) + .setTitle(title) + .setMessage(message) + .setPositiveButton(buttonString, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (dismiss) { + getActivity().finish(); + } + } + }) + .create(); + dialog.show(); + } } diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java index 8f832210707..c69bb894509 100644 --- a/src/com/android/settings/ConfirmLockPassword.java +++ b/src/com/android/settings/ConfirmLockPassword.java @@ -20,11 +20,9 @@ import android.app.Fragment; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; -import android.content.IntentSender; import android.os.AsyncTask; import android.os.Bundle; import android.os.CountDownTimer; -import android.os.Handler; import android.os.SystemClock; import android.os.UserManager; import android.os.storage.StorageManager; @@ -84,14 +82,11 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result"; private TextView mPasswordEntry; private TextViewInputDisabler mPasswordEntryInputDisabler; - private LockPatternUtils mLockPatternUtils; private AsyncTask mPendingLockCheck; private CredentialCheckResultTracker mCredentialCheckResultTracker; private boolean mDisappearing = false; private TextView mHeaderTextView; private TextView mDetailsTextView; - private TextView mErrorTextView; - private Handler mHandler = new Handler(); private CountDownTimer mCountdownTimer; private boolean mIsAlpha; private InputMethodManager mImm; @@ -108,7 +103,6 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mLockPatternUtils = new LockPatternUtils(getActivity()); } @Override @@ -193,6 +187,12 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { : 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(); @@ -406,9 +406,12 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { } private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs, - int effectiveUserId) { + int effectiveUserId, boolean newResult) { mPasswordEntryInputDisabler.setInputEnabled(true); if (matched) { + if (newResult) { + reportSuccessfullAttempt(); + } startDisappearAnimation(intent); checkForPendingIntent(); } else { @@ -417,15 +420,23 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { effectiveUserId, timeoutMs); handleAttemptLockout(deadline); } else { - showError(getErrorMessage()); + showError(getErrorMessage(), ERROR_MESSAGE_TIMEOUT); + } + if (newResult) { + reportFailedAttempt(); } } } @Override public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs, - int effectiveUserId) { - onPasswordChecked(matched, intent, timeoutMs, effectiveUserId); + int effectiveUserId, boolean newResult) { + onPasswordChecked(matched, intent, timeoutMs, effectiveUserId, newResult); + } + + @Override + protected void onShowError() { + mPasswordEntry.setText(null); } private void handleAttemptLockout(long elapsedRealtimeDeadline) { @@ -447,6 +458,10 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { public void onFinish() { resetState(); mErrorTextView.setText(""); + if (isProfileChallenge()) { + updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts( + mEffectiveUserId)); + } } }.start(); } @@ -464,29 +479,6 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { } } - private void showError(int msg) { - showError(msg, ERROR_MESSAGE_TIMEOUT); - } - - private final Runnable mResetErrorRunnable = new Runnable() { - public void run() { - mErrorTextView.setText(""); - } - }; - - private void showError(CharSequence msg, long timeout) { - mErrorTextView.setText(msg); - mPasswordEntry.setText(null); - mHandler.removeCallbacks(mResetErrorRunnable); - if (timeout != 0) { - mHandler.postDelayed(mResetErrorRunnable, timeout); - } - } - - private void showError(int msg, long timeout) { - showError(getText(msg), timeout); - } - // {@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 diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java index d4b09025693..dbe9ebe9293 100644 --- a/src/com/android/settings/ConfirmLockPattern.java +++ b/src/com/android/settings/ConfirmLockPattern.java @@ -18,7 +18,6 @@ package com.android.settings; import android.app.Activity; import android.content.Intent; -import android.content.IntentSender; import android.os.AsyncTask; import android.os.Bundle; import android.os.CountDownTimer; @@ -85,7 +84,6 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result"; private LockPatternView mLockPatternView; - private LockPatternUtils mLockPatternUtils; private AsyncTask mPendingLockCheck; private CredentialCheckResultTracker mCredentialCheckResultTracker; private boolean mDisappearing = false; @@ -93,7 +91,6 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { private TextView mHeaderTextView; private TextView mDetailsTextView; - private TextView mErrorTextView; private View mLeftSpacerLandscape; private View mRightSpacerLandscape; @@ -112,7 +109,6 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mLockPatternUtils = new LockPatternUtils(getActivity()); } @Override @@ -220,6 +216,10 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { mCredentialCheckResultTracker.setListener(this); } + @Override + protected void onShowError() { + } + @Override public void prepareEnterAnimation() { super.prepareEnterAnimation(); @@ -284,6 +284,10 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { R.string.lockpassword_confirm_your_pattern_generic_profile); } mErrorTextView.setText(""); + if (isProfileChallenge()) { + updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts( + mEffectiveUserId)); + } mLockPatternView.setEnabled(true); mLockPatternView.enableInput(); @@ -470,9 +474,12 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { }; private void onPatternChecked(boolean matched, Intent intent, int timeoutMs, - int effectiveUserId) { + int effectiveUserId, boolean newResult) { mLockPatternView.setEnabled(true); if (matched) { + if (newResult) { + reportSuccessfullAttempt(); + } startDisappearAnimation(intent); checkForPendingIntent(); } else { @@ -484,13 +491,21 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { updateStage(Stage.NeedToUnlockWrong); postClearPatternRunnable(); } + if (newResult) { + reportFailedAttempt(); + } } } @Override public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs, - int effectiveUserId) { - onPatternChecked(matched, intent, timeoutMs, effectiveUserId); + int effectiveUserId, boolean newResult) { + onPatternChecked(matched, intent, timeoutMs, effectiveUserId, newResult); + } + + @Override + protected int getLastTryErrorMessage() { + return R.string.lock_profile_wipe_warning_content_pattern; } private void handleAttemptLockout(long elapsedRealtimeDeadline) { diff --git a/src/com/android/settings/CredentialCheckResultTracker.java b/src/com/android/settings/CredentialCheckResultTracker.java index 5c5507344e3..179a93c7ad6 100644 --- a/src/com/android/settings/CredentialCheckResultTracker.java +++ b/src/com/android/settings/CredentialCheckResultTracker.java @@ -47,7 +47,7 @@ public class CredentialCheckResultTracker extends Fragment { mListener = listener; if (mListener != null && mHasResult) { mListener.onCredentialChecked(mResultMatched, mResultData, mResultTimeoutMs, - mResultEffectiveUserId); + mResultEffectiveUserId, false /* newResult */); } } @@ -60,7 +60,7 @@ public class CredentialCheckResultTracker extends Fragment { mHasResult = true; if (mListener != null) { mListener.onCredentialChecked(mResultMatched, mResultData, mResultTimeoutMs, - mResultEffectiveUserId); + mResultEffectiveUserId, true /* newResult */); } } @@ -74,6 +74,6 @@ public class CredentialCheckResultTracker extends Fragment { interface Listener { public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs, - int effectiveUserId); + int effectiveUserId, boolean newResult); } }