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);
}
}