New extra for ACTION_SET_NEW_PASSWORD to specify the min complexity
When an app that has the permission GET_AND_REQUEST_PASSWORD_COMPLEXITY launches ACTION_SET_NEW_PASSWORD, it can use the DPM PASSWORD_COMPLEXITY_* constants to specify the complexity it wants in a new extra EXTRA_PASSWORD_COMPLEXITY. The screen lock type picker would then filter out the options which cannot fulfil the min complexity (and DPM restrictions) and will show a footer with a brief description of the calling app and the requested type. The same password requirements UI is used in ChooseLockPassword screen to display the minimum requirements that can fulfil both DPM restrictions and the min complexity. The app must have permission GET_AND_REQUEST_PASSWORD_COMPLEXITY otherwise the extra would be ignored. ACTION_SET_NEW_PASSWORD is also updated to always display the calling app name in the screen lock type picker if it is not launched by Settings, with or without the new extra. Bug: 111173457 Test: atest packages/apps/Settings/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java atest packages/apps/Settings/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java atest packages/apps/Settings/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java atest packages/apps/Settings/tests/robotests/src/com/android/settings/password/PasswordUtilsTest.java atest packages/apps/Settings/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java atest packages/apps/Settings/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java manual test with TestDpc (ag/5901733) Change-Id: I21a25d28669bf1223c3b02ba85c0755e59feee2e
This commit is contained in:
@@ -1430,6 +1430,16 @@
|
||||
<!-- Title shown on security settings to allow the user to change their lockscreen password [CHAR LIMIT=22]-->
|
||||
<string name="unlock_change_lock_password_title">Change unlock password</string>
|
||||
|
||||
<!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and that it requests for a strong PIN or password [CHAR LIMIT=NONE] -->
|
||||
<string name="unlock_footer_high_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a strong PIN or password.</string>
|
||||
<!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and that it requests for a medium strength PIN or password [CHAR LIMIT=NONE] -->
|
||||
<string name="unlock_footer_medium_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new PIN or password.</string>
|
||||
<!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and it requests for any screen lock [CHAR LIMIT=NONE] -->
|
||||
<string name="unlock_footer_low_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new pattern, PIN or password.</string>
|
||||
<!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock [CHAR LIMIT=NONE] -->
|
||||
<string name="unlock_footer_none_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new screen lock.</string>
|
||||
|
||||
|
||||
<!-- Message shown on the lock screen when the user incorrectly enters their lock and it counts towards the max attempts before their data on the device is wiped. [CHAR LIMIT=NONE] -->
|
||||
<string name="lock_failed_attempts_before_wipe">Try again. Attempt <xliff:g id="current_attempts">%1$d</xliff:g> of <xliff:g id="total_attempts">%2$d</xliff:g>.</string>
|
||||
|
||||
|
@@ -18,13 +18,20 @@ package com.android.settings.password;
|
||||
|
||||
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.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 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_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -66,6 +73,8 @@ import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
import com.android.settingslib.widget.FooterPreferenceMixinCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -152,6 +161,14 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
private UserManager mUserManager;
|
||||
private ChooseLockGenericController mController;
|
||||
|
||||
/**
|
||||
* From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_REQUESTED_MIN_COMPLEXITY}.
|
||||
*/
|
||||
@PasswordComplexity private int mRequestedMinComplexity;
|
||||
|
||||
/** From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_CALLER_APP_NAME}. */
|
||||
private String mCallerAppName = null;
|
||||
|
||||
protected boolean mForFingerprint = false;
|
||||
protected boolean mForFace = false;
|
||||
|
||||
@@ -195,6 +212,10 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = getActivity().getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
mRequestedMinComplexity = getActivity().getIntent()
|
||||
.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
mCallerAppName =
|
||||
getActivity().getIntent().getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
|
||||
mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
|
||||
mUserManager = UserManager.get(getActivity());
|
||||
@@ -217,7 +238,8 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
UserManager.get(getActivity()),
|
||||
getArguments(),
|
||||
getActivity().getIntent().getExtras()).getIdentifier();
|
||||
mController = new ChooseLockGenericController(getContext(), mUserId);
|
||||
mController =
|
||||
new ChooseLockGenericController(getContext(), mUserId, mRequestedMinComplexity);
|
||||
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
|
||||
&& UserManager.get(getActivity()).isManagedProfile(mUserId)
|
||||
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
|
||||
@@ -291,6 +313,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
// Forward the target user id to ChooseLockGeneric.
|
||||
chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
|
||||
chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed);
|
||||
chooseLockGenericIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
|
||||
mRequestedMinComplexity);
|
||||
chooseLockGenericIntent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
|
||||
if (mUserPassword != null) {
|
||||
chooseLockGenericIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
|
||||
mUserPassword);
|
||||
@@ -461,6 +486,13 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
protected void addPreferences() {
|
||||
addPreferencesFromResource(R.xml.security_settings_picker);
|
||||
|
||||
if (!TextUtils.isEmpty(mCallerAppName)) {
|
||||
FooterPreferenceMixinCompat footerMixin =
|
||||
new FooterPreferenceMixinCompat(this, getSettingsLifecycle());
|
||||
FooterPreference footer = footerMixin.createFooterPreference();
|
||||
footer.setTitle(getFooterString());
|
||||
}
|
||||
|
||||
// Used for testing purposes
|
||||
findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none);
|
||||
findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none);
|
||||
@@ -469,6 +501,27 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
|
||||
}
|
||||
|
||||
private String getFooterString() {
|
||||
@StringRes int stringId;
|
||||
switch (mRequestedMinComplexity) {
|
||||
case PASSWORD_COMPLEXITY_HIGH:
|
||||
stringId = R.string.unlock_footer_high_complexity_requested;
|
||||
break;
|
||||
case PASSWORD_COMPLEXITY_MEDIUM:
|
||||
stringId = R.string.unlock_footer_medium_complexity_requested;
|
||||
break;
|
||||
case PASSWORD_COMPLEXITY_LOW:
|
||||
stringId = R.string.unlock_footer_low_complexity_requested;
|
||||
break;
|
||||
case PASSWORD_COMPLEXITY_NONE:
|
||||
default:
|
||||
stringId = R.string.unlock_footer_none_complexity_requested;
|
||||
break;
|
||||
}
|
||||
|
||||
return getResources().getString(stringId, mCallerAppName);
|
||||
}
|
||||
|
||||
private void updatePreferenceText() {
|
||||
if (mForFingerprint) {
|
||||
setPreferenceTitle(ScreenLockType.PATTERN,
|
||||
@@ -624,6 +677,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
ChooseLockPassword.IntentBuilder builder =
|
||||
new ChooseLockPassword.IntentBuilder(getContext())
|
||||
.setPasswordQuality(quality)
|
||||
.setRequestedMinComplexity(mRequestedMinComplexity)
|
||||
.setForFingerprint(mForFingerprint)
|
||||
.setForFace(mForFace)
|
||||
.setUserId(mUserId);
|
||||
|
@@ -16,7 +16,11 @@
|
||||
|
||||
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;
|
||||
import android.os.UserHandle;
|
||||
|
||||
@@ -36,6 +40,7 @@ public class ChooseLockGenericController {
|
||||
|
||||
private final Context mContext;
|
||||
private final int mUserId;
|
||||
@PasswordComplexity private final int mRequestedMinComplexity;
|
||||
private ManagedLockPasswordProvider mManagedPasswordProvider;
|
||||
private DevicePolicyManager mDpm;
|
||||
|
||||
@@ -43,6 +48,19 @@ public class ChooseLockGenericController {
|
||||
this(
|
||||
context,
|
||||
userId,
|
||||
PASSWORD_COMPLEXITY_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
this(
|
||||
context,
|
||||
userId,
|
||||
requestedMinComplexity,
|
||||
context.getSystemService(DevicePolicyManager.class),
|
||||
ManagedLockPasswordProvider.get(context, userId));
|
||||
}
|
||||
@@ -51,21 +69,26 @@ public class ChooseLockGenericController {
|
||||
ChooseLockGenericController(
|
||||
Context context,
|
||||
int userId,
|
||||
@PasswordComplexity int requestedMinComplexity,
|
||||
DevicePolicyManager dpm,
|
||||
ManagedLockPasswordProvider managedLockPasswordProvider) {
|
||||
mContext = context;
|
||||
mUserId = userId;
|
||||
mRequestedMinComplexity = requestedMinComplexity;
|
||||
mManagedPasswordProvider = managedLockPasswordProvider;
|
||||
mDpm = dpm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The higher quality of either the specified {@code quality} or the quality required
|
||||
* by {@link DevicePolicyManager#getPasswordQuality}.
|
||||
* Returns the highest quality among the specified {@code quality}, the quality required by
|
||||
* {@link DevicePolicyManager#getPasswordQuality}, and the quality required by min password
|
||||
* complexity.
|
||||
*/
|
||||
public int upgradeQuality(int quality) {
|
||||
// Compare min allowed password quality
|
||||
return Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
|
||||
// Compare specified quality and dpm quality
|
||||
int dpmUpgradedQuality = Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
|
||||
return Math.max(dpmUpgradedQuality,
|
||||
PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -16,14 +16,18 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
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_ALPHANUMERIC;
|
||||
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 com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
@@ -56,6 +60,7 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
|
||||
import com.android.internal.widget.TextViewInputDisabler;
|
||||
@@ -133,6 +138,11 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setRequestedMinComplexity(@PasswordComplexity int level) {
|
||||
mIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, level);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Intent build() {
|
||||
return mIntent;
|
||||
}
|
||||
@@ -190,12 +200,10 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
private int mPasswordMinNumeric = 0;
|
||||
private int mPasswordMinNonLetter = 0;
|
||||
private int mPasswordMinLengthToFulfillAllPolicies = 0;
|
||||
private boolean mPasswordNumSequenceAllowed = true;
|
||||
@PasswordComplexity private int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
|
||||
protected int mUserId;
|
||||
private byte[] mPasswordHistoryHashFactor;
|
||||
/**
|
||||
* Password requirements that we need to verify.
|
||||
*/
|
||||
private int[] mPasswordRequirements;
|
||||
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private SaveAndFinishWorker mSaveAndFinishWorker;
|
||||
@@ -372,7 +380,13 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mForFingerprint = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
processPasswordRequirements(intent);
|
||||
mRequestedMinComplexity = intent.getIntExtra(
|
||||
EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
mRequestedQuality = Math.max(
|
||||
intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality),
|
||||
mLockPatternUtils.getRequestedPasswordQuality(mUserId));
|
||||
|
||||
loadDpmPasswordRequirements();
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
|
||||
|
||||
if (intent.getBooleanExtra(
|
||||
@@ -504,31 +518,6 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
|
||||
private void setupPasswordRequirementsView(View view) {
|
||||
final List<Integer> passwordRequirements = new ArrayList<>();
|
||||
if (mPasswordMinUpperCase > 0) {
|
||||
passwordRequirements.add(MIN_UPPER_LETTERS_IN_PASSWORD);
|
||||
}
|
||||
if (mPasswordMinLowerCase > 0) {
|
||||
passwordRequirements.add(MIN_LOWER_LETTERS_IN_PASSWORD);
|
||||
}
|
||||
if (mPasswordMinLetters > 0) {
|
||||
if (mPasswordMinLetters > mPasswordMinUpperCase + mPasswordMinLowerCase) {
|
||||
passwordRequirements.add(MIN_LETTER_IN_PASSWORD);
|
||||
}
|
||||
}
|
||||
if (mPasswordMinNumeric > 0) {
|
||||
passwordRequirements.add(MIN_NUMBER_IN_PASSWORD);
|
||||
}
|
||||
if (mPasswordMinSymbols > 0) {
|
||||
passwordRequirements.add(MIN_SYMBOLS_IN_PASSWORD);
|
||||
}
|
||||
if (mPasswordMinNonLetter > 0) {
|
||||
if (mPasswordMinNonLetter > mPasswordMinNumeric + mPasswordMinSymbols) {
|
||||
passwordRequirements.add(MIN_NON_LETTER_IN_PASSWORD);
|
||||
}
|
||||
}
|
||||
// Convert list to array.
|
||||
mPasswordRequirements = passwordRequirements.stream().mapToInt(i -> i).toArray();
|
||||
mPasswordRestrictionView = view.findViewById(R.id.password_requirements_view);
|
||||
mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
mPasswordRequirementAdapter = new PasswordRequirementAdapter();
|
||||
@@ -603,13 +592,12 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
|
||||
/**
|
||||
* Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
|
||||
*
|
||||
* @param intent the incoming intent
|
||||
*/
|
||||
private void processPasswordRequirements(Intent intent) {
|
||||
private void loadDpmPasswordRequirements() {
|
||||
final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
|
||||
mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
mRequestedQuality), dpmPasswordQuality);
|
||||
if (dpmPasswordQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
|
||||
mPasswordNumSequenceAllowed = false;
|
||||
}
|
||||
mPasswordMinLength = Math.max(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
|
||||
mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
|
||||
mPasswordMaxLength = mLockPatternUtils.getMaximumPasswordLength(mRequestedQuality);
|
||||
@@ -620,7 +608,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mPasswordMinSymbols = mLockPatternUtils.getRequestedPasswordMinimumSymbols(mUserId);
|
||||
mPasswordMinNonLetter = mLockPatternUtils.getRequestedPasswordMinimumNonLetter(mUserId);
|
||||
|
||||
// Modify the value based on dpm policy.
|
||||
// Modify the value based on dpm policy
|
||||
switch (dpmPasswordQuality) {
|
||||
case PASSWORD_QUALITY_ALPHABETIC:
|
||||
if (mPasswordMinLetters == 0) {
|
||||
@@ -646,19 +634,88 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mPasswordMinSymbols = 0;
|
||||
mPasswordMinNonLetter = 0;
|
||||
}
|
||||
|
||||
mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the dpm requirements and the min complexity requirements.
|
||||
*
|
||||
* <p>Since there are more than one set of metrics to meet the min complexity requirement,
|
||||
* and we are not hard-coding any one of them to be the requirements the user must fulfil,
|
||||
* we are taking what the user has already entered into account when compiling the list of
|
||||
* requirements from min complexity. Then we merge this list with the DPM requirements, and
|
||||
* present the merged set as validation results to the user on the UI.
|
||||
*
|
||||
* <p>For example, suppose min complexity requires either ALPHABETIC(8+), or
|
||||
* ALPHANUMERIC(6+). If the user has entered "a", the length requirement displayed on the UI
|
||||
* would be 8. Then the user appends "1" to make it "a1". We now know the user is entering
|
||||
* an alphanumeric password so we would update the min complexity required min length to 6.
|
||||
* This might result in a little confusion for the user but the UI does not support showing
|
||||
* multiple sets of requirements / validation results as options to users, this is the best
|
||||
* we can do now.
|
||||
*/
|
||||
private void mergeMinComplexityAndDpmRequirements(int userEnteredPasswordQuality) {
|
||||
if (mRequestedMinComplexity == PASSWORD_COMPLEXITY_NONE) {
|
||||
// dpm requirements are dominant if min complexity is none
|
||||
return;
|
||||
}
|
||||
|
||||
// reset dpm requirements
|
||||
loadDpmPasswordRequirements();
|
||||
|
||||
PasswordMetrics minMetrics = PasswordMetrics.getMinimumMetrics(
|
||||
mRequestedMinComplexity, userEnteredPasswordQuality, mRequestedQuality,
|
||||
requiresNumeric(), requiresLettersOrSymbols());
|
||||
mPasswordNumSequenceAllowed = mPasswordNumSequenceAllowed
|
||||
&& minMetrics.quality != PASSWORD_QUALITY_NUMERIC_COMPLEX;
|
||||
mPasswordMinLength = Math.max(mPasswordMinLength, minMetrics.length);
|
||||
mPasswordMinLetters = Math.max(mPasswordMinLetters, minMetrics.letters);
|
||||
mPasswordMinUpperCase = Math.max(mPasswordMinUpperCase, minMetrics.upperCase);
|
||||
mPasswordMinLowerCase = Math.max(mPasswordMinLowerCase, minMetrics.lowerCase);
|
||||
mPasswordMinNumeric = Math.max(mPasswordMinNumeric, minMetrics.numeric);
|
||||
mPasswordMinSymbols = Math.max(mPasswordMinSymbols, minMetrics.symbols);
|
||||
mPasswordMinNonLetter = Math.max(mPasswordMinNonLetter, minMetrics.nonLetter);
|
||||
|
||||
if (minMetrics.quality == PASSWORD_QUALITY_ALPHABETIC) {
|
||||
if (!requiresLettersOrSymbols()) {
|
||||
mPasswordMinLetters = 1;
|
||||
}
|
||||
}
|
||||
if (minMetrics.quality == PASSWORD_QUALITY_ALPHANUMERIC) {
|
||||
if (!requiresLettersOrSymbols()) {
|
||||
mPasswordMinLetters = 1;
|
||||
}
|
||||
if (!requiresNumeric()) {
|
||||
mPasswordMinNumeric = 1;
|
||||
}
|
||||
}
|
||||
|
||||
mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
|
||||
}
|
||||
|
||||
private boolean requiresLettersOrSymbols() {
|
||||
// This is the condition for the password to be considered ALPHABETIC according to
|
||||
// PasswordMetrics.computeForPassword()
|
||||
return mPasswordMinLetters + mPasswordMinUpperCase
|
||||
+ mPasswordMinLowerCase + mPasswordMinSymbols + mPasswordMinNonLetter > 0;
|
||||
}
|
||||
|
||||
private boolean requiresNumeric() {
|
||||
return mPasswordMinNumeric > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates PIN/Password and returns the validation result.
|
||||
*
|
||||
* @param password the raw password the user typed in
|
||||
* @return the validation result.
|
||||
*/
|
||||
private int validatePassword(String password) {
|
||||
@VisibleForTesting
|
||||
int validatePassword(String password) {
|
||||
int errorCode = NO_ERROR;
|
||||
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
|
||||
|
||||
mergeMinComplexityAndDpmRequirements(metrics.quality);
|
||||
|
||||
if (password.length() < mPasswordMinLength) {
|
||||
if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) {
|
||||
@@ -668,14 +725,25 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
errorCode |= TOO_LONG;
|
||||
} else {
|
||||
// The length requirements are fulfilled.
|
||||
final int dpmQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
|
||||
if (dpmQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX &&
|
||||
metrics.numeric == password.length()) {
|
||||
if (!mPasswordNumSequenceAllowed
|
||||
&& !requiresLettersOrSymbols()
|
||||
&& metrics.numeric == password.length()) {
|
||||
// Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
|
||||
// if DevicePolicyManager requires a complex numeric password. There can be
|
||||
// two cases in the UI: 1. User chooses to enroll a PIN, 2. User chooses to
|
||||
// enroll a password but enters a numeric-only pin. We should carry out the
|
||||
// sequence check in both cases.
|
||||
// if DevicePolicyManager or min password complexity requires a complex numeric
|
||||
// password. There can be two cases in the UI: 1. User chooses to enroll a
|
||||
// PIN, 2. User chooses to enroll a password but enters a numeric-only pin. We
|
||||
// should carry out the sequence check in both cases.
|
||||
//
|
||||
// Conditions for the !requiresLettersOrSymbols() to be necessary:
|
||||
// - DPM requires NUMERIC_COMPLEX
|
||||
// - min complexity not NONE, user picks PASSWORD type so ALPHABETIC or
|
||||
// ALPHANUMERIC is required
|
||||
// Imagine user has entered "12345678", if we don't skip the sequence check, the
|
||||
// validation result would show both "requires a letter" and "sequence not
|
||||
// allowed", while the only requirement the user needs to know is "requires a
|
||||
// letter" because once the user has fulfilled the alphabetic requirement, the
|
||||
// password would not be containing only digits so this check would not be
|
||||
// performed anyway.
|
||||
final int sequence = PasswordMetrics.maxLengthSequence(password);
|
||||
if (sequence > PasswordMetrics.MAX_ALLOWED_SEQUENCE) {
|
||||
errorCode |= CONTAIN_SEQUENTIAL_DIGITS;
|
||||
@@ -706,43 +774,24 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
}
|
||||
|
||||
// Check the requirements one by one.
|
||||
for (int i = 0; i < mPasswordRequirements.length; i++) {
|
||||
int passwordRestriction = mPasswordRequirements[i];
|
||||
switch (passwordRestriction) {
|
||||
case MIN_LETTER_IN_PASSWORD:
|
||||
if (metrics.letters < mPasswordMinLetters) {
|
||||
errorCode |= NOT_ENOUGH_LETTER;
|
||||
}
|
||||
break;
|
||||
case MIN_UPPER_LETTERS_IN_PASSWORD:
|
||||
if (metrics.upperCase < mPasswordMinUpperCase) {
|
||||
errorCode |= NOT_ENOUGH_UPPER_CASE;
|
||||
}
|
||||
break;
|
||||
case MIN_LOWER_LETTERS_IN_PASSWORD:
|
||||
if (metrics.lowerCase < mPasswordMinLowerCase) {
|
||||
errorCode |= NOT_ENOUGH_LOWER_CASE;
|
||||
}
|
||||
break;
|
||||
case MIN_SYMBOLS_IN_PASSWORD:
|
||||
if (metrics.symbols < mPasswordMinSymbols) {
|
||||
errorCode |= NOT_ENOUGH_SYMBOLS;
|
||||
}
|
||||
break;
|
||||
case MIN_NUMBER_IN_PASSWORD:
|
||||
if (metrics.numeric < mPasswordMinNumeric) {
|
||||
errorCode |= NOT_ENOUGH_DIGITS;
|
||||
}
|
||||
break;
|
||||
case MIN_NON_LETTER_IN_PASSWORD:
|
||||
if (metrics.nonLetter < mPasswordMinNonLetter) {
|
||||
errorCode |= NOT_ENOUGH_NON_LETTER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
|
@@ -45,6 +45,18 @@ public final class ChooseLockSettingsHelper {
|
||||
public static final String EXTRA_KEY_FOR_FACE = "for_face";
|
||||
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
|
||||
|
||||
/**
|
||||
* Intent extra for passing the requested min password complexity to later steps in the set new
|
||||
* screen lock flow.
|
||||
*/
|
||||
public static final String EXTRA_KEY_REQUESTED_MIN_COMPLEXITY = "requested_min_complexity";
|
||||
|
||||
/**
|
||||
* Intent extra for passing the label of the calling app to later steps in the set new screen
|
||||
* lock flow.
|
||||
*/
|
||||
public static final String EXTRA_KEY_CALLER_APP_NAME = "caller_app_name";
|
||||
|
||||
/**
|
||||
* When invoked via {@link ConfirmLockPassword.InternalActivity}, this flag
|
||||
* controls if we relax the enforcement of
|
||||
|
97
src/com/android/settings/password/PasswordUtils.java
Normal file
97
src/com/android/settings/password/PasswordUtils.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.IActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
|
||||
public final class PasswordUtils extends com.android.settingslib.Utils {
|
||||
|
||||
private static final String TAG = "Settings";
|
||||
|
||||
private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
|
||||
|
||||
/**
|
||||
* Returns whether the uid which the activity with {@code activityToken} is launched from has
|
||||
* been granted the {@code permission}.
|
||||
*/
|
||||
public static boolean isCallingAppPermitted(Context context, IBinder activityToken,
|
||||
String permission) {
|
||||
try {
|
||||
return context.checkPermission(permission, /* pid= */ -1,
|
||||
ActivityManager.getService().getLaunchedFromUid(activityToken))
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Could not talk to activity manager.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label of the package which the activity with {@code activityToken} is launched
|
||||
* from or {@code null} if it is launched from the settings app itself.
|
||||
*/
|
||||
@Nullable
|
||||
public static CharSequence getCallingAppLabel(Context context, IBinder activityToken) {
|
||||
String pkg = getCallingAppPackageName(activityToken);
|
||||
if (pkg == null || pkg.equals(SETTINGS_PACKAGE_NAME)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Utils.getApplicationLabel(context, pkg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the package name which the activity with {@code activityToken} is launched from.
|
||||
*/
|
||||
@Nullable
|
||||
private static String getCallingAppPackageName(IBinder activityToken) {
|
||||
String pkg = null;
|
||||
try {
|
||||
pkg = ActivityManager.getService().getLaunchedFromPackage(activityToken);
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Could not talk to activity manager.", e);
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/** Crashes the calling application and provides it with {@code message}. */
|
||||
public static void crashCallingApplication(IBinder activityToken, String message) {
|
||||
IActivityManager am = ActivityManager.getService();
|
||||
try {
|
||||
int uid = am.getLaunchedFromUid(activityToken);
|
||||
int userId = UserHandle.getUserId(uid);
|
||||
am.crashApplication(
|
||||
uid,
|
||||
/* initialPid= */ -1,
|
||||
getCallingAppPackageName(activityToken),
|
||||
userId,
|
||||
message);
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Could not talk to activity manager.", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,13 +16,22 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_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_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_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
@@ -37,6 +46,21 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
|
||||
private String mNewPasswordAction;
|
||||
private SetNewPasswordController mSetNewPasswordController;
|
||||
|
||||
/**
|
||||
* From intent extra {@link DevicePolicyManager#EXTRA_PASSWORD_COMPLEXITY}.
|
||||
*
|
||||
* <p>This is used only if caller has the required permission and activity is launched by
|
||||
* {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD}.
|
||||
*/
|
||||
private @PasswordComplexity int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
|
||||
|
||||
/**
|
||||
* Label of the app which launches this activity.
|
||||
*
|
||||
* <p>Value would be {@code null} if launched from settings app.
|
||||
*/
|
||||
private String mCallerAppName = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
@@ -48,6 +72,25 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
IBinder activityToken = getActivityToken();
|
||||
mCallerAppName = (String) PasswordUtils.getCallingAppLabel(this, activityToken);
|
||||
if (ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
|
||||
&& getIntent().hasExtra(EXTRA_PASSWORD_COMPLEXITY)) {
|
||||
boolean hasPermission = PasswordUtils.isCallingAppPermitted(
|
||||
this, activityToken, GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
if (hasPermission) {
|
||||
mRequestedMinComplexity = PasswordMetrics.sanitizeComplexityLevel(getIntent()
|
||||
.getIntExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE));
|
||||
} else {
|
||||
PasswordUtils.crashCallingApplication(activityToken,
|
||||
"Must have permission " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY
|
||||
+ " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mSetNewPasswordController = SetNewPasswordController.create(
|
||||
this, this, getIntent(), getActivityToken());
|
||||
mSetNewPasswordController.dispatchSetNewPasswordIntent();
|
||||
@@ -60,6 +103,12 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
|
||||
: new Intent(this, ChooseLockGeneric.class);
|
||||
intent.setAction(mNewPasswordAction);
|
||||
intent.putExtras(chooseLockFingerprintExtras);
|
||||
if (mCallerAppName != null) {
|
||||
intent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
|
||||
}
|
||||
if (mRequestedMinComplexity != PASSWORD_COMPLEXITY_NONE) {
|
||||
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, mRequestedMinComplexity);
|
||||
}
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
@@ -16,11 +16,17 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
|
||||
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;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -48,6 +54,7 @@ import com.google.android.setupdesign.GlifPreferenceLayout;
|
||||
* Other changes should be done to ChooseLockGeneric class instead and let this class inherit
|
||||
* those changes.
|
||||
*/
|
||||
// TODO(b/123225425): Restrict SetupChooseLockGeneric to be accessible by SUW only
|
||||
public class SetupChooseLockGeneric extends ChooseLockGeneric {
|
||||
|
||||
private static final String KEY_UNLOCK_SET_DO_LATER = "unlock_set_do_later";
|
||||
@@ -71,6 +78,20 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstance) {
|
||||
super.onCreate(savedInstance);
|
||||
|
||||
if(getIntent().hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)) {
|
||||
IBinder activityToken = getActivityToken();
|
||||
boolean hasPermission = PasswordUtils.isCallingAppPermitted(
|
||||
this, activityToken, GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
if (!hasPermission) {
|
||||
PasswordUtils.crashCallingApplication(activityToken,
|
||||
"Must have permission " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY
|
||||
+ " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
|
||||
layout.setFitsSystemWindows(false);
|
||||
}
|
||||
|
@@ -16,15 +16,22 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
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 com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
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.content.ComponentName;
|
||||
|
||||
import com.android.settings.R;
|
||||
@@ -58,11 +65,7 @@ public class ChooseLockGenericControllerTest {
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mController = new ChooseLockGenericController(
|
||||
application,
|
||||
0 /* userId */,
|
||||
mDevicePolicyManager,
|
||||
mManagedLockPasswordProvider);
|
||||
mController = createController(PASSWORD_COMPLEXITY_NONE);
|
||||
SettingsShadowResources.overrideResource(R.bool.config_hide_none_security_option, false);
|
||||
SettingsShadowResources.overrideResource(R.bool.config_hide_swipe_security_option, false);
|
||||
}
|
||||
@@ -225,4 +228,44 @@ public class ChooseLockGenericControllerTest {
|
||||
assertThat(upgradedQuality).named("upgradedQuality")
|
||||
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeQuality_complexityHigh_minQualityNumericComplex() {
|
||||
when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
|
||||
.thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
|
||||
ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_HIGH);
|
||||
|
||||
assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
|
||||
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeQuality_complexityMedium_minQualityNumericComplex() {
|
||||
when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
|
||||
.thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
|
||||
ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_MEDIUM);
|
||||
|
||||
assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
|
||||
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeQuality_complexityLow_minQualitySomething() {
|
||||
when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
|
||||
.thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
|
||||
ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_LOW);
|
||||
|
||||
assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
|
||||
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
|
||||
}
|
||||
|
||||
private ChooseLockGenericController createController(
|
||||
@PasswordComplexity int minPasswordComplexity) {
|
||||
return new ChooseLockGenericController(
|
||||
application,
|
||||
0 /* userId */,
|
||||
minPasswordComplexity,
|
||||
mDevicePolicyManager,
|
||||
mManagedLockPasswordProvider);
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,15 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
|
||||
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 com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
@@ -27,8 +36,10 @@ import android.content.Intent;
|
||||
import android.provider.Settings.Global;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
|
||||
import com.android.settings.search.SearchFeatureProvider;
|
||||
@@ -36,6 +47,7 @@ import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowStorageManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -112,6 +124,70 @@ public class ChooseLockGenericTest {
|
||||
assertThat(shadowOf(mActivity).getNextStartedActivity()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferencesOrFinish_footerPreferenceAddedHighComplexityText() {
|
||||
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||
Intent intent = new Intent()
|
||||
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
|
||||
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
|
||||
initActivity(intent);
|
||||
CharSequence expectedTitle =
|
||||
mActivity.getString(R.string.unlock_footer_high_complexity_requested, "app name");
|
||||
|
||||
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||
FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
|
||||
|
||||
assertThat(footer.getTitle()).isEqualTo(expectedTitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferencesOrFinish_footerPreferenceAddedMediumComplexityText() {
|
||||
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||
Intent intent = new Intent()
|
||||
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
|
||||
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_MEDIUM);
|
||||
initActivity(intent);
|
||||
CharSequence expectedTitle =
|
||||
mActivity.getString(R.string.unlock_footer_medium_complexity_requested, "app name");
|
||||
|
||||
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||
FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
|
||||
|
||||
assertThat(footer.getTitle()).isEqualTo(expectedTitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferencesOrFinish_footerPreferenceAddedLowComplexityText() {
|
||||
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||
Intent intent = new Intent()
|
||||
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
|
||||
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
|
||||
initActivity(intent);
|
||||
CharSequence expectedTitle =
|
||||
mActivity.getString(R.string.unlock_footer_low_complexity_requested, "app name");
|
||||
|
||||
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||
FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
|
||||
|
||||
assertThat(footer.getTitle()).isEqualTo(expectedTitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferencesOrFinish_footerPreferenceAddedNoneComplexityText() {
|
||||
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
|
||||
Intent intent = new Intent()
|
||||
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
|
||||
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
initActivity(intent);
|
||||
CharSequence expectedTitle =
|
||||
mActivity.getString(R.string.unlock_footer_none_complexity_requested, "app name");
|
||||
|
||||
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||
FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
|
||||
|
||||
assertThat(footer.getTitle()).isEqualTo(expectedTitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityResult_requestcode0_shouldNotFinish() {
|
||||
initActivity(null);
|
||||
@@ -165,6 +241,48 @@ public class ChooseLockGenericTest {
|
||||
assertThat(mActivity.isFinishing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceTreeClick_fingerprintPassesMinComplexityInfoOntoNextActivity() {
|
||||
Intent intent = new Intent(ACTION_SET_NEW_PASSWORD)
|
||||
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH)
|
||||
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name");
|
||||
initActivity(intent);
|
||||
|
||||
Preference fingerprintPref = new Preference(application);
|
||||
fingerprintPref.setKey("unlock_skip_fingerprint");
|
||||
boolean result = mFragment.onPreferenceTreeClick(fingerprintPref);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
Intent actualIntent = shadowOf(mActivity).getNextStartedActivityForResult().intent;
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
|
||||
assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
|
||||
.isEqualTo(PASSWORD_COMPLEXITY_HIGH);
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
|
||||
assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME))
|
||||
.isEqualTo("app name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceTreeClick_facePassesMinComplexityInfoOntoNextActivity() {
|
||||
Intent intent = new Intent(ACTION_SET_NEW_PASSWORD)
|
||||
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH)
|
||||
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name");
|
||||
initActivity(intent);
|
||||
|
||||
Preference facePref = new Preference(application);
|
||||
facePref.setKey("unlock_skip_face");
|
||||
boolean result = mFragment.onPreferenceTreeClick(facePref);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
Intent actualIntent = shadowOf(mActivity).getNextStartedActivityForResult().intent;
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
|
||||
assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
|
||||
.isEqualTo(PASSWORD_COMPLEXITY_HIGH);
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
|
||||
assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME))
|
||||
.isEqualTo("app name");
|
||||
}
|
||||
|
||||
private void initActivity(@Nullable Intent intent) {
|
||||
if (intent == null) {
|
||||
intent = new Intent();
|
||||
|
@@ -16,19 +16,37 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
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_ALPHANUMERIC;
|
||||
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.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_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.content.Intent;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment;
|
||||
import com.android.settings.password.ChooseLockPassword.IntentBuilder;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
|
||||
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
@@ -44,13 +62,21 @@ import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowDrawable;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {SettingsShadowResources.class, ShadowUtils.class})
|
||||
@Config(shadows = {
|
||||
SettingsShadowResources.class,
|
||||
ShadowUtils.class,
|
||||
ShadowDevicePolicyManager.class,
|
||||
})
|
||||
public class ChooseLockPasswordTest {
|
||||
|
||||
private ShadowDevicePolicyManager mShadowDpm;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
SettingsShadowResources.overrideResource(
|
||||
com.android.internal.R.string.config_headlineFontFamily, "");
|
||||
mShadowDpm = ShadowDevicePolicyManager.getShadow();
|
||||
mShadowDpm.setPasswordMaximumLength(16);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -72,7 +98,7 @@ public class ChooseLockPasswordTest {
|
||||
assertThat(intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD))
|
||||
.named("EXTRA_KEY_PASSWORD")
|
||||
.isEqualTo("password");
|
||||
assertThat(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 0))
|
||||
assertThat(intent.getIntExtra(PASSWORD_TYPE_KEY, 0))
|
||||
.named("PASSWORD_TYPE_KEY")
|
||||
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
|
||||
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0))
|
||||
@@ -84,7 +110,7 @@ public class ChooseLockPasswordTest {
|
||||
public void intentBuilder_setChallenge_shouldAddExtras() {
|
||||
Intent intent = new IntentBuilder(application)
|
||||
.setChallenge(12345L)
|
||||
.setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC)
|
||||
.setPasswordQuality(PASSWORD_QUALITY_ALPHANUMERIC)
|
||||
.setUserId(123)
|
||||
.build();
|
||||
|
||||
@@ -94,14 +120,213 @@ public class ChooseLockPasswordTest {
|
||||
assertThat(intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0L))
|
||||
.named("EXTRA_KEY_CHALLENGE")
|
||||
.isEqualTo(12345L);
|
||||
assertThat(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 0))
|
||||
assertThat(intent.getIntExtra(PASSWORD_TYPE_KEY, 0))
|
||||
.named("PASSWORD_TYPE_KEY")
|
||||
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
|
||||
.isEqualTo(PASSWORD_QUALITY_ALPHANUMERIC);
|
||||
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0))
|
||||
.named("EXTRA_USER_ID")
|
||||
.isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intentBuilder_setMinComplexityMedium_hasMinComplexityExtraMedium() {
|
||||
Intent intent = new IntentBuilder(application)
|
||||
.setRequestedMinComplexity(PASSWORD_COMPLEXITY_MEDIUM)
|
||||
.build();
|
||||
|
||||
assertThat(intent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
|
||||
assertThat(intent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
|
||||
.isEqualTo(PASSWORD_COMPLEXITY_MEDIUM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intentBuilder_setMinComplexityNotCalled() {
|
||||
Intent intent = new IntentBuilder(application).build();
|
||||
|
||||
assertThat(intent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_noMinPasswordComplexity() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_ALPHABETIC);
|
||||
mShadowDpm.setPasswordMinimumLength(10);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "",
|
||||
"Must contain at least 1 letter",
|
||||
"Must be at least 10 characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_minPasswordComplexityStricter_pin() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_SOMETHING);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||
/* passwordType= */ PASSWORD_QUALITY_NUMERIC,
|
||||
/* userEnteredPassword= */ "",
|
||||
"PIN must be at least 8 digits");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_minPasswordComplexityStricter_password() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_SOMETHING);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "",
|
||||
"Must contain at least 1 letter",
|
||||
"Must be at least 4 characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_dpmRestrictionsStricter_password() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_ALPHANUMERIC);
|
||||
mShadowDpm.setPasswordMinimumLength(9);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "",
|
||||
"Must contain at least 1 letter",
|
||||
"Must contain at least 1 numerical digit",
|
||||
"Must be at least 9 characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_dpmLengthLonger_pin() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC);
|
||||
mShadowDpm.setPasswordMinimumLength(11);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
|
||||
/* passwordType= */ PASSWORD_QUALITY_NUMERIC,
|
||||
/* userEnteredPassword= */ "",
|
||||
"PIN must be at least 11 digits");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_dpmQualityComplex() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_COMPLEX);
|
||||
mShadowDpm.setPasswordMinimumSymbols(2);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "",
|
||||
"Must contain at least 2 special symbols",
|
||||
"Must be at least 6 characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowLockPatternUtils.class)
|
||||
public void processAndValidatePasswordRequirements_numericComplexNoMinComplexity_pinRequested() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
|
||||
/* passwordType= */ PASSWORD_QUALITY_NUMERIC,
|
||||
/* userEnteredPassword= */ "12345678",
|
||||
"Ascending, descending, or repeated sequence of digits isn't allowed");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowLockPatternUtils.class)
|
||||
public void processAndValidatePasswordRequirements_numericComplexNoMinComplexity_passwordRequested() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "12345678",
|
||||
"Ascending, descending, or repeated sequence of digits isn't allowed");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowLockPatternUtils.class)
|
||||
public void processAndValidatePasswordRequirements_numericComplexHighComplexity_pinRequested() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||
/* passwordType= */ PASSWORD_QUALITY_NUMERIC,
|
||||
/* userEnteredPassword= */ "12345678",
|
||||
"Ascending, descending, or repeated sequence of digits isn't allowed");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowLockPatternUtils.class)
|
||||
public void processAndValidatePasswordRequirements_numericHighComplexity_pinRequested() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||
/* passwordType= */ PASSWORD_QUALITY_NUMERIC,
|
||||
/* userEnteredPassword= */ "12345678",
|
||||
"Ascending, descending, or repeated sequence of digits isn't allowed");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowLockPatternUtils.class)
|
||||
public void processAndValidatePasswordRequirements_numericComplexLowComplexity_passwordRequested() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "12345678",
|
||||
"Must contain at least 1 letter");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_empty() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "",
|
||||
"Must contain at least 1 letter",
|
||||
"Must be at least 6 characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_numeric() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "1",
|
||||
"Must contain at least 1 letter",
|
||||
"Must be at least 6 characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_alphabetic() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "b",
|
||||
"Must be at least 6 characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_alphanumeric() {
|
||||
mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
|
||||
|
||||
assertPasswordValidationResult(
|
||||
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
|
||||
/* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
|
||||
/* userEnteredPassword= */ "b1",
|
||||
"Must be at least 6 characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertThat_chooseLockIconChanged_WhenFingerprintExtraSet() {
|
||||
ShadowDrawable drawable = setActivityAndGetIconDrawable(true);
|
||||
@@ -132,4 +357,18 @@ public class ChooseLockPasswordTest {
|
||||
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
|
||||
return Shadows.shadowOf(((GlifLayout) fragment.getView()).getIcon());
|
||||
}
|
||||
|
||||
private void assertPasswordValidationResult(@PasswordComplexity int minComplexity,
|
||||
int passwordType, String userEnteredPassword, String... expectedValidationResult) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(CONFIRM_CREDENTIALS, false);
|
||||
intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
|
||||
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
|
||||
ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
|
||||
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
|
||||
int validateResult = fragment.validatePassword(userEnteredPassword);
|
||||
String[] messages = fragment.convertErrorCodeToMessages(validateResult);
|
||||
|
||||
assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_DENIED;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
import static com.android.settings.password.PasswordUtils.getCallingAppLabel;
|
||||
import static com.android.settings.password.PasswordUtils.isCallingAppPermitted;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowActivityManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowActivityManager.class})
|
||||
public class PasswordUtilsTest {
|
||||
|
||||
private static final String PACKAGE_NAME = "com.android.app";
|
||||
private static final String PERMISSION = "com.testing.permission";
|
||||
private static final int UID = 1234;
|
||||
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private ApplicationInfo mApplicationInfo;
|
||||
@Mock
|
||||
private IActivityManager mActivityService;
|
||||
@Mock
|
||||
private IBinder mActivityToken;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
ShadowActivityManager.setService(mActivityService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCallingAppLabel_activityServiceThrowsRemoteException_returnsNull()
|
||||
throws Exception {
|
||||
when(mActivityService.getLaunchedFromPackage(mActivityToken))
|
||||
.thenThrow(new RemoteException());
|
||||
|
||||
assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCallingAppLabel_activityServiceReturnsSettingsApp_returnsNull()
|
||||
throws Exception {
|
||||
when(mActivityService.getLaunchedFromPackage(mActivityToken))
|
||||
.thenReturn("com.android.settings");
|
||||
|
||||
assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCallingAppLabel_packageManagerThrowsNameNotFound_returnsNull() throws Exception {
|
||||
when(mActivityService.getLaunchedFromPackage(mActivityToken))
|
||||
.thenReturn(PACKAGE_NAME);
|
||||
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||
.thenThrow(new NameNotFoundException());
|
||||
|
||||
assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCallingAppLabel_returnsLabel() throws Exception {
|
||||
when(mActivityService.getLaunchedFromPackage(mActivityToken))
|
||||
.thenReturn(PACKAGE_NAME);
|
||||
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||
.thenReturn(mApplicationInfo);
|
||||
when(mApplicationInfo.loadLabel(mPackageManager)).thenReturn("label");
|
||||
|
||||
assertThat(getCallingAppLabel(mContext, mActivityToken)).isEqualTo("label");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isCallingAppPermitted_permissionGranted_returnsTrue() throws Exception {
|
||||
when(mActivityService.getLaunchedFromUid(mActivityToken)).thenReturn(UID);
|
||||
when(mContext.checkPermission(PERMISSION, -1, UID)).thenReturn(PERMISSION_GRANTED);
|
||||
|
||||
assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isCallingAppPermitted_permissionDenied_returnsFalse() throws Exception {
|
||||
when(mActivityService.getLaunchedFromUid(mActivityToken)).thenReturn(UID);
|
||||
when(mContext.checkPermission(PERMISSION, -1, UID)).thenReturn(PERMISSION_DENIED);
|
||||
|
||||
assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isCallingAppPermitted_throwsRemoteException_returnsFalse() throws Exception {
|
||||
when(mActivityService.getLaunchedFromUid(mActivityToken)).thenThrow(new RemoteException());
|
||||
|
||||
assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isFalse();
|
||||
}
|
||||
}
|
@@ -16,6 +16,16 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_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_PASSWORD_COMPLEXITY;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
|
||||
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_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.ComponentName;
|
||||
@@ -23,6 +33,8 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowPasswordUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -31,11 +43,14 @@ import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowActivity;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class SetNewPasswordActivityTest {
|
||||
|
||||
private static final String APP_LABEL = "label";
|
||||
|
||||
private int mProvisioned;
|
||||
|
||||
@Before
|
||||
@@ -48,6 +63,7 @@ public class SetNewPasswordActivityTest {
|
||||
public void tearDown() {
|
||||
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, mProvisioned);
|
||||
ShadowPasswordUtils.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,4 +93,106 @@ public class SetNewPasswordActivityTest {
|
||||
assertThat(intent.getComponent())
|
||||
.isEqualTo(new ComponentName(activity, SetupChooseLockGeneric.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = {ShadowPasswordUtils.class})
|
||||
public void testLaunchChooseLock_setNewPasswordExtraWithoutPermission() {
|
||||
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
|
||||
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
|
||||
Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
|
||||
intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
|
||||
SetNewPasswordActivity activity =
|
||||
Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
|
||||
|
||||
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||
assertThat(shadowActivity.getNextStartedActivityForResult()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = {ShadowPasswordUtils.class})
|
||||
public void testLaunchChooseLock_setNewPasswordExtraWithPermission() {
|
||||
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
|
||||
ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
|
||||
Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
|
||||
intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
|
||||
SetNewPasswordActivity activity =
|
||||
Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
|
||||
|
||||
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||
Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
|
||||
assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
|
||||
assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
|
||||
.isEqualTo(PASSWORD_COMPLEXITY_HIGH);
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
|
||||
assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = {ShadowPasswordUtils.class})
|
||||
public void testLaunchChooseLock_setNewPasswordExtraInvalidValue() {
|
||||
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
|
||||
ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
|
||||
Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
|
||||
intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, -1);
|
||||
SetNewPasswordActivity activity =
|
||||
Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
|
||||
|
||||
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||
Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
|
||||
assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
|
||||
assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = {ShadowPasswordUtils.class})
|
||||
public void testLaunchChooseLock_setNewPasswordExtraNoneComplexity() {
|
||||
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
|
||||
ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
|
||||
Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
|
||||
intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
SetNewPasswordActivity activity =
|
||||
Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
|
||||
|
||||
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||
Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
|
||||
assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
|
||||
assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = {ShadowPasswordUtils.class})
|
||||
public void testLaunchChooseLock_setNewParentProfilePasswordExtraWithPermission() {
|
||||
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
|
||||
ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
|
||||
Settings.Global.DEVICE_PROVISIONED, 1);
|
||||
|
||||
Intent intent = new Intent(ACTION_SET_NEW_PARENT_PROFILE_PASSWORD);
|
||||
intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
|
||||
SetNewPasswordActivity activity =
|
||||
Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
|
||||
|
||||
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||
Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
|
||||
assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PARENT_PROFILE_PASSWORD);
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
|
||||
assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
|
||||
assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
|
||||
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowPasswordUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowActivity;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
ShadowUserManager.class,
|
||||
ShadowUtils.class,
|
||||
ShadowLockPatternUtils.class,
|
||||
})
|
||||
public class SetupChooseLockGenericTest {
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowPasswordUtils.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setupChooseLockGenericPasswordComplexityExtraWithoutPermission() {
|
||||
Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
|
||||
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
|
||||
SetupChooseLockGeneric activity =
|
||||
Robolectric.buildActivity(SetupChooseLockGeneric.class, intent).create().get();
|
||||
|
||||
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||
assertThat(shadowActivity.isFinishing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = {ShadowPasswordUtils.class})
|
||||
public void setupChooseLockGenericPasswordComplexityExtraWithPermission() {
|
||||
ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
|
||||
Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
|
||||
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
|
||||
SetupChooseLockGeneric activity =
|
||||
Robolectric.buildActivity(SetupChooseLockGeneric.class, intent).create().get();
|
||||
|
||||
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
|
||||
assertThat(shadowActivity.isFinishing()).isFalse();
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.testutils.shadow;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.IActivityManager;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
@@ -24,6 +25,7 @@ import org.robolectric.annotation.Implements;
|
||||
@Implements(ActivityManager.class)
|
||||
public class ShadowActivityManager {
|
||||
private static int sCurrentUserId = 0;
|
||||
private static IActivityManager sService = null;
|
||||
|
||||
@Implementation
|
||||
protected static int getCurrentUser() {
|
||||
@@ -33,4 +35,13 @@ public class ShadowActivityManager {
|
||||
public static void setCurrentUser(int userId) {
|
||||
sCurrentUserId = userId;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public static IActivityManager getService() {
|
||||
return sService;
|
||||
}
|
||||
|
||||
public static void setService(IActivityManager service) {
|
||||
sService = service;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package com.android.settings.testutils.shadow;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
@@ -23,6 +25,10 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev
|
||||
private boolean mIsAdminActiveAsUser = false;
|
||||
private ComponentName mDeviceOwnerComponentName;
|
||||
private int mDeviceOwnerUserId = -1;
|
||||
private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
|
||||
private int mPasswordMaxLength = 16;
|
||||
private int mPasswordMinLength = 0;
|
||||
private int mPasswordMinSymbols = 0;
|
||||
|
||||
public void setShortSupportMessageForUser(ComponentName admin, int userHandle, String message) {
|
||||
mSupportMessagesMap.put(Objects.hash(admin, userHandle), message);
|
||||
@@ -70,6 +76,42 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev
|
||||
mDeviceOwnerComponentName = admin;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public int getPasswordQuality(ComponentName admin, int userHandle) {
|
||||
return mPasswordMinQuality;
|
||||
}
|
||||
|
||||
public void setPasswordQuality(int quality) {
|
||||
mPasswordMinQuality = quality;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public int getPasswordMinimumLength(ComponentName admin, int userHandle) {
|
||||
return mPasswordMinLength;
|
||||
}
|
||||
|
||||
public void setPasswordMinimumLength(int length) {
|
||||
mPasswordMinLength = length;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public int getPasswordMinimumSymbols(ComponentName admin, int userHandle) {
|
||||
return mPasswordMinSymbols;
|
||||
}
|
||||
|
||||
public void setPasswordMinimumSymbols(int numOfSymbols) {
|
||||
mPasswordMinSymbols = numOfSymbols;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public int getPasswordMaximumLength(int quality) {
|
||||
return mPasswordMaxLength;
|
||||
}
|
||||
|
||||
public void setPasswordMaximumLength(int length) {
|
||||
mPasswordMaxLength = length;
|
||||
}
|
||||
|
||||
public static ShadowDevicePolicyManager getShadow() {
|
||||
return (ShadowDevicePolicyManager) Shadow.extract(
|
||||
RuntimeEnvironment.application.getSystemService(DevicePolicyManager.class));
|
||||
|
@@ -59,4 +59,14 @@ public class ShadowLockPatternUtils {
|
||||
public static void setDeviceEncryptionEnabled(boolean deviceEncryptionEnabled) {
|
||||
sDeviceEncryptionEnabled = deviceEncryptionEnabled;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected byte[] getPasswordHistoryHashFactor(String currentPassword, int userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected boolean checkPasswordHistory(String passwordToCheck, byte[] hashFactor, int userId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.testutils.shadow;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.android.settings.password.PasswordUtils;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Implements(PasswordUtils.class)
|
||||
public class ShadowPasswordUtils {
|
||||
|
||||
private static String sCallingAppLabel;
|
||||
private static Set<String> sGrantedPermissions;
|
||||
|
||||
public static void reset() {
|
||||
sCallingAppLabel = null;
|
||||
sGrantedPermissions = null;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected static boolean isCallingAppPermitted(Context context, IBinder activityToken,
|
||||
String permission) {
|
||||
if (sGrantedPermissions == null) {
|
||||
return false;
|
||||
}
|
||||
return sGrantedPermissions.contains(permission);
|
||||
}
|
||||
|
||||
public static void addGrantedPermission(String... permissions) {
|
||||
if (sGrantedPermissions == null) {
|
||||
sGrantedPermissions = new HashSet<>();
|
||||
}
|
||||
sGrantedPermissions.addAll(Arrays.asList(permissions));
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected static String getCallingAppLabel(Context context, IBinder activityToken) {
|
||||
return sCallingAppLabel;
|
||||
}
|
||||
|
||||
public static void setCallingAppLabel(String label) {
|
||||
sCallingAppLabel = label;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user