diff --git a/res/values/strings.xml b/res/values/strings.xml index ac239f61c96..2f9652ec998 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1311,16 +1311,16 @@ Choose screen lock - + Choose a screen lock - + Choose a new screen lock - + Choose a lock for work apps - + Choose a new work lock diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java index eede22ac868..97cda4b5118 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java +++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java @@ -298,9 +298,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity { private void launchChooseLock() { Log.d(TAG, "launchChooseLock"); Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent()); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true); + intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true); @@ -350,8 +348,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity { final Intent intent; // If only device credential was specified, ask the user to only set that up. intent = new Intent(this, ChooseLockGeneric.class); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); launchEnrollActivity(intent); } diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java index b44c2c9da1a..c34b2843706 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java @@ -222,9 +222,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase private void launchChooseLock() { Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent()); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true); + intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); intent.putExtra(getExtraKeyForBiometric(), true); if (mUserId != UserHandle.USER_NULL) { diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java index c449ff4bfce..b5f6ef328a6 100644 --- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java +++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java @@ -19,7 +19,6 @@ import static android.app.Activity.RESULT_OK; import static com.android.settings.password.ChooseLockPattern.RESULT_FINISHED; -import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.hardware.face.FaceManager; @@ -193,9 +192,8 @@ public abstract class BiometricsSettingsBase extends DashboardFragment { if (!launched) { Intent intent = BiometricUtils.getChooseLockIntent(getActivity(), getIntent()); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true); + intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, + true); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index a328f21e578..dbac4150380 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -685,9 +685,7 @@ public class FingerprintSettings extends SubSettings { // TODO: This should be cleaned up. ChooseLockGeneric should provide a way of // specifying arguments/requests, instead of relying on callers setting extras. intent.setClassName(SETTINGS_PACKAGE_NAME, ChooseLockGeneric.class.getName()); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, + intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); intent.putExtra(Intent.EXTRA_USER_ID, mUserId); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index 67fc7efc67c..a1826ba0cbc 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -75,8 +75,6 @@ import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.search.SearchFeatureProvider; -import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; -import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedPreference; import com.google.android.setupcompat.util.WizardManagerHelper; @@ -112,8 +110,7 @@ public class ChooseLockGeneric extends SettingsActivity { private static final String KEY_SKIP_BIOMETRICS = "unlock_skip_biometrics"; private static final String PASSWORD_CONFIRMED = "password_confirmed"; private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation"; - public static final String MINIMUM_QUALITY_KEY = "minimum_quality"; - public static final String HIDE_DISABLED_PREFS = "hide_disabled_prefs"; + public static final String HIDE_INSECURE_OPTIONS = "hide_insecure_options"; public static final String TAG_FRP_WARNING_DIALOG = "frp_warning_dialog"; public static final String KEY_LOCK_SETTINGS_FOOTER ="lock_settings_footer"; @@ -277,21 +274,20 @@ public class ChooseLockGeneric extends SettingsActivity { arguments, intent.getExtras()).getIdentifier(); mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mUserId); - mController = new ChooseLockGenericController( - getContext(), mUserId, mRequestedMinComplexity, - mOnlyEnforceDevicePasswordRequirement, - mLockPatternUtils); - - final int aggregatedComplexity = mController.getAggregatedPasswordComplexity(); - final boolean isComplexityProvidedByAdmin = - aggregatedComplexity > mRequestedMinComplexity - && aggregatedComplexity > PASSWORD_COMPLEXITY_NONE; + mController = new ChooseLockGenericController.Builder( + getContext(), mUserId, mLockPatternUtils) + .setAppRequestedMinComplexity(mRequestedMinComplexity) + .setEnforceDevicePasswordRequirementOnly(mOnlyEnforceDevicePasswordRequirement) + .setProfileToUnify(mUnificationProfileId) + .setHideInsecureScreenLockTypes(alwaysHideInsecureScreenLockTypes() + || intent.getBooleanExtra(HIDE_INSECURE_OPTIONS, false)) + .build(); // 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 : + mCallerAppName = mController.isComplexityProvidedByAdmin() ? null : intent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME); mManagedPasswordProvider = ManagedLockPasswordProvider.get(activity, mUserId); @@ -330,6 +326,10 @@ public class ChooseLockGeneric extends SettingsActivity { return super.onCreateView(inflater, container, savedInstanceState); } + protected boolean alwaysHideInsecureScreenLockTypes() { + return false; + } + private void updateActivityTitle() { if (mLockPatternUtils == null) { // mLockPatternUtils will be uninitialized if ChooseLockGenericFragment.onCreate() @@ -606,16 +606,12 @@ public class ChooseLockGeneric extends SettingsActivity { } if (quality == -1) { // If caller didn't specify password quality, show UI and allow the user to choose. - quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1); - quality = mController.upgradeQuality(quality); - final boolean hideDisabledPrefs = intent.getBooleanExtra( - HIDE_DISABLED_PREFS, false); final PreferenceScreen prefScreen = getPreferenceScreen(); if (prefScreen != null) { prefScreen.removeAll(); } addPreferences(); - disableUnusablePreferences(quality, hideDisabledPrefs); + disableUnusablePreferences(); updatePreferenceText(); updateCurrentPreference(); updatePreferenceSummaryIfNeeded(); @@ -746,71 +742,23 @@ public class ChooseLockGeneric extends SettingsActivity { return lock != null ? lock.preferenceKey : null; } - /*** - * Disables preferences that are less secure than required quality. The actual - * implementation is in disableUnusablePreferenceImpl. - * - * @param quality the requested quality. - * @param hideDisabledPrefs if false preferences show why they were disabled; otherwise - * they're not shown at all. - */ - protected void disableUnusablePreferences(final int quality, boolean hideDisabledPrefs) { - disableUnusablePreferencesImpl(quality, hideDisabledPrefs); - } - /*** * Disables preferences that are less secure than required quality. * - * @param quality the requested quality. - * @param hideDisabled whether to hide disable screen lock options. */ - protected void disableUnusablePreferencesImpl(final int quality, - boolean hideDisabled) { + private void disableUnusablePreferences() { final PreferenceScreen entries = getPreferenceScreen(); - int adminEnforcedQuality = LockPatternUtils.credentialTypeToPasswordQuality( - mLockPatternUtils.getRequestedPasswordMetrics( - mUserId, mOnlyEnforceDevicePasswordRequirement).credType); - EnforcedAdmin enforcedAdmin = - RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(getActivity(), - mUserId); - // 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 - // password. This is because once unified, the profile's password policy will - // be enforced on the new credential. - if (mUnificationProfileId != UserHandle.USER_NULL) { - int profileEnforceQuality = mDpm.getPasswordQuality(null, mUnificationProfileId); - if (profileEnforceQuality > adminEnforcedQuality) { - adminEnforcedQuality = profileEnforceQuality; - enforcedAdmin = EnforcedAdmin.combine(enforcedAdmin, - RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet( - getActivity(), mUnificationProfileId)); - } - } - for (ScreenLockType lock : ScreenLockType.values()) { String key = lock.preferenceKey; Preference pref = findPreference(key); if (pref instanceof RestrictedPreference) { boolean visible = mController.isScreenLockVisible(lock); - boolean enabled = mController.isScreenLockEnabled(lock, quality); - boolean disabledByAdmin = - mController.isScreenLockDisabledByAdmin(lock, adminEnforcedQuality); - if (hideDisabled) { - visible = visible && enabled; - } + boolean enabled = mController.isScreenLockEnabled(lock); if (!visible) { entries.removePreference(pref); - } else if (disabledByAdmin && enforcedAdmin != null) { - ((RestrictedPreference) pref).setDisabledByAdmin(enforcedAdmin); } else if (!enabled) { - // we need to setDisabledByAdmin to null first to disable the padlock - // in case it was set earlier. - ((RestrictedPreference) pref).setDisabledByAdmin(null); - pref.setSummary(R.string.unlock_set_unlock_disabled_summary); pref.setEnabled(false); - } else { - ((RestrictedPreference) pref).setDisabledByAdmin(null); } } } diff --git a/src/com/android/settings/password/ChooseLockGenericController.java b/src/com/android/settings/password/ChooseLockGenericController.java index 6900e3d0f60..1b951d4f968 100644 --- a/src/com/android/settings/password/ChooseLockGenericController.java +++ b/src/com/android/settings/password/ChooseLockGenericController.java @@ -17,6 +17,7 @@ package com.android.settings.password; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; @@ -24,6 +25,7 @@ import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.app.admin.PasswordMetrics; import android.content.Context; import android.os.UserHandle; +import android.os.UserManager; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -36,84 +38,140 @@ import java.util.List; /** * A controller for ChooseLockGeneric, and other similar classes which shows a list of possible - * screen locks for the user to choose from. + * screen lock types for the user to choose from. This is the main place where different + * restrictions on allowed screen lock types are aggregated in Settings. + * + * Each screen lock type has two states: whether it is visible and whether it is enabled. + * Visibility is affected by things like resource configs, whether it's for a managed profile, + * or whether the caller allows it or not. This is determined by + * {@link #isScreenLockVisible(ScreenLockType)}. For visible screen lock types, they can be disabled + * by a combination of admin policies and request from the calling app, which is determined by + * {@link #isScreenLockEnabled(ScreenLockType}. */ + public class ChooseLockGenericController { private final Context mContext; private final int mUserId; - @PasswordComplexity private final int mRequestedMinComplexity; + private final boolean mHideInsecureScreenLockTypes; + @PasswordComplexity private final int mAppRequestedMinComplexity; private final boolean mDevicePasswordRequirementOnly; - private ManagedLockPasswordProvider mManagedPasswordProvider; + private final int mUnificationProfileId; + private final ManagedLockPasswordProvider mManagedPasswordProvider; private final LockPatternUtils mLockPatternUtils; - public ChooseLockGenericController(Context context, int userId) { - this( - context, - userId, - PASSWORD_COMPLEXITY_NONE, - /* mOnlyEnforceDevicePasswordRequirement */ false, - new LockPatternUtils(context)); - } - - /** - * @param requestedMinComplexity specifies the min password complexity to be taken into account - * when determining the available screen lock types - */ public ChooseLockGenericController(Context context, int userId, - @PasswordComplexity int requestedMinComplexity, - boolean devicePasswordRequirementOnly, - LockPatternUtils lockPatternUtils) { - this( - context, - userId, - requestedMinComplexity, - devicePasswordRequirementOnly, - ManagedLockPasswordProvider.get(context, userId), - lockPatternUtils); - } - - @VisibleForTesting - ChooseLockGenericController( - Context context, - int userId, - @PasswordComplexity int requestedMinComplexity, - boolean devicePasswordRequirementOnly, - ManagedLockPasswordProvider managedLockPasswordProvider, - LockPatternUtils lockPatternUtils) { + ManagedLockPasswordProvider managedPasswordProvider, LockPatternUtils lockPatternUtils, + boolean hideInsecureScreenLockTypes, int appRequestedMinComplexity, + boolean devicePasswordRequirementOnly, int unificationProfileId) { mContext = context; mUserId = userId; - mRequestedMinComplexity = requestedMinComplexity; - mDevicePasswordRequirementOnly = devicePasswordRequirementOnly; - mManagedPasswordProvider = managedLockPasswordProvider; + mManagedPasswordProvider = managedPasswordProvider; mLockPatternUtils = lockPatternUtils; + mHideInsecureScreenLockTypes = hideInsecureScreenLockTypes; + mAppRequestedMinComplexity = appRequestedMinComplexity; + mDevicePasswordRequirementOnly = devicePasswordRequirementOnly; + mUnificationProfileId = unificationProfileId; + } + + /** Builder class for {@link ChooseLockGenericController} */ + public static class Builder { + private final Context mContext; + private final int mUserId; + private final ManagedLockPasswordProvider mManagedPasswordProvider; + private final LockPatternUtils mLockPatternUtils; + + private boolean mHideInsecureScreenLockTypes = false; + @PasswordComplexity private int mAppRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE; + private boolean mDevicePasswordRequirementOnly = false; + private int mUnificationProfileId = UserHandle.USER_NULL; + + public Builder(Context context, int userId) { + this(context, userId, new LockPatternUtils(context)); + } + + public Builder(Context context, int userId, + LockPatternUtils lockPatternUtils) { + this( + context, + userId, + ManagedLockPasswordProvider.get(context, userId), + lockPatternUtils); + } + + @VisibleForTesting + Builder( + Context context, + int userId, + ManagedLockPasswordProvider managedLockPasswordProvider, + LockPatternUtils lockPatternUtils) { + mContext = context; + mUserId = userId; + mManagedPasswordProvider = managedLockPasswordProvider; + mLockPatternUtils = lockPatternUtils; + } + /** + * Sets the password complexity requested by the calling app via + * {@link android.app.admin.DevicePolicyManager#EXTRA_PASSWORD_COMPLEXITY}. + */ + public Builder setAppRequestedMinComplexity(int complexity) { + mAppRequestedMinComplexity = complexity; + return this; + } + + /** + * Sets whether the enrolment flow should discard any password policies originating from the + * work profile, even if the work profile currently has unified challenge. This can be + * requested by the calling app via + * {@link android.app.admin.DevicePolicyManager#EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY}. + */ + public Builder setEnforceDevicePasswordRequirementOnly(boolean deviceOnly) { + mDevicePasswordRequirementOnly = deviceOnly; + return this; + } + + /** + * Sets the user ID of any profile whose work challenge should be unified at the end of this + * enrolment flow. This will lead to all password policies from that profile to be taken + * into consideration by this class, so that we are enrolling a compliant password. This is + * because once unified, the profile's password policy will be enforced on the new + * credential. + */ + public Builder setProfileToUnify(int profileId) { + mUnificationProfileId = profileId; + return this; + } + + /** + * Sets whether insecure screen lock types (NONE and SWIPE) should be hidden in the UI. + */ + public Builder setHideInsecureScreenLockTypes(boolean hide) { + mHideInsecureScreenLockTypes = hide; + return this; + } + + /** Creates {@link ChooseLockGenericController} instance. */ + public ChooseLockGenericController build() { + return new ChooseLockGenericController(mContext, mUserId, mManagedPasswordProvider, + mLockPatternUtils, mHideInsecureScreenLockTypes, mAppRequestedMinComplexity, + mDevicePasswordRequirementOnly, mUnificationProfileId); + } } /** - * Returns the highest quality among the specified {@code quality}, the password requirement - * set by device admins (legacy password quality metrics and password complexity), and the - * min password complexity requested by the calling app. - */ - public int upgradeQuality(int quality) { - // Compare specified quality and dpm quality - // TODO(b/142781408): convert from quality to credential type once PIN is supported. - int dpmUpgradedQuality = Math.max(quality, LockPatternUtils.credentialTypeToPasswordQuality( - getAggregatedPasswordMetrics().credType)); - return Math.max(dpmUpgradedQuality, - PasswordMetrics.complexityLevelToMinQuality(getAggregatedPasswordComplexity())); - } - - /** - * Whether the given screen lock type should be visible in the given context. + * Returns whether the given screen lock type should be visible in the given context. */ public boolean isScreenLockVisible(ScreenLockType type) { - final boolean managedProfile = mUserId != UserHandle.myUserId(); + final boolean managedProfile = mContext.getSystemService(UserManager.class) + .isManagedProfile(mUserId); switch (type) { case NONE: - return !mContext.getResources().getBoolean(R.bool.config_hide_none_security_option) + return !mHideInsecureScreenLockTypes + && !mContext.getResources().getBoolean(R.bool.config_hide_none_security_option) && !managedProfile; // Profiles should use unified challenge instead. case SWIPE: - return !mContext.getResources().getBoolean(R.bool.config_hide_swipe_security_option) + return !mHideInsecureScreenLockTypes + && !mContext.getResources().getBoolean(R.bool.config_hide_swipe_security_option) && !managedProfile; // Swipe doesn't make sense for profiles. case MANAGED: return mManagedPasswordProvider.isManagedPasswordChoosable(); @@ -128,29 +186,27 @@ public class ChooseLockGenericController { } /** - * Whether screen lock with {@code type} should be enabled. - * - * @param type The screen lock type. - * @param quality The minimum required quality. This can either be requirement by device policy - * manager or because some flow only makes sense with secure lock screens. + * Whether screen lock with {@code type} should be enabled assuming all relevant password + * requirements. The lock's visibility ({@link #isScreenLockVisible}) is not considered here. */ - public boolean isScreenLockEnabled(ScreenLockType type, int quality) { - return type.maxQuality >= quality; + public boolean isScreenLockEnabled(ScreenLockType type) { + return type.maxQuality >= upgradeQuality(PASSWORD_QUALITY_UNSPECIFIED); } /** - * Whether screen lock with {@code type} is disabled by device policy admin. - * - * @param type The screen lock type. - * @param adminEnforcedQuality The minimum quality that the admin enforces. + * Increases the given quality to be as high as the combined quality from all relevant + * password requirements. */ - public boolean isScreenLockDisabledByAdmin(ScreenLockType type, int adminEnforcedQuality) { - boolean disabledByAdmin = type.maxQuality < adminEnforcedQuality; - if (type == ScreenLockType.MANAGED) { - disabledByAdmin = disabledByAdmin - || !mManagedPasswordProvider.isManagedPasswordChoosable(); - } - return disabledByAdmin; + // TODO(b/142781408): convert from quality to credential type once PIN is supported. + public int upgradeQuality(int quality) { + return Math.max(quality, + Math.max( + LockPatternUtils.credentialTypeToPasswordQuality( + getAggregatedPasswordMetrics().credType), + PasswordMetrics.complexityLevelToMinQuality( + getAggregatedPasswordComplexity()) + ) + ); } /** @@ -175,43 +231,72 @@ public class ChooseLockGenericController { } /** - * Gets a list of screen locks that should be visible for the given quality. The returned list - * is ordered in the natural order of the enum (the order those enums were defined). - * - * @param quality The minimum quality required in the context of the current flow. This should - * be one of the constants defined in - * {@code DevicePolicyManager#PASSWORD_QUALITY_*}. - * @param includeDisabled Whether to include screen locks disabled by {@code quality} - * requirements in the returned list. + * Gets a list of screen lock types that should be visible for the given quality. The returned + * list is ordered in the natural order of the enum (the order those enums were defined). Screen + * locks disabled by password policy will not be returned. */ @NonNull - public List getVisibleScreenLockTypes(int quality, boolean includeDisabled) { - int upgradedQuality = upgradeQuality(quality); + public List getVisibleAndEnabledScreenLockTypes() { List locks = new ArrayList<>(); // EnumSet's iterator guarantees the natural order of the enums for (ScreenLockType lock : ScreenLockType.values()) { - if (isScreenLockVisible(lock)) { - if (includeDisabled || isScreenLockEnabled(lock, upgradedQuality)) { - locks.add(lock); - } + if (isScreenLockVisible(lock) && isScreenLockEnabled(lock)) { + locks.add(lock); } } return locks; } + /** + * Returns the combined password metrics from all relevant policies which affects the current + * user. Normally password policies set on the current user's work profile instance will be + * taken into consideration here iff the work profile doesn't have its own work challenge. + * By setting {@link #mUnificationProfileId}, the work profile's password policy will always + * be combined here. Alternatively, by setting {@link #mDevicePasswordRequirementOnly}, its + * password policy will always be disregarded here. + */ public PasswordMetrics getAggregatedPasswordMetrics() { - return mLockPatternUtils.getRequestedPasswordMetrics(mUserId, + PasswordMetrics metrics = mLockPatternUtils.getRequestedPasswordMetrics(mUserId, mDevicePasswordRequirementOnly); + if (mUnificationProfileId != UserHandle.USER_NULL) { + metrics.maxWith(mLockPatternUtils.getRequestedPasswordMetrics(mUnificationProfileId)); + } + return metrics; } + /** + * Returns the combined password complexity from all relevant policies which affects the current + * user. The same logic of handling work profile password policies as + * {@link #getAggregatedPasswordMetrics} applies here. + */ public int getAggregatedPasswordComplexity() { - return Math.max(mRequestedMinComplexity, + int complexity = Math.max(mAppRequestedMinComplexity, mLockPatternUtils.getRequestedPasswordComplexity( mUserId, mDevicePasswordRequirementOnly)); + if (mUnificationProfileId != UserHandle.USER_NULL) { + complexity = Math.max(complexity, + mLockPatternUtils.getRequestedPasswordComplexity(mUnificationProfileId)); + } + return complexity; } + /** + * Returns whether any screen lock type has been disabled only due to password policy + * from the admin. Will return {@code false} if the restriction is purely due to calling + * app's request. + */ public boolean isScreenLockRestrictedByAdmin() { return getAggregatedPasswordMetrics().credType != CREDENTIAL_TYPE_NONE - || getAggregatedPasswordComplexity() != PASSWORD_COMPLEXITY_NONE; + || isComplexityProvidedByAdmin(); + } + + /** + * Returns whether the aggregated password complexity is non-zero and comes from + * admin policy. + */ + public boolean isComplexityProvidedByAdmin() { + final int aggregatedComplexity = getAggregatedPasswordComplexity(); + return aggregatedComplexity > mAppRequestedMinComplexity + && aggregatedComplexity > PASSWORD_COMPLEXITY_NONE; } } diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index de377a73d67..6382abf9dbf 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -416,19 +416,6 @@ public class ChooseLockPassword extends SettingsActivity { mMinComplexity = intent.getIntExtra(EXTRA_KEY_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE); mMinMetrics = intent.getParcelableExtra(EXTRA_KEY_MIN_METRICS); if (mMinMetrics == null) mMinMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); - // 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 - // password. This is because once unified, the profile's password policy will - // be enforced on the new credential. - //TODO: Move this logic to ChooseLockGeneric; let ChooseLockGeneric be the only place - //where password requirement mixing happens. ChooseLockPassword simply enforces what's - //set via IntentBuilder.setPasswordRequirement() - if (mUnificationProfileId != UserHandle.USER_NULL) { - mMinMetrics.maxWith( - mLockPatternUtils.getRequestedPasswordMetrics(mUnificationProfileId)); - mMinComplexity = Math.max(mMinComplexity, - mLockPatternUtils.getRequestedPasswordComplexity(mUnificationProfileId)); - } if (intent.getBooleanExtra( ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, false)) { diff --git a/src/com/android/settings/password/ChooseLockTypeDialogFragment.java b/src/com/android/settings/password/ChooseLockTypeDialogFragment.java index 8bc29763e2c..6ec70574b11 100644 --- a/src/com/android/settings/password/ChooseLockTypeDialogFragment.java +++ b/src/com/android/settings/password/ChooseLockTypeDialogFragment.java @@ -18,7 +18,6 @@ package com.android.settings.password; import android.app.Activity; import android.app.Dialog; -import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; @@ -100,7 +99,9 @@ public class ChooseLockTypeDialogFragment extends InstrumentedDialogFragment public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final int userId = getArguments().getInt(ARG_USER_ID); - mController = new ChooseLockGenericController(getContext(), userId); + mController = new ChooseLockGenericController.Builder(getContext(), userId) + .setHideInsecureScreenLockTypes(true) + .build(); } @Override @@ -124,10 +125,7 @@ public class ChooseLockTypeDialogFragment extends InstrumentedDialogFragment public Dialog onCreateDialog(Bundle savedInstanceState) { Context context = getContext(); Builder builder = new Builder(context); - List locks = - mController.getVisibleScreenLockTypes( - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, - false /* includeDisabled */); + List locks = mController.getVisibleAndEnabledScreenLockTypes(); mAdapter = new ScreenLockAdapter(context, locks, mController); builder.setAdapter(mAdapter, this); builder.setTitle(R.string.setup_lock_settings_options_dialog_title); diff --git a/src/com/android/settings/password/SetNewPasswordController.java b/src/com/android/settings/password/SetNewPasswordController.java index 03efa605d53..ef4ff75497c 100644 --- a/src/com/android/settings/password/SetNewPasswordController.java +++ b/src/com/android/settings/password/SetNewPasswordController.java @@ -19,7 +19,6 @@ package com.android.settings.password; import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static com.android.internal.util.Preconditions.checkNotNull; @@ -145,10 +144,8 @@ final class SetNewPasswordController { private Bundle getBiometricChooseLockExtras() { Bundle chooseLockExtras = new Bundle(); - chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - PASSWORD_QUALITY_SOMETHING); chooseLockExtras.putBoolean( - ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true); + ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true); return chooseLockExtras; @@ -156,10 +153,8 @@ final class SetNewPasswordController { private Bundle getFingerprintChooseLockExtras() { Bundle chooseLockExtras = new Bundle(); - chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - PASSWORD_QUALITY_SOMETHING); chooseLockExtras.putBoolean( - ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true); + ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true); return chooseLockExtras; @@ -167,10 +162,8 @@ final class SetNewPasswordController { private Bundle getFaceChooseLockExtras() { Bundle chooseLockExtras = new Bundle(); - chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - PASSWORD_QUALITY_SOMETHING); chooseLockExtras.putBoolean( - ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true); + ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true); chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, true); return chooseLockExtras; diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java index 7818f0d2fc9..15a90e3fdbf 100644 --- a/src/com/android/settings/password/SetupChooseLockGeneric.java +++ b/src/com/android/settings/password/SetupChooseLockGeneric.java @@ -21,7 +21,6 @@ import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY; -import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -160,22 +159,12 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric { return SetupChooseLockGeneric.InternalActivity.class; } - /*** - * Disables preferences that are less secure than required quality and shows only secure - * screen lock options here. - * - * @param quality the requested quality. - */ @Override - protected void disableUnusablePreferences(final int quality, boolean hideDisabled) { + protected boolean alwaysHideInsecureScreenLockTypes() { // At this part of the flow, the user has already indicated they want to add a pin, - // pattern or password, so don't show "None" or "Slide". We disable them here and set - // the HIDE_DISABLED flag to true to hide them. This only happens for setup wizard. - // We do the following max check here since the device may already have a Device Admin - // installed with a policy we need to honor. - final int newQuality = Math.max(quality, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - super.disableUnusablePreferencesImpl(newQuality, true /* hideDisabled */); + // pattern or password, so don't show "None" or "Slide". We disable them here. + // This only happens for setup wizard. + return true; } @Override diff --git a/src/com/android/settings/password/SetupChooseLockPassword.java b/src/com/android/settings/password/SetupChooseLockPassword.java index 25f5a348904..7cf90c09929 100644 --- a/src/com/android/settings/password/SetupChooseLockPassword.java +++ b/src/com/android/settings/password/SetupChooseLockPassword.java @@ -17,7 +17,6 @@ package com.android.settings.password; import android.app.Activity; -import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -81,9 +80,11 @@ public class SetupChooseLockPassword extends ChooseLockPassword { super.onViewCreated(view, savedInstanceState); final Activity activity = getActivity(); ChooseLockGenericController chooseLockGenericController = - new ChooseLockGenericController(activity, mUserId); - boolean anyOptionsShown = chooseLockGenericController.getVisibleScreenLockTypes( - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false).size() > 0; + new ChooseLockGenericController.Builder(activity, mUserId) + .setHideInsecureScreenLockTypes(true) + .build(); + boolean anyOptionsShown = chooseLockGenericController + .getVisibleAndEnabledScreenLockTypes().size() > 0; boolean showOptionsButton = activity.getIntent().getBooleanExtra( ChooseLockGeneric.ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, false); if (!anyOptionsShown) { diff --git a/src/com/android/settings/security/ScreenPinningSettings.java b/src/com/android/settings/security/ScreenPinningSettings.java index 3fa098b826a..e219b44084d 100644 --- a/src/com/android/settings/security/ScreenPinningSettings.java +++ b/src/com/android/settings/security/ScreenPinningSettings.java @@ -136,8 +136,8 @@ public class ScreenPinningSettings extends SettingsPreferenceFragment if (passwordQuality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); chooseLockIntent.putExtra( - ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, + true); startActivityForResult(chooseLockIntent, CHANGE_LOCK_METHOD_REQUEST); return false; } diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index d88d8b5c316..5f14399a26b 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -432,8 +432,8 @@ public class UserSettings extends SettingsPreferenceFragment private void launchChooseLockscreen() { Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); - chooseLockIntent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + chooseLockIntent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, + true); startActivityForResult(chooseLockIntent, REQUEST_CHOOSE_LOCK); } diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java index 02b89cba845..049a34969c1 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java @@ -20,23 +20,31 @@ 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_MEDIUM; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import static org.robolectric.RuntimeEnvironment.application; import android.app.admin.DevicePolicyManager; -import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.app.admin.PasswordPolicy; +import android.os.UserHandle; import com.android.internal.widget.LockPatternUtils; import com.android.settings.R; import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settings.testutils.shadow.ShadowUserManager; import org.junit.After; import org.junit.Before; @@ -48,10 +56,11 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.util.Arrays; +import java.util.Set; import java.util.regex.Pattern; @RunWith(RobolectricTestRunner.class) -@Config(shadows = SettingsShadowResources.class) +@Config(shadows = {ShadowUserManager.class, SettingsShadowResources.class}) public class ChooseLockGenericControllerTest { private ChooseLockGenericController mController; @@ -68,7 +77,7 @@ public class ChooseLockGenericControllerTest { when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(true); setDevicePolicyPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); - mController = createController(PASSWORD_COMPLEXITY_NONE); + mController = createBuilder().build(); SettingsShadowResources.overrideResource(R.bool.config_hide_none_security_option, false); SettingsShadowResources.overrideResource(R.bool.config_hide_swipe_security_option, false); } @@ -95,8 +104,8 @@ public class ChooseLockGenericControllerTest { } @Test - public void isScreenLockVisible_notCurrentUser_shouldHideInsecure() { - mController = new ChooseLockGenericController(application, 1 /* userId */); + public void isScreenLockVisible_ManagedProfile_shouldHideInsecure() { + ShadowUserManager.getShadow().setManagedProfiles(Set.of(0)); assertWithMessage("SWIPE visible").that( mController.isScreenLockVisible(ScreenLockType.SWIPE)).isFalse(); assertWithMessage("NONE visible").that(mController.isScreenLockVisible(ScreenLockType.NONE)) @@ -112,62 +121,116 @@ public class ChooseLockGenericControllerTest { } @Test - public void isScreenLockEnabled_lowerQuality_shouldReturnFalse() { - for (ScreenLockType lock : ScreenLockType.values()) { - assertWithMessage(lock + " enabled").that( - mController.isScreenLockEnabled(lock, lock.maxQuality + 1)).isFalse(); - } + public void isScreenLockEnabled_Default() { + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); } @Test - public void isScreenLockEnabled_equalQuality_shouldReturnTrue() { - for (ScreenLockType lock : ScreenLockType.values()) { - assertWithMessage(lock + " enabled").that( - mController.isScreenLockEnabled(lock, lock.defaultQuality)).isTrue(); - } + public void isScreenLockEnabled_QualityUnspecified() { + setDevicePolicyPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); } @Test - public void isScreenLockEnabled_higherQuality_shouldReturnTrue() { - for (ScreenLockType lock : ScreenLockType.values()) { - assertWithMessage(lock + " enabled").that( - mController.isScreenLockEnabled(lock, lock.maxQuality - 1)).isTrue(); - } + public void isScreenLockEnabled_QualitySomething() { + setDevicePolicyPasswordQuality(PASSWORD_QUALITY_SOMETHING); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); } @Test - public void isScreenLockDisabledByAdmin_lowerQuality_shouldReturnTrue() { - doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable(); - for (ScreenLockType lock : ScreenLockType.values()) { - assertWithMessage(lock + " disabledByAdmin").that( - mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality + 1)).isTrue(); - } + public void isScreenLockEnabled_QualityNumeric() { + setDevicePolicyPasswordQuality(PASSWORD_QUALITY_NUMERIC); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); } @Test - public void isScreenLockDisabledByAdmin_equalQuality_shouldReturnFalse() { - doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable(); - for (ScreenLockType lock : ScreenLockType.values()) { - assertWithMessage(lock + " disabledByAdmin").that( - mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality)).isFalse(); - } + public void isScreenLockEnabled_QualityNumericComplex() { + setDevicePolicyPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); } @Test - public void isScreenLockDisabledByAdmin_higherQuality_shouldReturnFalse() { - doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable(); - for (ScreenLockType lock : ScreenLockType.values()) { - assertWithMessage(lock + " disabledByAdmin").that( - mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality - 1)).isFalse(); - } + public void isScreenLockEnabled_QualityAlphabetic() { + setDevicePolicyPasswordQuality(PASSWORD_QUALITY_ALPHABETIC); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); } @Test - public void isScreenLockDisabledByAdmin_managedNotChoosable_shouldReturnTrue() { - doReturn(false).when(mManagedLockPasswordProvider).isManagedPasswordChoosable(); - assertWithMessage("MANANGED disabledByAdmin").that(mController.isScreenLockDisabledByAdmin( - ScreenLockType.MANAGED, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)) - .isTrue(); + public void isScreenLockEnabled_QualityComplex() { + setDevicePolicyPasswordQuality(PASSWORD_QUALITY_COMPLEX); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); + } + + @Test + public void isScreenLockEnabled_NoneComplexity() { + when(mLockPatternUtils.getRequestedPasswordComplexity(anyInt(), anyBoolean())) + .thenReturn(PASSWORD_COMPLEXITY_NONE); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); + } + + @Test + public void isScreenLockEnabled_lowComplexity() { + when(mLockPatternUtils.getRequestedPasswordComplexity(anyInt(), anyBoolean())) + .thenReturn(PASSWORD_COMPLEXITY_LOW); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); + } + + @Test + public void isScreenLockEnabled_mediumComplexity() { + when(mLockPatternUtils.getRequestedPasswordComplexity(anyInt(), anyBoolean())) + .thenReturn(PASSWORD_COMPLEXITY_MEDIUM); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); + } + + @Test + public void isScreenLockEnabled_highComplexity() { + when(mLockPatternUtils.getRequestedPasswordComplexity(anyInt(), anyBoolean())) + .thenReturn(PASSWORD_COMPLEXITY_HIGH); + assertThat(mController.isScreenLockEnabled(ScreenLockType.NONE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.SWIPE)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PATTERN)).isFalse(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PIN)).isTrue(); + assertThat(mController.isScreenLockEnabled(ScreenLockType.PASSWORD)).isTrue(); } @Test @@ -181,8 +244,8 @@ public class ChooseLockGenericControllerTest { @Test public void getVisibleScreenLockTypes_qualitySomething_shouldReturnPatterPinPassword() { - assertThat(mController.getVisibleScreenLockTypes( - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false)) + mController = createBuilder().setHideInsecureScreenLockTypes(true).build(); + assertThat(mController.getVisibleAndEnabledScreenLockTypes()) .isEqualTo(Arrays.asList( ScreenLockType.PATTERN, ScreenLockType.PIN, @@ -191,8 +254,7 @@ public class ChooseLockGenericControllerTest { @Test public void getVisibleScreenLockTypes_showDisabled_shouldReturnAllButManaged() { - assertThat(mController.getVisibleScreenLockTypes( - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, true)) + assertThat(mController.getVisibleAndEnabledScreenLockTypes()) .isEqualTo(Arrays.asList( ScreenLockType.NONE, ScreenLockType.SWIPE, @@ -223,31 +285,68 @@ public class ChooseLockGenericControllerTest { @Test public void upgradeQuality_complexityHigh_minQualityNumericComplex() { + mController = createBuilder().setAppRequestedMinComplexity(PASSWORD_COMPLEXITY_HIGH) + .build(); setDevicePolicyPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); - ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_HIGH); - assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)) + assertThat(mController.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX); } @Test public void upgradeQuality_complexityMedium_minQualityNumericComplex() { + mController = createBuilder().setAppRequestedMinComplexity(PASSWORD_COMPLEXITY_MEDIUM) + .build(); setDevicePolicyPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); - ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_MEDIUM); - assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)) + assertThat(mController.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX); } @Test public void upgradeQuality_complexityLow_minQualitySomething() { + mController = createBuilder().setAppRequestedMinComplexity(PASSWORD_COMPLEXITY_LOW) + .build(); setDevicePolicyPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); - ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_LOW); - assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)) + assertThat(mController.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)) .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); } + @Test + public void getAggregatedPasswordComplexity_AppRequest() { + mController = createBuilder().setAppRequestedMinComplexity(PASSWORD_COMPLEXITY_HIGH) + .build(); + assertThat(mController.getAggregatedPasswordComplexity()) + .isEqualTo(PASSWORD_COMPLEXITY_HIGH); + } + + @Test + public void getAggregatedPasswordComplexity_DevicePolicy() { + mController = createBuilder().setAppRequestedMinComplexity(PASSWORD_COMPLEXITY_LOW) + .build(); + when(mLockPatternUtils.getRequestedPasswordComplexity(eq(UserHandle.myUserId()), eq(false))) + .thenReturn(PASSWORD_COMPLEXITY_MEDIUM); + + assertThat(mController.getAggregatedPasswordComplexity()) + .isEqualTo(PASSWORD_COMPLEXITY_MEDIUM); + } + + @Test + public void getAggregatedPasswordComplexity_ProfileUnification() { + mController = createBuilder() + .setProfileToUnify(123) + .setAppRequestedMinComplexity(PASSWORD_COMPLEXITY_LOW) + .build(); + when(mLockPatternUtils.getRequestedPasswordComplexity(eq(UserHandle.myUserId()), eq(false))) + .thenReturn(PASSWORD_COMPLEXITY_MEDIUM); + when(mLockPatternUtils.getRequestedPasswordComplexity(eq(123))) + .thenReturn(PASSWORD_COMPLEXITY_HIGH); + + assertThat(mController.getAggregatedPasswordComplexity()) + .isEqualTo(PASSWORD_COMPLEXITY_HIGH); + } + private void setDevicePolicyPasswordQuality(int quality) { PasswordPolicy policy = new PasswordPolicy(); policy.quality = quality; @@ -256,13 +355,10 @@ public class ChooseLockGenericControllerTest { .thenReturn(policy.getMinMetrics()); } - private ChooseLockGenericController createController( - @PasswordComplexity int minPasswordComplexity) { - return new ChooseLockGenericController( + private ChooseLockGenericController.Builder createBuilder() { + return new ChooseLockGenericController.Builder( application, 0 /* userId */, - minPasswordComplexity, - false, mManagedLockPasswordProvider, mLockPatternUtils); } diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java index 2233f1d1d00..7da9c505b8e 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java @@ -30,7 +30,6 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED 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.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -429,18 +428,6 @@ public class ChooseLockPasswordTest { "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(null, 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) { return Robolectric.buildActivity(ChooseLockPassword.class, intent).setup().get(); } diff --git a/tests/robotests/src/com/android/settings/password/SetNewPasswordControllerTest.java b/tests/robotests/src/com/android/settings/password/SetNewPasswordControllerTest.java index 3034807baa6..f7b8d2d3f1b 100644 --- a/tests/robotests/src/com/android/settings/password/SetNewPasswordControllerTest.java +++ b/tests/robotests/src/com/android/settings/password/SetNewPasswordControllerTest.java @@ -19,8 +19,7 @@ package com.android.settings.password; import static android.content.pm.PackageManager.FEATURE_FACE; import static android.content.pm.PackageManager.FEATURE_FINGERPRINT; -import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS; -import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY; +import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE; @@ -262,12 +261,9 @@ public final class SetNewPasswordControllerTest { private void compareFingerprintExtras(Bundle actualBundle) { assertEquals( - "Password quality must be something in order to config fingerprint.", - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, - actualBundle.getInt(MINIMUM_QUALITY_KEY)); - assertTrue( - "All disabled preference should be removed.", - actualBundle.getBoolean(HIDE_DISABLED_PREFS)); + "Insecure options must be disabled in order to config fingerprint.", + true, + actualBundle.getBoolean(HIDE_INSECURE_OPTIONS)); assertTrue( "Fingerprint enroll must request Gatekeeper Password.", actualBundle.getBoolean(EXTRA_KEY_REQUEST_GK_PW_HANDLE)); @@ -282,12 +278,9 @@ public final class SetNewPasswordControllerTest { private void compareFaceExtras(Bundle actualBundle) { assertEquals( - "Password quality must be something in order to config face.", - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, - actualBundle.getInt(MINIMUM_QUALITY_KEY)); - assertTrue( - "All disabled preference should be removed.", - actualBundle.getBoolean(HIDE_DISABLED_PREFS)); + "Insecure options must be disabled in order to config face.", + true, + actualBundle.getBoolean(HIDE_INSECURE_OPTIONS)); assertTrue( "Face enroll must request Gatekeeper Password", actualBundle.getBoolean(EXTRA_KEY_REQUEST_GK_PW_HANDLE)); diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java index 5242e11a6e7..af4ae0b8a30 100644 --- a/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java +++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java @@ -198,8 +198,7 @@ public class SetupChooseLockPasswordTest { @Implements(ChooseLockGenericController.class) public static class ShadowChooseLockGenericController { @Implementation - protected List getVisibleScreenLockTypes(int quality, - boolean includeDisabled) { + protected List getVisibleScreenLockTypes() { return Collections.emptyList(); } }