Make failed ConfirmCredential attempts count towards wipe

Previously, failed ConfirmDeviceCredential attempts only counted towards the
wipe limit if it is used as a separate work challenge.

In this CL, we additionally make these failed attempts count towards the total
failures in the following scenarios:
  1) when unified work challenge is enabled
  2) for the primary user (e.g. when a wipe limit is set by a DO)
  3) for secondary users

Bug: 27238008
Test: manual, by entering wrong credentials multiple times
Test: make SettingsRoboTests
Change-Id: Ie5a099bb3fd46245c13ccf4c8f91c4d935412a4e
This commit is contained in:
Charles He
2017-04-26 18:45:48 +01:00
parent 5230ffe3a5
commit 641c9fc23a
4 changed files with 140 additions and 75 deletions

View File

@@ -1227,20 +1227,38 @@
<!-- Title shown on security settings to allow the user to change their lockscreen password [CHAR LIMIT=22]--> <!-- Title shown on security settings to allow the user to change their lockscreen password [CHAR LIMIT=22]-->
<string name="unlock_change_lock_password_title">Change unlock password</string> <string name="unlock_change_lock_password_title">Change unlock password</string>
<!-- Message shown when the user incorrectly enters their lock and it counts towards the max attempts before wiping the work profile. --> <!-- Message shown on the lock screen when the user incorrectly enters their lock and it counts towards the max attempts before their data on the device is wiped. [CHAR LIMIT=NONE] -->
<string name="lock_profile_wipe_attempts">Try again. Attempt <xliff:g id="current_attempts">%1$d</xliff:g> of <xliff:g id="total_attempts">%2$d</xliff:g>.</string> <string name="lock_failed_attempts_before_wipe">Try again. Attempt <xliff:g id="current_attempts">%1$d</xliff:g> of <xliff:g id="total_attempts">%2$d</xliff:g>.</string>
<!-- Title of a dialog shown when the user only has one attempt left to provide the lock before the work profile is wiped. -->
<string name="lock_profile_wipe_warning_title">Last try</string> <!-- Title of a dialog shown when the user only has one attempt left to provide the lock before the device, one of its users, or a work profile is wiped. [CHAR LIMIT=NONE] -->
<!-- Content of the dialog shown when the user only has one attempt left to provide the work pattern before the work profile is wiped. --> <string name="lock_last_attempt_before_wipe_warning_title">Your data will be deleted</string>
<string name="lock_profile_wipe_warning_content_pattern">If you enter an incorrect work pattern on this attempt, your work profile and associated data will be removed from this device.</string> <!-- Content of the dialog shown when the user only has one attempt left to provide the device lock pattern before the device is wiped. [CHAR LIMIT=NONE] -->
<!-- Content of the dialog shown when the user only has one attempt left to provide the work PIN before the work profile is wiped. --> <string name="lock_last_pattern_attempt_before_wipe_device">If you enter an incorrect pattern on the next attempt, this device's data will be deleted</string>
<string name="lock_profile_wipe_warning_content_pin">If you enter an incorrect work PIN on this attempt, your work profile and associated data will be removed from this device.</string> <!-- Content of the dialog shown when the user only has one attempt left to provide the device lock PIN before the device is wiped. [CHAR LIMIT=NONE] -->
<!-- Content of the dialog shown when the user only has one attempt left to provide the work password before the work profile is wiped. --> <string name="lock_last_pin_attempt_before_wipe_device">If you enter an incorrect PIN on the next attempt, this device's data will be deleted</string>
<string name="lock_profile_wipe_warning_content_password">If you enter an incorrect work password on this attempt, your work profile and associated data will be removed from this device.</string> <!-- Content of the dialog shown when the user only has one attempt left to provide the device lock password before the device is wiped. [CHAR LIMIT=NONE] -->
<!-- Content of the dialog shown when the user has failed to provide the work lock too many times and the work profile is wiped. --> <string name="lock_last_password_attempt_before_wipe_device">If you enter an incorrect password on the next attempt, this device's data will be deleted</string>
<string name="lock_profile_wipe_content">Too many incorrect attempts. Your work profile and associated data will be removed from this device.</string> <!-- Content of the dialog shown when the user only has one attempt left to provide the user lock pattern before the user is removed. [CHAR LIMIT=NONE] -->
<string name="lock_last_pattern_attempt_before_wipe_user">If you enter an incorrect pattern on the next attempt, this user will be deleted</string>
<!-- Content of the dialog shown when the user only has one attempt left to provide the user lock PIN before the user is removed. [CHAR LIMIT=NONE] -->
<string name="lock_last_pin_attempt_before_wipe_user">If you enter an incorrect PIN on the next attempt, this user will be deleted</string>
<!-- Content of the dialog shown when the user only has one attempt left to provide the user lock password before the user is removed. [CHAR LIMIT=NONE] -->
<string name="lock_last_password_attempt_before_wipe_user">If you enter an incorrect password on the next attempt, this user will be deleted</string>
<!-- Content of the dialog shown when the user only has one attempt left to provide the work lock pattern before the work profile is removed. [CHAR LIMIT=NONE] -->
<string name="lock_last_pattern_attempt_before_wipe_profile">If you enter an incorrect pattern on the next attempt, your work profile and its data will be deleted</string>
<!-- Content of the dialog shown when the user only has one attempt left to provide the work lock PIN before the work profile is removed. [CHAR LIMIT=NONE] -->
<string name="lock_last_pin_attempt_before_wipe_profile">If you enter an incorrect PIN on the next attempt, your work profile and its data will be deleted</string>
<!-- Content of the dialog shown when the user only has one attempt left to provide the work lock password before the work profile is removed. [CHAR LIMIT=NONE] -->
<string name="lock_last_password_attempt_before_wipe_profile">If you enter an incorrect password on the next attempt, your work profile and its data will be deleted</string>
<!-- Content of the dialog shown when the user has failed to provide the device lock too many times and the device is wiped. [CHAR LIMIT=NONE] -->
<string name="lock_failed_attempts_now_wiping_device">Too many incorrect attempts. This device's data will be deleted.</string>
<!-- Content of the dialog shown when the user has failed to provide the user lock too many times and the user is removed. [CHAR LIMIT=NONE] -->
<string name="lock_failed_attempts_now_wiping_user">Too many incorrect attempts. This user will be deleted.</string>
<!-- Content of the dialog shown when the user has failed to provide the work lock too many times and the work profile is removed. [CHAR LIMIT=NONE] -->
<string name="lock_failed_attempts_now_wiping_profile">Too many incorrect attempts. This work profile and its data will be deleted.</string>
<!-- Button label to dismiss the dialog telling the user the work profile has been wiped. [CHAR LIMIT=40] --> <!-- Button label to dismiss the dialog telling the user the work profile has been wiped. [CHAR LIMIT=40] -->
<string name="lock_profile_wipe_dismiss">Dismiss</string> <string name="lock_failed_attempts_now_wiping_dialog_dismiss">Dismiss</string>
<!-- Hint shown in dialog screen when password is too short --> <!-- Hint shown in dialog screen when password is too short -->
<string name="lockpassword_password_too_short">Must be at least <xliff:g id="count" example="3">%d</xliff:g> characters</string> <string name="lockpassword_password_too_short">Must be at least <xliff:g id="count" example="3">%d</xliff:g> characters</string>

