Merge "New extra for ACTION_SET_NEW_PASSWORD to specify the min complexity"

This commit is contained in:
TreeHugger Robot
2019-01-24 13:56:20 +00:00
committed by Android (Google) Code Review
18 changed files with 1280 additions and 97 deletions

View File

@@ -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);

View File

@@ -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));
}
/**

View File

@@ -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;
}
if (metrics.letters < mPasswordMinLetters) {
errorCode |= NOT_ENOUGH_LETTER;
}
if (metrics.upperCase < mPasswordMinUpperCase) {
errorCode |= NOT_ENOUGH_UPPER_CASE;
}
if (metrics.lowerCase < mPasswordMinLowerCase) {
errorCode |= NOT_ENOUGH_LOWER_CASE;
}
if (metrics.symbols < mPasswordMinSymbols) {
errorCode |= NOT_ENOUGH_SYMBOLS;
}
if (metrics.numeric < mPasswordMinNumeric) {
errorCode |= NOT_ENOUGH_DIGITS;
}
if (metrics.nonLetter < mPasswordMinNonLetter) {
errorCode |= NOT_ENOUGH_NON_LETTER;
}
return errorCode;
}

View File

@@ -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

View 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);
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}