Merge "Enforce password complexity in lockscreen setting"
This commit is contained in:
@@ -221,9 +221,19 @@ public class ChooseLockGeneric extends SettingsActivity {
|
|||||||
mForBiometrics = intent.getBooleanExtra(
|
mForBiometrics = intent.getBooleanExtra(
|
||||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||||
|
|
||||||
mRequestedMinComplexity = intent
|
final int complexityFromIntent = intent
|
||||||
.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||||
mCallerAppName =
|
final int complexityFromAdmin = mLockPatternUtils.getRequestedPasswordComplexity(
|
||||||
|
mUserId);
|
||||||
|
mRequestedMinComplexity = Math.max(complexityFromIntent, complexityFromAdmin);
|
||||||
|
final boolean isComplexityProvidedByAdmin = (complexityFromAdmin > complexityFromIntent)
|
||||||
|
&& mRequestedMinComplexity > PASSWORD_COMPLEXITY_NONE;
|
||||||
|
|
||||||
|
// If the complexity is provided by the admin, do not get the caller app's name.
|
||||||
|
// If the app requires, for example, low complexity, and the admin requires high
|
||||||
|
// complexity, it does not make sense to show a footer telling the user it's the app
|
||||||
|
// requesting a particular complexity because the admin-set complexity will override it.
|
||||||
|
mCallerAppName = isComplexityProvidedByAdmin ? null :
|
||||||
intent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
|
intent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
|
||||||
mIsCallingAppAdmin = intent
|
mIsCallingAppAdmin = intent
|
||||||
.getBooleanExtra(EXTRA_KEY_IS_CALLING_APP_ADMIN, /* defValue= */ false);
|
.getBooleanExtra(EXTRA_KEY_IS_CALLING_APP_ADMIN, /* defValue= */ false);
|
||||||
@@ -669,8 +679,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
|||||||
final PreferenceScreen entries = getPreferenceScreen();
|
final PreferenceScreen entries = getPreferenceScreen();
|
||||||
|
|
||||||
int adminEnforcedQuality = mDpm.getPasswordQuality(null, mUserId);
|
int adminEnforcedQuality = mDpm.getPasswordQuality(null, mUserId);
|
||||||
EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(
|
EnforcedAdmin enforcedAdmin =
|
||||||
getActivity(), mUserId);
|
RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(getActivity(),
|
||||||
|
mUserId);
|
||||||
// If we are to unify a work challenge at the end of the credential enrollment, manually
|
// If we are to unify a work challenge at the end of the credential enrollment, manually
|
||||||
// merge any password policy from that profile here, so we are enrolling a compliant
|
// merge any password policy from that profile here, so we are enrolling a compliant
|
||||||
// password. This is because once unified, the profile's password policy will
|
// password. This is because once unified, the profile's password policy will
|
||||||
|
@@ -421,6 +421,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
|||||||
if (mUnificationProfileId != UserHandle.USER_NULL) {
|
if (mUnificationProfileId != UserHandle.USER_NULL) {
|
||||||
mMinMetrics.maxWith(
|
mMinMetrics.maxWith(
|
||||||
mLockPatternUtils.getRequestedPasswordMetrics(mUnificationProfileId));
|
mLockPatternUtils.getRequestedPasswordMetrics(mUnificationProfileId));
|
||||||
|
mMinComplexity = Math.max(mMinComplexity,
|
||||||
|
mLockPatternUtils.getRequestedPasswordComplexity(mUnificationProfileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intent.getBooleanExtra(
|
if (intent.getBooleanExtra(
|
||||||
|
@@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
|
|||||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
|
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
|
||||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
|
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
|
||||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
|
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
|
||||||
|
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
|
||||||
|
|
||||||
import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.KEY_LOCK_SETTINGS_FOOTER;
|
import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.KEY_LOCK_SETTINGS_FOOTER;
|
||||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
|
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
|
||||||
@@ -90,6 +91,7 @@ public class ChooseLockGenericTest {
|
|||||||
Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
|
Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
|
||||||
ShadowStorageManager.reset();
|
ShadowStorageManager.reset();
|
||||||
ShadowPersistentDataBlockManager.reset();
|
ShadowPersistentDataBlockManager.reset();
|
||||||
|
ShadowLockPatternUtils.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -377,6 +379,64 @@ public class ChooseLockGenericTest {
|
|||||||
ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL)).isNotNull();
|
ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL)).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updatePreferencesOrFinish_ComplexityIsReadFromDPM() {
|
||||||
|
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||||
|
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
|
||||||
|
|
||||||
|
initActivity(null);
|
||||||
|
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||||
|
|
||||||
|
FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
|
||||||
|
assertThat(footer.getTitle()).isEqualTo(null);
|
||||||
|
|
||||||
|
Intent intent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
|
||||||
|
assertThat(intent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
|
||||||
|
PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updatePreferencesOrFinish_ComplexityIsMergedWithDPM() {
|
||||||
|
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||||
|
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
|
||||||
|
Intent intent = new Intent()
|
||||||
|
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
|
||||||
|
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
|
||||||
|
initActivity(intent);
|
||||||
|
|
||||||
|
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||||
|
|
||||||
|
// Footer should be null because admin complexity wins.
|
||||||
|
FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
|
||||||
|
assertThat(footer.getTitle()).isEqualTo(null);
|
||||||
|
|
||||||
|
Intent passwordIntent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
|
||||||
|
assertThat(passwordIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
|
||||||
|
PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updatePreferencesOrFinish_ComplexityIsMergedWithDPM_AppIsHigher() {
|
||||||
|
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||||
|
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
|
||||||
|
Intent intent = new Intent()
|
||||||
|
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
|
||||||
|
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
|
||||||
|
initActivity(intent);
|
||||||
|
|
||||||
|
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||||
|
|
||||||
|
// Footer should include app name because app requirement is higher.
|
||||||
|
CharSequence expectedTitle =
|
||||||
|
mActivity.getString(R.string.unlock_footer_high_complexity_requested, "app name");
|
||||||
|
FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
|
||||||
|
assertThat(footer.getTitle()).isEqualTo(expectedTitle);
|
||||||
|
|
||||||
|
Intent passwordIntent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
|
||||||
|
assertThat(passwordIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
|
||||||
|
PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
private void initActivity(@Nullable Intent intent) {
|
private void initActivity(@Nullable Intent intent) {
|
||||||
if (intent == null) {
|
if (intent == null) {
|
||||||
intent = new Intent();
|
intent = new Intent();
|
||||||
|
@@ -31,6 +31,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
|
|||||||
import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY;
|
import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY;
|
||||||
import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS;
|
import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS;
|
||||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||||
|
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
import static com.google.common.truth.Truth.assertWithMessage;
|
||||||
@@ -67,6 +68,7 @@ import org.robolectric.shadows.ShadowDrawable;
|
|||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@Config(shadows = {
|
@Config(shadows = {
|
||||||
SettingsShadowResources.class,
|
SettingsShadowResources.class,
|
||||||
|
ShadowLockPatternUtils.class,
|
||||||
ShadowUtils.class,
|
ShadowUtils.class,
|
||||||
ShadowDevicePolicyManager.class,
|
ShadowDevicePolicyManager.class,
|
||||||
})
|
})
|
||||||
@@ -84,6 +86,7 @@ public class ChooseLockPasswordTest {
|
|||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
SettingsShadowResources.reset();
|
SettingsShadowResources.reset();
|
||||||
|
ShadowLockPatternUtils.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -378,6 +381,29 @@ public class ChooseLockPasswordTest {
|
|||||||
assertThat(drawable.getCreatedFromResId()).isNotEqualTo(R.drawable.ic_fingerprint_header);
|
assertThat(drawable.getCreatedFromResId()).isNotEqualTo(R.drawable.ic_fingerprint_header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateComplexityMergedFromDpmOnCreate() {
|
||||||
|
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
|
||||||
|
|
||||||
|
assertPasswordValidationResult(
|
||||||
|
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||||
|
/* passwordType= */ PASSWORD_QUALITY_NUMERIC,
|
||||||
|
/* userEnteredPassword= */ LockscreenCredential.createNone(),
|
||||||
|
"PIN must be at least 8 digits");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateComplexityMergedFromUnificationUserOnCreate() {
|
||||||
|
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
|
||||||
|
ShadowLockPatternUtils.setRequiredPasswordComplexity(123, PASSWORD_COMPLEXITY_HIGH);
|
||||||
|
|
||||||
|
Intent intent = createIntentForPasswordValidation(PASSWORD_COMPLEXITY_NONE,
|
||||||
|
PASSWORD_QUALITY_NUMERIC);
|
||||||
|
intent.putExtra(EXTRA_KEY_UNIFICATION_PROFILE_ID, 123);
|
||||||
|
assertPasswordValidationResultForIntent(LockscreenCredential.createNone(), intent,
|
||||||
|
"PIN must be at least 8 digits");
|
||||||
|
}
|
||||||
|
|
||||||
private ChooseLockPassword buildChooseLockPasswordActivity(Intent intent) {
|
private ChooseLockPassword buildChooseLockPasswordActivity(Intent intent) {
|
||||||
return Robolectric.buildActivity(ChooseLockPassword.class, intent).setup().get();
|
return Robolectric.buildActivity(ChooseLockPassword.class, intent).setup().get();
|
||||||
}
|
}
|
||||||
@@ -400,14 +426,27 @@ public class ChooseLockPasswordTest {
|
|||||||
private void assertPasswordValidationResult(@PasswordComplexity int minComplexity,
|
private void assertPasswordValidationResult(@PasswordComplexity int minComplexity,
|
||||||
int passwordType, LockscreenCredential userEnteredPassword,
|
int passwordType, LockscreenCredential userEnteredPassword,
|
||||||
String... expectedValidationResult) {
|
String... expectedValidationResult) {
|
||||||
Intent intent = new Intent();
|
Intent intent = createIntentForPasswordValidation(minComplexity, passwordType);
|
||||||
intent.putExtra(CONFIRM_CREDENTIALS, false);
|
assertPasswordValidationResultForIntent(userEnteredPassword, intent,
|
||||||
intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
|
expectedValidationResult);
|
||||||
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
|
}
|
||||||
|
|
||||||
|
private void assertPasswordValidationResultForIntent(LockscreenCredential userEnteredPassword,
|
||||||
|
Intent intent, String... expectedValidationResult) {
|
||||||
ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
|
ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
|
||||||
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
|
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
|
||||||
fragment.validatePassword(userEnteredPassword);
|
fragment.validatePassword(userEnteredPassword);
|
||||||
String[] messages = fragment.convertErrorCodeToMessages();
|
String[] messages = fragment.convertErrorCodeToMessages();
|
||||||
assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
|
assertThat(messages).asList().containsExactly(expectedValidationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent createIntentForPasswordValidation(
|
||||||
|
@PasswordComplexity int minComplexity,
|
||||||
|
int passwordType) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(CONFIRM_CREDENTIALS, false);
|
||||||
|
intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
|
||||||
|
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
|
||||||
|
return intent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,19 +18,31 @@ package com.android.settings.testutils.shadow;
|
|||||||
|
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
import com.android.internal.widget.LockscreenCredential;
|
import com.android.internal.widget.LockscreenCredential;
|
||||||
|
|
||||||
import org.robolectric.annotation.Implementation;
|
import org.robolectric.annotation.Implementation;
|
||||||
import org.robolectric.annotation.Implements;
|
import org.robolectric.annotation.Implements;
|
||||||
|
import org.robolectric.annotation.Resetter;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Implements(LockPatternUtils.class)
|
@Implements(LockPatternUtils.class)
|
||||||
public class ShadowLockPatternUtils {
|
public class ShadowLockPatternUtils {
|
||||||
|
|
||||||
private static boolean sDeviceEncryptionEnabled;
|
private static boolean sDeviceEncryptionEnabled;
|
||||||
|
private static Map<Integer, Integer> sUserToComplexityMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
@Resetter
|
||||||
|
public static void reset() {
|
||||||
|
sUserToComplexityMap.clear();
|
||||||
|
sDeviceEncryptionEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
protected boolean hasSecureLockScreen() {
|
protected boolean hasSecureLockScreen() {
|
||||||
@@ -76,4 +88,18 @@ public class ShadowLockPatternUtils {
|
|||||||
protected boolean checkPasswordHistory(byte[] passwordToCheck, byte[] hashFactor, int userId) {
|
protected boolean checkPasswordHistory(byte[] passwordToCheck, byte[] hashFactor, int userId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) {
|
||||||
|
return sUserToComplexityMap.getOrDefault(userId,
|
||||||
|
DevicePolicyManager.PASSWORD_COMPLEXITY_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setRequiredPasswordComplexity(int userId, int complexity) {
|
||||||
|
sUserToComplexityMap.put(userId, complexity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setRequiredPasswordComplexity(int complexity) {
|
||||||
|
setRequiredPasswordComplexity(UserHandle.myUserId(), complexity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user