View File

@@ -31,6 +31,7 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.pm.UserInfo;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
@@ -70,6 +71,10 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
public static final String SHOW_WHEN_LOCKED = public static final String SHOW_WHEN_LOCKED =
PACKAGE + ".ConfirmCredentials.showWhenLocked"; PACKAGE + ".ConfirmCredentials.showWhenLocked";
protected static final int USER_TYPE_PRIMARY = 1;
protected static final int USER_TYPE_MANAGED_PROFILE = 2;
protected static final int USER_TYPE_SECONDARY = 3;
private FingerprintUiHelper mFingerprintHelper; private FingerprintUiHelper mFingerprintHelper;
protected boolean mReturnCredentials = false; protected boolean mReturnCredentials = false;
protected Button mCancelButton; protected Button mCancelButton;
@@ -78,6 +83,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
protected int mUserId; protected int mUserId;
protected UserManager mUserManager; protected UserManager mUserManager;
protected LockPatternUtils mLockPatternUtils; protected LockPatternUtils mLockPatternUtils;
protected DevicePolicyManager mDevicePolicyManager;
protected TextView mErrorTextView; protected TextView mErrorTextView;
protected final Handler mHandler = new Handler(); protected final Handler mHandler = new Handler();
@@ -92,6 +98,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
mUserManager = UserManager.get(getActivity()); mUserManager = UserManager.get(getActivity());
mEffectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId); mEffectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
mLockPatternUtils = new LockPatternUtils(getActivity()); mLockPatternUtils = new LockPatternUtils(getActivity());
mDevicePolicyManager = (DevicePolicyManager) getActivity().getSystemService(
Context.DEVICE_POLICY_SERVICE);
} }
@Override @Override
@@ -122,9 +130,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
} }
private boolean isFingerprintDisabledByAdmin() { private boolean isFingerprintDisabledByAdmin() {
DevicePolicyManager dpm = (DevicePolicyManager) getActivity().getSystemService( final int disabledFeatures =
Context.DEVICE_POLICY_SERVICE); mDevicePolicyManager.getKeyguardDisabledFeatures(null, mEffectiveUserId);
final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, mEffectiveUserId);
return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0; return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0;
} }
@@ -158,10 +165,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
mFingerprintHelper.stopListening(); mFingerprintHelper.stopListening();
} }
} }
if (isProfileChallenge()) { updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
mEffectiveUserId));
}
} }
protected void setAccessibilityTitle(CharSequence supplementalText) { protected void setAccessibilityTitle(CharSequence supplementalText) {
@@ -245,9 +249,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
mainContent.setPadding(0, 0, 0, 0); mainContent.setPadding(0, 0, 0, 0);
} }
DevicePolicyManager dpm = (DevicePolicyManager) getActivity().getSystemService( baseView.setBackground(
Context.DEVICE_POLICY_SERVICE); new ColorDrawable(mDevicePolicyManager.getOrganizationColorForUser(userId)));
baseView.setBackground(new ColorDrawable(dpm.getOrganizationColorForUser(userId)));
ImageView imageView = (ImageView) baseView.findViewById(R.id.background_image); ImageView imageView = (ImageView) baseView.findViewById(R.id.background_image);
if (imageView != null) { if (imageView != null) {
Drawable image = getResources().getDrawable(R.drawable.work_challenge_background); Drawable image = getResources().getDrawable(R.drawable.work_challenge_background);
@@ -263,13 +266,9 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
} }
} }
protected boolean isProfileChallenge() { protected void reportSuccessfulAttempt() {
return mUserManager.isManagedProfile(mEffectiveUserId); mLockPatternUtils.reportSuccessfulPasswordAttempt(mEffectiveUserId);
} if (mUserManager.isManagedProfile(mEffectiveUserId)) {
protected void reportSuccessfullAttempt() {
if (isProfileChallenge()) {
mLockPatternUtils.reportSuccessfulPasswordAttempt(mEffectiveUserId);
// Keyguard is responsible to disable StrongAuth for primary user. Disable StrongAuth // Keyguard is responsible to disable StrongAuth for primary user. Disable StrongAuth
// for work challenge only here. // for work challenge only here.
mLockPatternUtils.userPresent(mEffectiveUserId); mLockPatternUtils.userPresent(mEffectiveUserId);
@@ -277,40 +276,73 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
} }
protected void reportFailedAttempt() { protected void reportFailedAttempt() {
if (isProfileChallenge()) { updateErrorMessage(
// + 1 for this attempt. mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId) + 1);
updateErrorMessage( mLockPatternUtils.reportFailedPasswordAttempt(mEffectiveUserId);
mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId) + 1);
mLockPatternUtils.reportFailedPasswordAttempt(mEffectiveUserId);
}
} }
protected void updateErrorMessage(int numAttempts) { protected void updateErrorMessage(int numAttempts) {
final int maxAttempts = final int maxAttempts =
mLockPatternUtils.getMaximumFailedPasswordsForWipe(mEffectiveUserId); mLockPatternUtils.getMaximumFailedPasswordsForWipe(mEffectiveUserId);
if (maxAttempts > 0 && numAttempts > 0) { if (maxAttempts <= 0 || numAttempts <= 0) {
int remainingAttempts = maxAttempts - numAttempts; return;
if (remainingAttempts == 1) { }
// Last try
final String title = getActivity().getString( // Update the on-screen error string
R.string.lock_profile_wipe_warning_title); if (mErrorTextView != null) {
LastTryDialog.show(getFragmentManager(), title, getLastTryErrorMessage(), final String message = getActivity().getString(
android.R.string.ok, false /* dismiss */); R.string.lock_failed_attempts_before_wipe, numAttempts, maxAttempts);
} else if (remainingAttempts <= 0) { showError(message, 0);
// Profile is wiped }
LastTryDialog.show(getFragmentManager(), null /* title */,
R.string.lock_profile_wipe_content, R.string.lock_profile_wipe_dismiss, // Only show popup dialog before the last attempt and before wipe
true /* dismiss */); final int remainingAttempts = maxAttempts - numAttempts;
} if (remainingAttempts > 1) {
if (mErrorTextView != null) { return;
final String message = getActivity().getString(R.string.lock_profile_wipe_attempts, }
numAttempts, maxAttempts); final FragmentManager fragmentManager = getChildFragmentManager();
showError(message, 0); final int userType = getUserTypeForWipe();
} if (remainingAttempts == 1) {
// Last try
final String title = getActivity().getString(
R.string.lock_last_attempt_before_wipe_warning_title);
final int messageId = getLastTryErrorMessage(userType);
LastTryDialog.show(fragmentManager, title, messageId,
android.R.string.ok, false /* dismiss */);
} else {
// Device, profile, or secondary user is wiped
final int messageId = getWipeMessage(userType);
LastTryDialog.show(fragmentManager, null /* title */, messageId,
R.string.lock_failed_attempts_now_wiping_dialog_dismiss, true /* dismiss */);
} }
} }
protected abstract int getLastTryErrorMessage(); private int getUserTypeForWipe() {
final UserInfo userToBeWiped = mUserManager.getUserInfo(
mDevicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(mEffectiveUserId));
if (userToBeWiped == null || userToBeWiped.isPrimary()) {
return USER_TYPE_PRIMARY;
} else if (userToBeWiped.isManagedProfile()) {
return USER_TYPE_MANAGED_PROFILE;
} else {
return USER_TYPE_SECONDARY;
}
}
protected abstract int getLastTryErrorMessage(int userType);
private int getWipeMessage(int userType) {
switch (userType) {
case USER_TYPE_PRIMARY:
return R.string.lock_failed_attempts_now_wiping_device;
case USER_TYPE_MANAGED_PROFILE:
return R.string.lock_failed_attempts_now_wiping_profile;
case USER_TYPE_SECONDARY:
return R.string.lock_failed_attempts_now_wiping_user;
default:
throw new IllegalArgumentException("Unrecognized user type:" + userType);
}
}
private final Runnable mResetErrorRunnable = new Runnable() { private final Runnable mResetErrorRunnable = new Runnable() {
@Override @Override
@@ -357,6 +389,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra
DialogFragment dialog = new LastTryDialog(); DialogFragment dialog = new LastTryDialog();
dialog.setArguments(args); dialog.setArguments(args);
dialog.show(from, TAG); dialog.show(from, TAG);
from.executePendingTransactions();
return true; return true;
} }

View File

@@ -211,9 +211,20 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
} }
@Override @Override
protected int getLastTryErrorMessage() { protected int getLastTryErrorMessage(int userType) {
return mIsAlpha ? R.string.lock_profile_wipe_warning_content_password switch (userType) {
: R.string.lock_profile_wipe_warning_content_pin; case USER_TYPE_PRIMARY:
return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_device
: R.string.lock_last_pin_attempt_before_wipe_device;
case USER_TYPE_MANAGED_PROFILE:
return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_profile
: R.string.lock_last_pin_attempt_before_wipe_profile;
case USER_TYPE_SECONDARY:
return mIsAlpha ? R.string.lock_last_password_attempt_before_wipe_user
: R.string.lock_last_pin_attempt_before_wipe_user;
default:
throw new IllegalArgumentException("Unrecognized user type:" + userType);
}
} }
@Override @Override
@@ -278,10 +289,8 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
} else { } else {
resetState(); resetState();
mErrorTextView.setText(""); mErrorTextView.setText("");
if (isProfileChallenge()) { updateErrorMessage(
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts( mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
mEffectiveUserId));
}
} }
mCredentialCheckResultTracker.setListener(this); mCredentialCheckResultTracker.setListener(this);
} }
@@ -444,7 +453,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
mPasswordEntryInputDisabler.setInputEnabled(true); mPasswordEntryInputDisabler.setInputEnabled(true);
if (matched) { if (matched) {
if (newResult) { if (newResult) {
reportSuccessfullAttempt(); reportSuccessfulAttempt();
} }
startDisappearAnimation(intent); startDisappearAnimation(intent);
checkForPendingIntent(); checkForPendingIntent();
@@ -493,10 +502,8 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
public void onFinish() { public void onFinish() {
resetState(); resetState();
mErrorTextView.setText(""); mErrorTextView.setText("");
if (isProfileChallenge()) { updateErrorMessage(
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts( mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
mEffectiveUserId));
}
} }
}.start(); }.start();
} }

