Support EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY

When set, only enforce password requirement explicitly set device-wide.

As part of the change, restructure the code such that ChooseLockGeneric
becomes the central place for aggregating password requirements from
different parties, while ChooseLockPassword only enforces whatever
password reuirement it is told (by ChooseLockGeneric via intent extras)

Bug: 169832516
Test: m RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.password

Change-Id: I0acbea4819c13d4a8444c7b06928baccead18837
This commit is contained in:
Rubin Xu
2021-01-05 16:47:51 +00:00
parent 844bfb3683
commit ff1b547548
10 changed files with 334 additions and 111 deletions

View File

@@ -25,6 +25,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment.RESULT_FINISHED;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
@@ -162,6 +163,8 @@ public class ChooseLockGeneric extends SettingsActivity {
/**
* From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_REQUESTED_MIN_COMPLEXITY}.
* Only contains complexity requested by calling app, not complexity enforced by device
* admins.
*/
@PasswordComplexity private int mRequestedMinComplexity;
@@ -178,6 +181,8 @@ public class ChooseLockGeneric extends SettingsActivity {
protected boolean mForFace = false;
protected boolean mForBiometrics = false;
private boolean mOnlyEnforceDevicePasswordRequirement = false;
@Override
public int getMetricsCategory() {
return SettingsEnums.CHOOSE_LOCK_GENERIC;
@@ -221,20 +226,11 @@ public class ChooseLockGeneric extends SettingsActivity {
mForBiometrics = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
final int complexityFromIntent = intent
.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
final int complexityFromAdmin = mLockPatternUtils.getRequestedPasswordComplexity(
mUserId);
mRequestedMinComplexity = Math.max(complexityFromIntent, complexityFromAdmin);
final boolean isComplexityProvidedByAdmin = (complexityFromAdmin > complexityFromIntent)
&& mRequestedMinComplexity > PASSWORD_COMPLEXITY_NONE;
mRequestedMinComplexity = intent.getIntExtra(
EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
mOnlyEnforceDevicePasswordRequirement = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY, false);
// 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);
mIsCallingAppAdmin = intent
.getBooleanExtra(EXTRA_KEY_IS_CALLING_APP_ADMIN, /* defValue= */ false);
mForChangeCredRequiredForBoot = arguments != null && arguments.getBoolean(
@@ -268,7 +264,22 @@ public class ChooseLockGeneric extends SettingsActivity {
arguments,
intent.getExtras()).getIdentifier();
mController = new ChooseLockGenericController(
getContext(), mUserId, mRequestedMinComplexity, mLockPatternUtils);
getContext(), mUserId, mRequestedMinComplexity,
mOnlyEnforceDevicePasswordRequirement,
mLockPatternUtils);
final int aggregatedComplexity = mController.getAggregatedPasswordComplexity();
final boolean isComplexityProvidedByAdmin =
aggregatedComplexity > mRequestedMinComplexity
&& aggregatedComplexity > 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);
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
&& UserManager.get(activity).isManagedProfile(mUserId)
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
@@ -356,6 +367,8 @@ public class ChooseLockGeneric extends SettingsActivity {
chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed);
chooseLockGenericIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
mRequestedMinComplexity);
chooseLockGenericIntent.putExtra(EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY,
mOnlyEnforceDevicePasswordRequirement);
chooseLockGenericIntent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
if (mUserPassword != null) {
chooseLockGenericIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
@@ -557,7 +570,7 @@ public class ChooseLockGeneric extends SettingsActivity {
private String getFooterString() {
@StringRes int stringId;
switch (mRequestedMinComplexity) {
switch (mController.getAggregatedPasswordComplexity()) {
case PASSWORD_COMPLEXITY_HIGH:
stringId = R.string.unlock_footer_high_complexity_requested;
break;
@@ -678,7 +691,9 @@ public class ChooseLockGeneric extends SettingsActivity {
boolean hideDisabled) {
final PreferenceScreen entries = getPreferenceScreen();
int adminEnforcedQuality = mDpm.getPasswordQuality(null, mUserId);
int adminEnforcedQuality = LockPatternUtils.credentialTypeToPasswordQuality(
mLockPatternUtils.getRequestedPasswordMetrics(
mUserId, mOnlyEnforceDevicePasswordRequirement).credType);
EnforcedAdmin enforcedAdmin =
RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(getActivity(),
mUserId);
@@ -753,8 +768,10 @@ public class ChooseLockGeneric extends SettingsActivity {
protected Intent getLockPasswordIntent(int quality) {
ChooseLockPassword.IntentBuilder builder =
new ChooseLockPassword.IntentBuilder(getContext())
.setPasswordQuality(quality)
.setRequestedMinComplexity(mRequestedMinComplexity)
.setPasswordType(quality)
.setPasswordRequirement(
mController.getAggregatedPasswordComplexity(),
mController.getAggregatedPasswordMetrics())
.setForFingerprint(mForFingerprint)
.setForFace(mForFace)
.setForBiometrics(mForBiometrics)

View File

@@ -18,7 +18,6 @@ package com.android.settings.password;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.PasswordMetrics;
import android.content.Context;
@@ -42,8 +41,8 @@ public class ChooseLockGenericController {
private final Context mContext;
private final int mUserId;
@PasswordComplexity private final int mRequestedMinComplexity;
private final boolean mDevicePasswordRequirementOnly;
private ManagedLockPasswordProvider mManagedPasswordProvider;
private DevicePolicyManager mDpm;
private final LockPatternUtils mLockPatternUtils;
public ChooseLockGenericController(Context context, int userId) {
@@ -51,6 +50,7 @@ public class ChooseLockGenericController {
context,
userId,
PASSWORD_COMPLEXITY_NONE,
/* mOnlyEnforceDevicePasswordRequirement */ false,
new LockPatternUtils(context));
}
@@ -59,12 +59,14 @@ public class ChooseLockGenericController {
* when determining the available screen lock types
*/
public ChooseLockGenericController(Context context, int userId,
@PasswordComplexity int requestedMinComplexity, LockPatternUtils lockPatternUtils) {
@PasswordComplexity int requestedMinComplexity,
boolean devicePasswordRequirementOnly,
LockPatternUtils lockPatternUtils) {
this(
context,
userId,
requestedMinComplexity,
context.getSystemService(DevicePolicyManager.class),
devicePasswordRequirementOnly,
ManagedLockPasswordProvider.get(context, userId),
lockPatternUtils);
}
@@ -74,28 +76,29 @@ public class ChooseLockGenericController {
Context context,
int userId,
@PasswordComplexity int requestedMinComplexity,
DevicePolicyManager dpm,
boolean devicePasswordRequirementOnly,
ManagedLockPasswordProvider managedLockPasswordProvider,
LockPatternUtils lockPatternUtils) {
mContext = context;
mUserId = userId;
mRequestedMinComplexity = requestedMinComplexity;
mDevicePasswordRequirementOnly = devicePasswordRequirementOnly;
mManagedPasswordProvider = managedLockPasswordProvider;
mDpm = dpm;
mLockPatternUtils = lockPatternUtils;
}
/**
* Returns the highest quality among the specified {@code quality}, the quality required by
* {@link DevicePolicyManager#getPasswordQuality}, and the quality required by min password
* complexity.
* Returns the highest quality among the specified {@code quality}, the password requiremnet
* 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, mDpm.getPasswordQuality(null, mUserId));
int dpmUpgradedQuality = Math.max(quality, LockPatternUtils.credentialTypeToPasswordQuality(
getAggregatedPasswordMetrics().credType));
return Math.max(dpmUpgradedQuality,
PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
PasswordMetrics.complexityLevelToMinQuality(getAggregatedPasswordComplexity()));
}
/**
@@ -193,4 +196,15 @@ public class ChooseLockGenericController {
}
return locks;
}
public PasswordMetrics getAggregatedPasswordMetrics() {
return mLockPatternUtils.getRequestedPasswordMetrics(mUserId,
mDevicePasswordRequirementOnly);
}
public int getAggregatedPasswordComplexity() {
return Math.max(mRequestedMinComplexity,
mLockPatternUtils.getRequestedPasswordComplexity(
mUserId, mDevicePasswordRequirementOnly));
}
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.password;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE;
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_DIGITS;
@@ -31,7 +32,6 @@ import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_UPP
import static com.android.internal.widget.PasswordValidationError.RECENTLY_USED;
import static com.android.internal.widget.PasswordValidationError.TOO_LONG;
import static com.android.internal.widget.PasswordValidationError.TOO_SHORT;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID;
@@ -96,6 +96,9 @@ import java.util.List;
public class ChooseLockPassword extends SettingsActivity {
private static final String TAG = "ChooseLockPassword";
static final String EXTRA_KEY_MIN_METRICS = "min_metrics";
static final String EXTRA_KEY_MIN_COMPLEXITY = "min_complexity";
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
@@ -119,8 +122,13 @@ public class ChooseLockPassword extends SettingsActivity {
mIntent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, false);
}
public IntentBuilder setPasswordQuality(int quality) {
mIntent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
/**
* Sets the intended credential type i.e. whether it's numeric PIN or general password
* @param passwordType password type represented by one of the {@code PASSWORD_QUALITY_}
* constants.
*/
public IntentBuilder setPasswordType(int passwordType) {
mIntent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, passwordType);
return this;
}
@@ -156,8 +164,11 @@ public class ChooseLockPassword extends SettingsActivity {
return this;
}
public IntentBuilder setRequestedMinComplexity(@PasswordComplexity int level) {
mIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, level);
/** Sets the minimum password requirement in terms of complexity and metrics */
public IntentBuilder setPasswordRequirement(@PasswordComplexity int level,
PasswordMetrics metrics) {
mIntent.putExtra(EXTRA_KEY_MIN_COMPLEXITY, level);
mIntent.putExtra(EXTRA_KEY_MIN_METRICS, metrics);
return this;
}
@@ -240,7 +251,7 @@ public class ChooseLockPassword extends SettingsActivity {
private LockPatternUtils mLockPatternUtils;
private SaveAndFinishWorker mSaveAndFinishWorker;
private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
private int mPasswordType = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
protected Stage mUiStage = Stage.Introduction;
private PasswordRequirementAdapter mPasswordRequirementAdapter;
private GlifLayout mLayout;
@@ -410,19 +421,22 @@ public class ChooseLockPassword extends SettingsActivity {
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
mForBiometrics = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
mMinComplexity = intent.getIntExtra(
EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
mRequestedQuality = intent.getIntExtra(
mPasswordType = intent.getIntExtra(
LockPatternUtils.PASSWORD_TYPE_KEY, PASSWORD_QUALITY_NUMERIC);
mUnificationProfileId = intent.getIntExtra(
EXTRA_KEY_UNIFICATION_PROFILE_ID, UserHandle.USER_NULL);
mMinMetrics = mLockPatternUtils.getRequestedPasswordMetrics(mUserId);
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));
@@ -494,9 +508,9 @@ public class ChooseLockPassword extends SettingsActivity {
mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_lock));
}
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mPasswordType
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mPasswordType
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mPasswordType;
setupPasswordRequirementsView(view);

View File

@@ -99,6 +99,12 @@ public final class ChooseLockSettingsHelper {
*/
public static final String EXTRA_KEY_ALLOW_ANY_USER = "allow_any_user";
/**
*
*/
public static final String EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY =
"device_password_requirement_only";
@VisibleForTesting @NonNull LockPatternUtils mLockPatternUtils;
@NonNull private final Activity mActivity;
@Nullable private final Fragment mFragment;

View File

@@ -19,10 +19,12 @@ package com.android.settings.password;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import static android.app.admin.DevicePolicyManager.EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY;
import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
@@ -62,6 +64,8 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
*/
private @PasswordComplexity int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
private boolean mDevicePasswordRequirementOnly = false;
/**
* Label of the app which launches this activity.
*
@@ -72,27 +76,27 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
final Intent intent = getIntent();
mNewPasswordAction = getIntent().getAction();
mNewPasswordAction = intent.getAction();
if (!ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
&& !ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(mNewPasswordAction)) {
Log.e(TAG, "Unexpected action to launch this activity");
finish();
return;
}
logSetNewPasswordIntent();
final IBinder activityToken = getActivityToken();
mCallerAppName = (String) PasswordUtils.getCallingAppLabel(this, activityToken);
if (ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
&& getIntent().hasExtra(EXTRA_PASSWORD_COMPLEXITY)) {
&& intent.hasExtra(EXTRA_PASSWORD_COMPLEXITY)) {
final boolean hasPermission = PasswordUtils.isCallingAppPermitted(
this, activityToken, REQUEST_PASSWORD_COMPLEXITY);
if (hasPermission) {
mRequestedMinComplexity =
PasswordMetrics.sanitizeComplexityLevel(getIntent()
.getIntExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE));
PasswordMetrics.sanitizeComplexityLevel(intent.getIntExtra(
EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE));
} else {
PasswordUtils.crashCallingApplication(activityToken,
"Must have permission "
@@ -102,9 +106,14 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
return;
}
}
if (ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(mNewPasswordAction)) {
mDevicePasswordRequirementOnly = intent.getBooleanExtra(
EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY, false);
Log.i(TAG, String.format("DEVICE_PASSWORD_REQUIREMENT_ONLY: %b",
mDevicePasswordRequirementOnly));
}
mSetNewPasswordController = SetNewPasswordController.create(
this, this, getIntent(), getActivityToken());
this, this, intent, activityToken);
mSetNewPasswordController.dispatchSetNewPasswordIntent();
}
@@ -124,6 +133,7 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
if (isCallingAppAdmin()) {
intent.putExtra(EXTRA_KEY_IS_CALLING_APP_ADMIN, true);
}
intent.putExtra(EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY, mDevicePasswordRequirementOnly);
startActivity(intent);
finish();
}