View File

@@ -299,10 +299,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
mDetailsTextView.setText(getDefaultDetails()); mDetailsTextView.setText(getDefaultDetails());
} }
mErrorTextView.setText(""); mErrorTextView.setText("");
if (isProfileChallenge()) { updateErrorMessage(
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts( mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
mEffectiveUserId));
}
mLockPatternView.setEnabled(true); mLockPatternView.setEnabled(true);
mLockPatternView.enableInput(); mLockPatternView.enableInput();
@@ -497,7 +495,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
mLockPatternView.setEnabled(true); mLockPatternView.setEnabled(true);
if (matched) { if (matched) {
if (newResult) { if (newResult) {
reportSuccessfullAttempt(); reportSuccessfulAttempt();
} }
startDisappearAnimation(intent); startDisappearAnimation(intent);
checkForPendingIntent(); checkForPendingIntent();
@@ -524,8 +522,17 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
} }
@Override @Override
protected int getLastTryErrorMessage() { protected int getLastTryErrorMessage(int userType) {
return R.string.lock_profile_wipe_warning_content_pattern; switch (userType) {
case USER_TYPE_PRIMARY:
return R.string.lock_last_pattern_attempt_before_wipe_device;
case USER_TYPE_MANAGED_PROFILE:
return R.string.lock_last_pattern_attempt_before_wipe_profile;
case USER_TYPE_SECONDARY:
return R.string.lock_last_pattern_attempt_before_wipe_user;
default:
throw new IllegalArgumentException("Unrecognized user type:" + userType);
}
} }
private void handleAttemptLockout(long elapsedRealtimeDeadline) { private void handleAttemptLockout(long elapsedRealtimeDeadline) {