Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507 Merged-In: Ie9d2c4d6d4618a167af1c5627d5d7918a404f398 Change-Id: I2ae428e37fd96226ce4e06032e2c0beaacbd0301
This commit is contained in:
@@ -16,27 +16,18 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.DialogInterface;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
|
||||
import android.hardware.biometrics.BiometricPrompt.AuthenticationResult;
|
||||
import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -79,6 +70,20 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
});
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
mClientExecutor.execute(() -> {
|
||||
mClientCallback.onAuthenticationFailed();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSystemEvent(int event) {
|
||||
mClientExecutor.execute(() -> {
|
||||
mClientCallback.onSystemEvent(event);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private final DialogInterface.OnClickListener mNegativeButtonListener =
|
||||
@@ -91,20 +96,6 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO(b/123378871): Remove when moved.
|
||||
private final IBiometricConfirmDeviceCredentialCallback mCancelCallback
|
||||
= new IBiometricConfirmDeviceCredentialCallback.Stub() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
final Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
activity.finish();
|
||||
} else {
|
||||
Log.e(TAG, "Activity null!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param bundle Bundle passed from {@link BiometricPrompt.Builder#buildIntent()}
|
||||
* @return
|
||||
@@ -133,14 +124,11 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
|
||||
private void cleanup() {
|
||||
if (getActivity() != null) {
|
||||
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
|
||||
getActivity().getSupportFragmentManager().beginTransaction().remove(this)
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAuthenticating() {
|
||||
return mAuthenticating;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -150,38 +138,18 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(getContext())
|
||||
.setTitle(mBundle.getString(BiometricPrompt.KEY_TITLE))
|
||||
.setUseDefaultTitle() // use default title if title is null/empty
|
||||
.setFromConfirmDeviceCredential()
|
||||
.setDeviceCredentialAllowed(true)
|
||||
.setSubtitle(mBundle.getString(BiometricPrompt.KEY_SUBTITLE))
|
||||
.setDescription(mBundle.getString(BiometricPrompt.KEY_DESCRIPTION))
|
||||
.setConfirmationRequired(
|
||||
mBundle.getBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true));
|
||||
|
||||
final LockPatternUtils lockPatternUtils = FeatureFactory.getFactory(
|
||||
getContext())
|
||||
.getSecurityFeatureProvider()
|
||||
.getLockPatternUtils(getContext());
|
||||
|
||||
switch (lockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)) {
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
|
||||
builder.setNegativeButton(getResources().getString(
|
||||
R.string.confirm_device_credential_pattern),
|
||||
mClientExecutor, mNegativeButtonListener);
|
||||
break;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
|
||||
builder.setNegativeButton(getResources().getString(
|
||||
R.string.confirm_device_credential_pin),
|
||||
mClientExecutor, mNegativeButtonListener);
|
||||
break;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
|
||||
builder.setNegativeButton(getResources().getString(
|
||||
R.string.confirm_device_credential_password),
|
||||
mClientExecutor, mNegativeButtonListener);
|
||||
break;
|
||||
}
|
||||
.setTextForDeviceCredential(
|
||||
mBundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE),
|
||||
mBundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE),
|
||||
mBundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION))
|
||||
.setConfirmationRequired(mBundle.getBoolean(
|
||||
BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true))
|
||||
.setDisallowBiometricsIfPolicyExists(mBundle.getBoolean(
|
||||
BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false))
|
||||
.setReceiveSystemEvents(true);
|
||||
|
||||
mBiometricPrompt = builder.build();
|
||||
mCancellationSignal = new CancellationSignal();
|
||||
@@ -189,7 +157,7 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
// TODO: CC doesn't use crypto for now
|
||||
mAuthenticating = true;
|
||||
mBiometricPrompt.authenticateUser(mCancellationSignal, mClientExecutor,
|
||||
mAuthenticationCallback, mUserId, mCancelCallback);
|
||||
mAuthenticationCallback, mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,16 +36,13 @@ import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.hardware.face.Face;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintManager.RemovalCallback;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.service.persistentdata.PersistentDataBlockManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
@@ -61,6 +58,7 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.settings.EncryptionInterstitial;
|
||||
import com.android.settings.EventLogTags;
|
||||
import com.android.settings.R;
|
||||
@@ -74,10 +72,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;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
public class ChooseLockGeneric extends SettingsActivity {
|
||||
public static final String CONFIRM_CREDENTIALS = "confirm_credentials";
|
||||
@@ -112,6 +108,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
|
||||
public static final String HIDE_DISABLED_PREFS = "hide_disabled_prefs";
|
||||
public static final String TAG_FRP_WARNING_DIALOG = "frp_warning_dialog";
|
||||
public static final String KEY_LOCK_SETTINGS_FOOTER ="lock_settings_footer";
|
||||
|
||||
/**
|
||||
* Boolean extra determining whether a "screen lock options" button should be shown. This
|
||||
@@ -146,13 +143,13 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
static final int SKIP_FINGERPRINT_REQUEST = 104;
|
||||
|
||||
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
|
||||
private DevicePolicyManager mDPM;
|
||||
private DevicePolicyManager mDpm;
|
||||
private boolean mHasChallenge = false;
|
||||
private long mChallenge;
|
||||
private boolean mPasswordConfirmed = false;
|
||||
private boolean mWaitingForConfirmation = false;
|
||||
private boolean mForChangeCredRequiredForBoot = false;
|
||||
private byte[] mUserPassword;
|
||||
private LockscreenCredential mUserPassword;
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private FingerprintManager mFingerprintManager;
|
||||
private FaceManager mFaceManager;
|
||||
@@ -161,6 +158,8 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
private boolean mIsSetNewPassword = false;
|
||||
private UserManager mUserManager;
|
||||
private ChooseLockGenericController mController;
|
||||
private int mUnificationProfileId = UserHandle.USER_NULL;
|
||||
private LockscreenCredential mUnificationProfileCredential;
|
||||
|
||||
/**
|
||||
* From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_REQUESTED_MIN_COMPLEXITY}.
|
||||
@@ -188,53 +187,63 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final Activity activity = getActivity();
|
||||
if (!Utils.isDeviceProvisioned(activity) && !canRunBeforeDeviceProvisioned()) {
|
||||
final Bundle arguments = getArguments();
|
||||
if (!WizardManagerHelper.isDeviceProvisioned(activity)
|
||||
&& !canRunBeforeDeviceProvisioned()) {
|
||||
Log.i(TAG, "Refusing to start because device is not provisioned");
|
||||
activity.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
String chooseLockAction = getActivity().getIntent().getAction();
|
||||
mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
|
||||
mFaceManager = Utils.getFaceManagerOrNull(getActivity());
|
||||
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
|
||||
mLockPatternUtils = new LockPatternUtils(getActivity());
|
||||
final Intent intent = activity.getIntent();
|
||||
String chooseLockAction = intent.getAction();
|
||||
mFingerprintManager = Utils.getFingerprintManagerOrNull(activity);
|
||||
mFaceManager = Utils.getFaceManagerOrNull(activity);
|
||||
mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(activity);
|
||||
mLockPatternUtils = new LockPatternUtils(activity);
|
||||
mIsSetNewPassword = ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction)
|
||||
|| ACTION_SET_NEW_PASSWORD.equals(chooseLockAction);
|
||||
|
||||
// Defaults to needing to confirm credentials
|
||||
final boolean confirmCredentials = getActivity().getIntent()
|
||||
final boolean confirmCredentials = intent
|
||||
.getBooleanExtra(CONFIRM_CREDENTIALS, true);
|
||||
if (getActivity() instanceof ChooseLockGeneric.InternalActivity) {
|
||||
if (activity instanceof ChooseLockGeneric.InternalActivity) {
|
||||
mPasswordConfirmed = !confirmCredentials;
|
||||
mUserPassword = getActivity().getIntent().getByteArrayExtra(
|
||||
mUserPassword = intent.getParcelableExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
}
|
||||
|
||||
mHasChallenge = getActivity().getIntent().getBooleanExtra(
|
||||
mHasChallenge = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
mChallenge = getActivity().getIntent().getLongExtra(
|
||||
mChallenge = intent.getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
|
||||
mForFingerprint = getActivity().getIntent().getBooleanExtra(
|
||||
mForFingerprint = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = getActivity().getIntent().getBooleanExtra(
|
||||
mForFace = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
mRequestedMinComplexity = getActivity().getIntent()
|
||||
mRequestedMinComplexity = intent
|
||||
.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
mCallerAppName =
|
||||
getActivity().getIntent().getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
|
||||
mIsCallingAppAdmin = getActivity().getIntent()
|
||||
intent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
|
||||
mIsCallingAppAdmin = intent
|
||||
.getBooleanExtra(EXTRA_KEY_IS_CALLING_APP_ADMIN, /* defValue= */ false);
|
||||
mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
|
||||
mForChangeCredRequiredForBoot = arguments != null && arguments.getBoolean(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
|
||||
mUserManager = UserManager.get(getActivity());
|
||||
mUserManager = UserManager.get(activity);
|
||||
|
||||
if (arguments != null) {
|
||||
mUnificationProfileCredential = (LockscreenCredential) arguments.getParcelable(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL);
|
||||
mUnificationProfileId = arguments.getInt(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID,
|
||||
UserHandle.USER_NULL);
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
|
||||
mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION);
|
||||
if (mUserPassword == null) {
|
||||
mUserPassword = savedInstanceState.getByteArray(
|
||||
mUserPassword = savedInstanceState.getParcelable(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
}
|
||||
}
|
||||
@@ -244,19 +253,19 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
// from Settings app itself.
|
||||
// c) Otherwise, use UserHandle.myUserId().
|
||||
mUserId = Utils.getSecureTargetUser(
|
||||
getActivity().getActivityToken(),
|
||||
UserManager.get(getActivity()),
|
||||
getArguments(),
|
||||
getActivity().getIntent().getExtras()).getIdentifier();
|
||||
activity.getActivityToken(),
|
||||
UserManager.get(activity),
|
||||
arguments,
|
||||
intent.getExtras()).getIdentifier();
|
||||
mController = new ChooseLockGenericController(
|
||||
getContext(), mUserId, mRequestedMinComplexity, mLockPatternUtils);
|
||||
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
|
||||
&& UserManager.get(getActivity()).isManagedProfile(mUserId)
|
||||
&& UserManager.get(activity).isManagedProfile(mUserId)
|
||||
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
|
||||
getActivity().setTitle(R.string.lock_settings_picker_title_profile);
|
||||
activity.setTitle(R.string.lock_settings_picker_title_profile);
|
||||
}
|
||||
|
||||
mManagedPasswordProvider = ManagedLockPasswordProvider.get(getActivity(), mUserId);
|
||||
mManagedPasswordProvider = ManagedLockPasswordProvider.get(activity, mUserId);
|
||||
|
||||
if (mPasswordConfirmed) {
|
||||
updatePreferencesOrFinish(savedInstanceState != null);
|
||||
@@ -266,9 +275,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
}
|
||||
} else if (!mWaitingForConfirmation) {
|
||||
ChooseLockSettingsHelper helper =
|
||||
new ChooseLockSettingsHelper(this.getActivity(), this);
|
||||
new ChooseLockSettingsHelper(activity, this);
|
||||
boolean managedProfileWithUnifiedLock =
|
||||
UserManager.get(getActivity()).isManagedProfile(mUserId)
|
||||
UserManager.get(activity).isManagedProfile(mUserId)
|
||||
&& !mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId);
|
||||
boolean skipConfirmation = managedProfileWithUnifiedLock && !mIsSetNewPassword;
|
||||
if (skipConfirmation
|
||||
@@ -284,7 +293,12 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
}
|
||||
|
||||
protected boolean canRunBeforeDeviceProvisioned() {
|
||||
return false;
|
||||
PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
|
||||
getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
|
||||
|
||||
// Can only run during setup if factory reset protection has already been cleared
|
||||
// or if the device does not support FRP.
|
||||
return (pdbm == null || pdbm.getDataBlockSize() == 0);
|
||||
}
|
||||
|
||||
protected Class<? extends ChooseLockGeneric.InternalActivity> getInternalActivityClass() {
|
||||
@@ -309,8 +323,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference) {
|
||||
final String key = preference.getKey();
|
||||
writePreferenceClickMetric(preference);
|
||||
|
||||
final String key = preference.getKey();
|
||||
if (!isUnlockMethodSecure(key) && mLockPatternUtils.isSecure(mUserId)) {
|
||||
// Show the disabling FRP warning only when the user is switching from a secure
|
||||
// unlock method to an insecure one
|
||||
@@ -393,11 +408,11 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
|
||||
mPasswordConfirmed = true;
|
||||
mUserPassword = data != null
|
||||
? data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)
|
||||
? data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)
|
||||
: null;
|
||||
updatePreferencesOrFinish(false /* isRecreatingActivity */);
|
||||
if (mForChangeCredRequiredForBoot) {
|
||||
if (!(mUserPassword == null || mUserPassword.length == 0)) {
|
||||
if (mUserPassword != null && !mUserPassword.isNone()) {
|
||||
maybeEnableEncryption(
|
||||
mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId), false);
|
||||
} else {
|
||||
@@ -460,7 +475,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
|
||||
outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation);
|
||||
if (mUserPassword != null) {
|
||||
outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mUserPassword);
|
||||
outState.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mUserPassword);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,11 +514,12 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
protected void addPreferences() {
|
||||
addPreferencesFromResource(R.xml.security_settings_picker);
|
||||
|
||||
final Preference footer = findPreference(KEY_LOCK_SETTINGS_FOOTER);
|
||||
if (!TextUtils.isEmpty(mCallerAppName) && !mIsCallingAppAdmin) {
|
||||
FooterPreferenceMixinCompat footerMixin =
|
||||
new FooterPreferenceMixinCompat(this, getSettingsLifecycle());
|
||||
FooterPreference footer = footerMixin.createFooterPreference();
|
||||
footer.setVisible(true);
|
||||
footer.setTitle(getFooterString());
|
||||
} else {
|
||||
footer.setVisible(false);
|
||||
}
|
||||
|
||||
// Used for testing purposes
|
||||
@@ -628,9 +644,22 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
boolean hideDisabled) {
|
||||
final PreferenceScreen entries = getPreferenceScreen();
|
||||
|
||||
int adminEnforcedQuality = mDPM.getPasswordQuality(null, mUserId);
|
||||
int adminEnforcedQuality = mDpm.getPasswordQuality(null, mUserId);
|
||||
EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(
|
||||
getActivity(), mUserId);
|
||||
// If we are to unify a work challenge at the end of the credential enrollment, manually
|
||||
// merge any password policy from that profile here, so we are enrolling a compliant
|
||||
// password. This is because once unified, the profile's password policy will
|
||||
// be enforced on the new credential.
|
||||
if (mUnificationProfileId != UserHandle.USER_NULL) {
|
||||
int profileEnforceQuality = mDpm.getPasswordQuality(null, mUnificationProfileId);
|
||||
if (profileEnforceQuality > adminEnforcedQuality) {
|
||||
adminEnforcedQuality = profileEnforceQuality;
|
||||
enforcedAdmin = EnforcedAdmin.combine(enforcedAdmin,
|
||||
RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(
|
||||
getActivity(), mUnificationProfileId));
|
||||
}
|
||||
}
|
||||
|
||||
for (ScreenLockType lock : ScreenLockType.values()) {
|
||||
String key = lock.preferenceKey;
|
||||
@@ -682,7 +711,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
setPreferenceSummary(ScreenLockType.MANAGED, R.string.secure_lock_encryption_warning);
|
||||
}
|
||||
|
||||
protected Intent getLockManagedPasswordIntent(byte[] password) {
|
||||
protected Intent getLockManagedPasswordIntent(LockscreenCredential password) {
|
||||
return mManagedPasswordProvider.createIntent(false, password);
|
||||
}
|
||||
|
||||
@@ -700,6 +729,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
if (mUserPassword != null) {
|
||||
builder.setPassword(mUserPassword);
|
||||
}
|
||||
if (mUnificationProfileId != UserHandle.USER_NULL) {
|
||||
builder.setProfileToUnify(mUnificationProfileId, mUnificationProfileCredential);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -715,6 +747,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
if (mUserPassword != null) {
|
||||
builder.setPattern(mUserPassword);
|
||||
}
|
||||
if (mUnificationProfileId != UserHandle.USER_NULL) {
|
||||
builder.setProfileToUnify(mUnificationProfileId, mUnificationProfileCredential);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -724,30 +759,6 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
unlockMethodIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps track of the biometric removal status. When all biometrics (including managed
|
||||
* profiles) are removed, finishes the activity. Otherwise, it's possible the UI still
|
||||
* shows enrolled biometrics due to the async remove.
|
||||
*/
|
||||
private class RemovalTracker {
|
||||
boolean mFingerprintDone;
|
||||
boolean mFaceDone;
|
||||
|
||||
void onFingerprintDone() {
|
||||
mFingerprintDone = true;
|
||||
if (mFingerprintDone && mFaceDone) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
void onFaceDone() {
|
||||
mFaceDone = true;
|
||||
if (mFingerprintDone && mFaceDone) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes an activity to change the user's pattern, password or PIN based on given quality
|
||||
* and minimum quality specified by DevicePolicyManager. If quality is
|
||||
@@ -781,21 +792,20 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
}
|
||||
|
||||
if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
|
||||
mChooseLockSettingsHelper.utils().clearLock(mUserPassword, mUserId);
|
||||
// Clearing of user biometrics when screen lock is cleared is done at
|
||||
// LockSettingsService.removeBiometricsForUser().
|
||||
if (mUserPassword != null) {
|
||||
// No need to call setLockCredential if the user currently doesn't
|
||||
// have a password
|
||||
mChooseLockSettingsHelper.utils().setLockCredential(
|
||||
LockscreenCredential.createNone(), mUserPassword, mUserId);
|
||||
}
|
||||
mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled, mUserId);
|
||||
getActivity().setResult(Activity.RESULT_OK);
|
||||
removeAllBiometricsForUserAndFinish(mUserId);
|
||||
} else {
|
||||
removeAllBiometricsForUserAndFinish(mUserId);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAllBiometricsForUserAndFinish(final int userId) {
|
||||
final RemovalTracker tracker = new RemovalTracker();
|
||||
removeAllFingerprintForUserAndFinish(userId, tracker);
|
||||
removeAllFaceForUserAndFinish(userId, tracker);
|
||||
}
|
||||
|
||||
private Intent getIntentForUnlockMethod(int quality) {
|
||||
Intent intent = null;
|
||||
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
|
||||
@@ -808,131 +818,17 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
return intent;
|
||||
}
|
||||
|
||||
private void removeAllFingerprintForUserAndFinish(final int userId,
|
||||
RemovalTracker tracker) {
|
||||
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
|
||||
if (mFingerprintManager.hasEnrolledFingerprints(userId)) {
|
||||
mFingerprintManager.setActiveUser(userId);
|
||||
// For the purposes of M and N, groupId is the same as userId.
|
||||
final int groupId = userId;
|
||||
Fingerprint finger = new Fingerprint(null, groupId, 0, 0);
|
||||
mFingerprintManager.remove(finger, userId,
|
||||
new RemovalCallback() {
|
||||
@Override
|
||||
public void onRemovalError(Fingerprint fp, int errMsgId,
|
||||
CharSequence errString) {
|
||||
Log.e(TAG, String.format(
|
||||
"Can't remove fingerprint %d in group %d. Reason: %s",
|
||||
fp.getBiometricId(), fp.getGroupId(), errString));
|
||||
// TODO: need to proceed with the removal of managed profile
|
||||
// fingerprints and finish() gracefully.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemovalSucceeded(Fingerprint fp, int remaining) {
|
||||
if (remaining == 0) {
|
||||
removeManagedProfileFingerprintsAndFinishIfNecessary(userId,
|
||||
tracker);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// No fingerprints in this user, we may also want to delete managed profile
|
||||
// fingerprints
|
||||
removeManagedProfileFingerprintsAndFinishIfNecessary(userId, tracker);
|
||||
}
|
||||
} else {
|
||||
// The removal callback will call finish, once all fingerprints are removed.
|
||||
// We need to wait for that to occur, otherwise, the UI will still show that
|
||||
// fingerprints exist even though they are (about to) be removed depending on
|
||||
// the race condition.
|
||||
tracker.onFingerprintDone();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeManagedProfileFingerprintsAndFinishIfNecessary(final int parentUserId,
|
||||
RemovalTracker tracker) {
|
||||
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
|
||||
mFingerprintManager.setActiveUser(UserHandle.myUserId());
|
||||
}
|
||||
boolean hasChildProfile = false;
|
||||
if (!mUserManager.getUserInfo(parentUserId).isManagedProfile()) {
|
||||
// Current user is primary profile, remove work profile fingerprints if necessary
|
||||
final List<UserInfo> profiles = mUserManager.getProfiles(parentUserId);
|
||||
final int profilesSize = profiles.size();
|
||||
for (int i = 0; i < profilesSize; i++) {
|
||||
final UserInfo userInfo = profiles.get(i);
|
||||
if (userInfo.isManagedProfile() && !mLockPatternUtils
|
||||
.isSeparateProfileChallengeEnabled(userInfo.id)) {
|
||||
removeAllFingerprintForUserAndFinish(userInfo.id, tracker);
|
||||
hasChildProfile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasChildProfile) {
|
||||
tracker.onFingerprintDone();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: figure out how to eliminate duplicated code. It's a bit hard due to the async-ness
|
||||
private void removeAllFaceForUserAndFinish(final int userId, RemovalTracker tracker) {
|
||||
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
|
||||
if (mFaceManager.hasEnrolledTemplates(userId)) {
|
||||
mFaceManager.setActiveUser(userId);
|
||||
Face face = new Face(null, 0, 0);
|
||||
mFaceManager.remove(face, userId,
|
||||
new FaceManager.RemovalCallback() {
|
||||
@Override
|
||||
public void onRemovalError(Face face, int errMsgId, CharSequence err) {
|
||||
Log.e(TAG, String.format("Can't remove face %d. Reason: %s",
|
||||
face.getBiometricId(), err));
|
||||
}
|
||||
@Override
|
||||
public void onRemovalSucceeded(Face face, int remaining) {
|
||||
if (remaining == 0) {
|
||||
removeManagedProfileFacesAndFinishIfNecessary(userId, tracker);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// No faces in this user, we may also want to delete managed profile faces
|
||||
removeManagedProfileFacesAndFinishIfNecessary(userId, tracker);
|
||||
}
|
||||
} else {
|
||||
tracker.onFaceDone();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: figure out how to eliminate duplicated code. It's a bit hard due to the async-ness
|
||||
private void removeManagedProfileFacesAndFinishIfNecessary(final int parentUserId,
|
||||
RemovalTracker tracker) {
|
||||
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
|
||||
mFaceManager.setActiveUser(UserHandle.myUserId());
|
||||
}
|
||||
boolean hasChildProfile = false;
|
||||
if (!mUserManager.getUserInfo(parentUserId).isManagedProfile()) {
|
||||
// Current user is primary profile, remove work profile faces if necessary
|
||||
final List<UserInfo> profiles = mUserManager.getProfiles(parentUserId);
|
||||
final int profilesSize = profiles.size();
|
||||
for (int i = 0; i < profilesSize; i++) {
|
||||
final UserInfo userInfo = profiles.get(i);
|
||||
if (userInfo.isManagedProfile() && !mLockPatternUtils
|
||||
.isSeparateProfileChallengeEnabled(userInfo.id)) {
|
||||
removeAllFaceForUserAndFinish(userInfo.id, tracker);
|
||||
hasChildProfile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasChildProfile) {
|
||||
tracker.onFaceDone();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mUserPassword != null) {
|
||||
mUserPassword.zeroize();
|
||||
}
|
||||
// Force a garbage collection immediately to remove remnant of user password shards
|
||||
// from memory.
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -92,6 +92,7 @@ public class ChooseLockGenericController {
|
||||
*/
|
||||
public int upgradeQuality(int quality) {
|
||||
// Compare specified quality and dpm quality
|
||||
// TODO(b/142781408): convert from quality to credential type once PIN is supported.
|
||||
int dpmUpgradedQuality = Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
|
||||
return Math.max(dpmUpgradedQuality,
|
||||
PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
|
||||
|
||||
@@ -17,13 +17,23 @@
|
||||
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.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
|
||||
import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE;
|
||||
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_DIGITS;
|
||||
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_LETTERS;
|
||||
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_LOWER_CASE;
|
||||
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_NON_DIGITS;
|
||||
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_NON_LETTER;
|
||||
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_SYMBOLS;
|
||||
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_UPPER_CASE;
|
||||
import static com.android.internal.widget.PasswordValidationError.RECENTLY_USED;
|
||||
import static com.android.internal.widget.PasswordValidationError.TOO_LONG;
|
||||
import static com.android.internal.widget.PasswordValidationError.TOO_SHORT;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
@@ -38,6 +48,7 @@ import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.UserHandle;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.Selection;
|
||||
@@ -51,7 +62,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ImeAwareEditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
@@ -63,6 +74,8 @@ 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.LockscreenCredential;
|
||||
import com.android.internal.widget.PasswordValidationError;
|
||||
import com.android.internal.widget.TextViewInputDisabler;
|
||||
import com.android.settings.EncryptionInterstitial;
|
||||
import com.android.settings.R;
|
||||
@@ -71,14 +84,13 @@ import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.notification.RedactionInterstitial;
|
||||
import com.android.settings.widget.ImeAwareEditText;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ChooseLockPassword extends SettingsActivity {
|
||||
@@ -124,7 +136,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setPassword(byte[] password) {
|
||||
public IntentBuilder setPassword(LockscreenCredential password) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
|
||||
return this;
|
||||
}
|
||||
@@ -144,6 +156,18 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the launch such that at the end of the password enrollment, one of its
|
||||
* managed profile (specified by {@code profileId}) will have its lockscreen unified
|
||||
* to the parent user. The profile's current lockscreen credential needs to be specified by
|
||||
* {@code credential}.
|
||||
*/
|
||||
public IntentBuilder setProfileToUnify(int profileId, LockscreenCredential credential) {
|
||||
mIntent.putExtra(EXTRA_KEY_UNIFICATION_PROFILE_ID, profileId);
|
||||
mIntent.putExtra(EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL, credential);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Intent build() {
|
||||
return mIntent;
|
||||
}
|
||||
@@ -180,30 +204,26 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
|
||||
public static class ChooseLockPasswordFragment extends InstrumentedFragment
|
||||
implements OnEditorActionListener, TextWatcher, SaveAndFinishWorker.Listener {
|
||||
private static final String KEY_FIRST_PIN = "first_pin";
|
||||
private static final String KEY_FIRST_PASSWORD = "first_password";
|
||||
private static final String KEY_UI_STAGE = "ui_stage";
|
||||
private static final String KEY_CURRENT_PASSWORD = "current_password";
|
||||
private static final String KEY_CURRENT_CREDENTIAL = "current_credential";
|
||||
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
|
||||
|
||||
private byte[] mCurrentPassword;
|
||||
private byte[] mChosenPassword;
|
||||
private LockscreenCredential mCurrentCredential;
|
||||
private LockscreenCredential mChosenPassword;
|
||||
private boolean mHasChallenge;
|
||||
private long mChallenge;
|
||||
private ImeAwareEditText mPasswordEntry;
|
||||
private TextViewInputDisabler mPasswordEntryInputDisabler;
|
||||
private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
|
||||
private int mPasswordMaxLength = 16;
|
||||
private int mPasswordMinLetters = 0;
|
||||
private int mPasswordMinUpperCase = 0;
|
||||
private int mPasswordMinLowerCase = 0;
|
||||
private int mPasswordMinSymbols = 0;
|
||||
private int mPasswordMinNumeric = 0;
|
||||
private int mPasswordMinNonLetter = 0;
|
||||
private int mPasswordMinLengthToFulfillAllPolicies = 0;
|
||||
private boolean mPasswordNumSequenceAllowed = true;
|
||||
@PasswordComplexity private int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
|
||||
|
||||
// Minimum password metrics enforced by admins.
|
||||
private PasswordMetrics mMinMetrics;
|
||||
private List<PasswordValidationError> mValidationErrors;
|
||||
|
||||
@PasswordComplexity private int mMinComplexity = PASSWORD_COMPLEXITY_NONE;
|
||||
protected int mUserId;
|
||||
private byte[] mPasswordHistoryHashFactor;
|
||||
private int mUnificationProfileId = UserHandle.USER_NULL;
|
||||
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private SaveAndFinishWorker mSaveAndFinishWorker;
|
||||
@@ -215,7 +235,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
protected boolean mForFingerprint;
|
||||
protected boolean mForFace;
|
||||
|
||||
private byte[] mFirstPin;
|
||||
private LockscreenCredential mFirstPassword;
|
||||
private RecyclerView mPasswordRestrictionView;
|
||||
protected boolean mIsAlphaMode;
|
||||
protected FooterButton mSkipOrClearButton;
|
||||
@@ -227,28 +247,6 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
private static final int CONFIRM_EXISTING_REQUEST = 58;
|
||||
static final int RESULT_FINISHED = RESULT_FIRST_USER;
|
||||
|
||||
private static final int MIN_LETTER_IN_PASSWORD = 0;
|
||||
private static final int MIN_UPPER_LETTERS_IN_PASSWORD = 1;
|
||||
private static final int MIN_LOWER_LETTERS_IN_PASSWORD = 2;
|
||||
private static final int MIN_SYMBOLS_IN_PASSWORD = 3;
|
||||
private static final int MIN_NUMBER_IN_PASSWORD = 4;
|
||||
private static final int MIN_NON_LETTER_IN_PASSWORD = 5;
|
||||
|
||||
// Error code returned from {@link #validatePassword(byte[])}.
|
||||
static final int NO_ERROR = 0;
|
||||
static final int CONTAIN_INVALID_CHARACTERS = 1 << 0;
|
||||
static final int TOO_SHORT = 1 << 1;
|
||||
static final int TOO_LONG = 1 << 2;
|
||||
static final int CONTAIN_NON_DIGITS = 1 << 3;
|
||||
static final int CONTAIN_SEQUENTIAL_DIGITS = 1 << 4;
|
||||
static final int RECENTLY_USED = 1 << 5;
|
||||
static final int NOT_ENOUGH_LETTER = 1 << 6;
|
||||
static final int NOT_ENOUGH_UPPER_CASE = 1 << 7;
|
||||
static final int NOT_ENOUGH_LOWER_CASE = 1 << 8;
|
||||
static final int NOT_ENOUGH_DIGITS = 1 << 9;
|
||||
static final int NOT_ENOUGH_SYMBOLS = 1 << 10;
|
||||
static final int NOT_ENOUGH_NON_LETTER = 1 << 11;
|
||||
|
||||
/**
|
||||
* Keep track internally of where the user is in choosing a pattern.
|
||||
*/
|
||||
@@ -380,13 +378,24 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mForFingerprint = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
mRequestedMinComplexity = intent.getIntExtra(
|
||||
mMinComplexity = intent.getIntExtra(
|
||||
EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
mRequestedQuality = Math.max(
|
||||
intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality),
|
||||
mLockPatternUtils.getRequestedPasswordQuality(mUserId));
|
||||
|
||||
loadDpmPasswordRequirements();
|
||||
mRequestedQuality = intent.getIntExtra(
|
||||
LockPatternUtils.PASSWORD_TYPE_KEY, PASSWORD_QUALITY_NUMERIC);
|
||||
mUnificationProfileId = intent.getIntExtra(
|
||||
EXTRA_KEY_UNIFICATION_PROFILE_ID, UserHandle.USER_NULL);
|
||||
|
||||
mMinMetrics = mLockPatternUtils.getRequestedPasswordMetrics(mUserId);
|
||||
// If we are to unify a work challenge at the end of the credential enrollment, manually
|
||||
// merge any password policy from that profile here, so we are enrolling a compliant
|
||||
// password. This is because once unified, the profile's password policy will
|
||||
// be enforced on the new credential.
|
||||
if (mUnificationProfileId != UserHandle.USER_NULL) {
|
||||
mMinMetrics.maxWith(
|
||||
mLockPatternUtils.getRequestedPasswordMetrics(mUnificationProfileId));
|
||||
}
|
||||
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
|
||||
|
||||
if (intent.getBooleanExtra(
|
||||
@@ -394,13 +403,13 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
SaveAndFinishWorker w = new SaveAndFinishWorker();
|
||||
final boolean required = getActivity().getIntent().getBooleanExtra(
|
||||
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
|
||||
byte[] currentBytes = intent.getByteArrayExtra(
|
||||
LockscreenCredential currentCredential = intent.getParcelableExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
|
||||
w.setBlocking(true);
|
||||
w.setListener(this);
|
||||
w.start(mChooseLockSettingsHelper.utils(), required, false, 0,
|
||||
currentBytes, currentBytes, mRequestedQuality, mUserId);
|
||||
currentCredential, currentCredential, mUserId);
|
||||
}
|
||||
mTextChangedHandler = new TextChangedHandler();
|
||||
}
|
||||
@@ -467,6 +476,13 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
int currentType = mPasswordEntry.getInputType();
|
||||
mPasswordEntry.setInputType(mIsAlphaMode ? currentType
|
||||
: (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
|
||||
if (mIsAlphaMode) {
|
||||
mPasswordEntry.setContentDescription(
|
||||
getString(R.string.unlock_set_unlock_password_title));
|
||||
} else {
|
||||
mPasswordEntry.setContentDescription(
|
||||
getString(R.string.unlock_set_unlock_pin_title));
|
||||
}
|
||||
// Can't set via XML since setInputType resets the fontFamily to null
|
||||
mPasswordEntry.setTypeface(Typeface.create(
|
||||
getContext().getString(com.android.internal.R.string.config_headlineFontFamily),
|
||||
@@ -475,7 +491,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
Intent intent = getActivity().getIntent();
|
||||
final boolean confirmCredentials = intent.getBooleanExtra(
|
||||
ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
|
||||
mCurrentPassword = intent.getByteArrayExtra(
|
||||
mCurrentCredential = intent.getParcelableExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
mHasChallenge = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
@@ -490,15 +506,15 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
} else {
|
||||
|
||||
// restore from previous state
|
||||
mFirstPin = savedInstanceState.getByteArray(KEY_FIRST_PIN);
|
||||
mFirstPassword = savedInstanceState.getParcelable(KEY_FIRST_PASSWORD);
|
||||
final String state = savedInstanceState.getString(KEY_UI_STAGE);
|
||||
if (state != null) {
|
||||
mUiStage = Stage.valueOf(state);
|
||||
updateStage(mUiStage);
|
||||
}
|
||||
|
||||
if (mCurrentPassword == null) {
|
||||
mCurrentPassword = savedInstanceState.getByteArray(KEY_CURRENT_PASSWORD);
|
||||
if (mCurrentCredential == null) {
|
||||
mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_CREDENTIAL);
|
||||
}
|
||||
|
||||
// Re-attach to the exiting worker if there is one.
|
||||
@@ -514,6 +530,19 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mCurrentCredential != null) {
|
||||
mCurrentCredential.zeroize();
|
||||
}
|
||||
// Force a garbage collection immediately to remove remnant of user password shards
|
||||
// from memory.
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
}
|
||||
|
||||
protected int getStageType() {
|
||||
return mForFingerprint ? Stage.TYPE_FINGERPRINT :
|
||||
mForFace ? Stage.TYPE_FACE :
|
||||
@@ -556,8 +585,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(KEY_UI_STAGE, mUiStage.name());
|
||||
outState.putByteArray(KEY_FIRST_PIN, mFirstPin);
|
||||
outState.putByteArray(KEY_CURRENT_PASSWORD, mCurrentPassword);
|
||||
outState.putParcelable(KEY_FIRST_PASSWORD, mFirstPassword);
|
||||
outState.putParcelable(KEY_CURRENT_CREDENTIAL, mCurrentCredential);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -570,7 +599,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
getActivity().setResult(RESULT_FINISHED);
|
||||
getActivity().finish();
|
||||
} else {
|
||||
mCurrentPassword = data.getByteArrayExtra(
|
||||
mCurrentCredential = data.getParcelableExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
}
|
||||
break;
|
||||
@@ -594,208 +623,23 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
|
||||
*/
|
||||
private void loadDpmPasswordRequirements() {
|
||||
final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
|
||||
if (dpmPasswordQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
|
||||
mPasswordNumSequenceAllowed = false;
|
||||
}
|
||||
mPasswordMinLength = Math.max(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
|
||||
mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
|
||||
mPasswordMaxLength = mLockPatternUtils.getMaximumPasswordLength(mRequestedQuality);
|
||||
mPasswordMinLetters = mLockPatternUtils.getRequestedPasswordMinimumLetters(mUserId);
|
||||
mPasswordMinUpperCase = mLockPatternUtils.getRequestedPasswordMinimumUpperCase(mUserId);
|
||||
mPasswordMinLowerCase = mLockPatternUtils.getRequestedPasswordMinimumLowerCase(mUserId);
|
||||
mPasswordMinNumeric = mLockPatternUtils.getRequestedPasswordMinimumNumeric(mUserId);
|
||||
mPasswordMinSymbols = mLockPatternUtils.getRequestedPasswordMinimumSymbols(mUserId);
|
||||
mPasswordMinNonLetter = mLockPatternUtils.getRequestedPasswordMinimumNonLetter(mUserId);
|
||||
|
||||
// Modify the value based on dpm policy
|
||||
switch (dpmPasswordQuality) {
|
||||
case PASSWORD_QUALITY_ALPHABETIC:
|
||||
if (mPasswordMinLetters == 0) {
|
||||
mPasswordMinLetters = 1;
|
||||
}
|
||||
break;
|
||||
case PASSWORD_QUALITY_ALPHANUMERIC:
|
||||
if (mPasswordMinLetters == 0) {
|
||||
mPasswordMinLetters = 1;
|
||||
}
|
||||
if (mPasswordMinNumeric == 0) {
|
||||
mPasswordMinNumeric = 1;
|
||||
}
|
||||
break;
|
||||
case PASSWORD_QUALITY_COMPLEX:
|
||||
// Reserve all the requirements.
|
||||
break;
|
||||
default:
|
||||
mPasswordMinNumeric = 0;
|
||||
mPasswordMinLetters = 0;
|
||||
mPasswordMinUpperCase = 0;
|
||||
mPasswordMinLowerCase = 0;
|
||||
mPasswordMinSymbols = 0;
|
||||
mPasswordMinNonLetter = 0;
|
||||
}
|
||||
|
||||
mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the dpm requirements and the min complexity requirements.
|
||||
* Validates PIN/Password and returns the validation result and updates mValidationErrors
|
||||
* and mPasswordReused to reflect validation results.
|
||||
*
|
||||
* <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.
|
||||
* @param credential credential the user typed in.
|
||||
* @return whether password satisfies all the requirements.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int validatePassword(byte[] password) {
|
||||
int errorCode = NO_ERROR;
|
||||
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
|
||||
mergeMinComplexityAndDpmRequirements(metrics.quality);
|
||||
|
||||
if (password == null || password.length < mPasswordMinLength) {
|
||||
if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) {
|
||||
errorCode |= TOO_SHORT;
|
||||
}
|
||||
} else if (password.length > mPasswordMaxLength) {
|
||||
errorCode |= TOO_LONG;
|
||||
} else {
|
||||
// The length requirements are fulfilled.
|
||||
if (!mPasswordNumSequenceAllowed
|
||||
&& !requiresLettersOrSymbols()
|
||||
&& metrics.numeric == password.length) {
|
||||
// Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// Is the password recently used?
|
||||
if (mLockPatternUtils.checkPasswordHistory(password, getPasswordHistoryHashFactor(),
|
||||
mUserId)) {
|
||||
errorCode |= RECENTLY_USED;
|
||||
}
|
||||
boolean validatePassword(LockscreenCredential credential) {
|
||||
final byte[] password = credential.getCredential();
|
||||
mValidationErrors = PasswordMetrics.validatePassword(
|
||||
mMinMetrics, mMinComplexity, !mIsAlphaMode, password);
|
||||
if (mValidationErrors.isEmpty() && mLockPatternUtils.checkPasswordHistory(
|
||||
password, getPasswordHistoryHashFactor(), mUserId)) {
|
||||
mValidationErrors =
|
||||
Collections.singletonList(new PasswordValidationError(RECENTLY_USED));
|
||||
}
|
||||
|
||||
// Allow non-control Latin-1 characters only.
|
||||
for (int i = 0; i < password.length; i++) {
|
||||
char c = (char) password[i];
|
||||
if (c < 32 || c > 127) {
|
||||
errorCode |= CONTAIN_INVALID_CHARACTERS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure no non-digits if we are requesting numbers. This shouldn't be possible unless
|
||||
// user finds some way to bring up soft keyboard.
|
||||
if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC
|
||||
|| mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
|
||||
if (metrics.letters > 0 || metrics.symbols > 0) {
|
||||
errorCode |= CONTAIN_NON_DIGITS;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return mValidationErrors.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -805,7 +649,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
private byte[] getPasswordHistoryHashFactor() {
|
||||
if (mPasswordHistoryHashFactor == null) {
|
||||
mPasswordHistoryHashFactor = mLockPatternUtils.getPasswordHistoryHashFactor(
|
||||
mCurrentPassword, mUserId);
|
||||
mCurrentCredential != null ? mCurrentCredential
|
||||
: LockscreenCredential.createNone(), mUserId);
|
||||
}
|
||||
return mPasswordHistoryHashFactor;
|
||||
}
|
||||
@@ -813,20 +658,22 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
public void handleNext() {
|
||||
if (mSaveAndFinishWorker != null) return;
|
||||
// TODO(b/120484642): This is a point of entry for passwords from the UI
|
||||
mChosenPassword = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText());
|
||||
if (mChosenPassword == null || mChosenPassword.length == 0) {
|
||||
final Editable passwordText = mPasswordEntry.getText();
|
||||
if (TextUtils.isEmpty(passwordText)) {
|
||||
return;
|
||||
}
|
||||
mChosenPassword = mIsAlphaMode ? LockscreenCredential.createPassword(passwordText)
|
||||
: LockscreenCredential.createPin(passwordText);
|
||||
if (mUiStage == Stage.Introduction) {
|
||||
if (validatePassword(mChosenPassword) == NO_ERROR) {
|
||||
mFirstPin = mChosenPassword;
|
||||
if (validatePassword(mChosenPassword)) {
|
||||
mFirstPassword = mChosenPassword;
|
||||
mPasswordEntry.setText("");
|
||||
updateStage(Stage.NeedToConfirm);
|
||||
} else {
|
||||
Arrays.fill(mChosenPassword, (byte) 0);
|
||||
mChosenPassword.zeroize();
|
||||
}
|
||||
} else if (mUiStage == Stage.NeedToConfirm) {
|
||||
if (Arrays.equals(mFirstPin, mChosenPassword)) {
|
||||
if (mChosenPassword.equals(mFirstPassword)) {
|
||||
startSaveAndFinish();
|
||||
} else {
|
||||
CharSequence tmp = mPasswordEntry.getText();
|
||||
@@ -834,7 +681,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
Selection.setSelection((Spannable) tmp, 0, tmp.length());
|
||||
}
|
||||
updateStage(Stage.ConfirmWrong);
|
||||
Arrays.fill(mChosenPassword, (byte) 0);
|
||||
mChosenPassword.zeroize();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -867,79 +714,79 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param errorCode error code returned from {@link #validatePassword(String)}.
|
||||
* @param errorCode error code returned from password validation.
|
||||
* @return an array of messages describing the error, important messages come first.
|
||||
*/
|
||||
String[] convertErrorCodeToMessages(int errorCode) {
|
||||
String[] convertErrorCodeToMessages() {
|
||||
List<String> messages = new ArrayList<>();
|
||||
if ((errorCode & CONTAIN_INVALID_CHARACTERS) > 0) {
|
||||
messages.add(getString(R.string.lockpassword_illegal_character));
|
||||
for (PasswordValidationError error : mValidationErrors) {
|
||||
switch (error.errorCode) {
|
||||
case CONTAINS_INVALID_CHARACTERS:
|
||||
messages.add(getString(R.string.lockpassword_illegal_character));
|
||||
break;
|
||||
case NOT_ENOUGH_UPPER_CASE:
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_uppercase,
|
||||
error.requirement, error.requirement));
|
||||
break;
|
||||
case NOT_ENOUGH_LOWER_CASE:
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_lowercase,
|
||||
error.requirement, error.requirement));
|
||||
break;
|
||||
case NOT_ENOUGH_LETTERS:
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_letters,
|
||||
error.requirement, error.requirement));
|
||||
break;
|
||||
case NOT_ENOUGH_DIGITS:
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_numeric,
|
||||
error.requirement, error.requirement));
|
||||
break;
|
||||
case NOT_ENOUGH_SYMBOLS:
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_symbols,
|
||||
error.requirement, error.requirement));
|
||||
break;
|
||||
case NOT_ENOUGH_NON_LETTER:
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_nonletter,
|
||||
error.requirement, error.requirement));
|
||||
break;
|
||||
case NOT_ENOUGH_NON_DIGITS:
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_nonnumerical,
|
||||
error.requirement, error.requirement));
|
||||
break;
|
||||
case TOO_SHORT:
|
||||
messages.add(getResources().getQuantityString(
|
||||
mIsAlphaMode
|
||||
? R.plurals.lockpassword_password_too_short
|
||||
: R.plurals.lockpassword_pin_too_short,
|
||||
error.requirement, error.requirement));
|
||||
break;
|
||||
case TOO_LONG:
|
||||
messages.add(getResources().getQuantityString(
|
||||
mIsAlphaMode
|
||||
? R.plurals.lockpassword_password_too_long
|
||||
: R.plurals.lockpassword_pin_too_long,
|
||||
error.requirement + 1, error.requirement + 1));
|
||||
break;
|
||||
case CONTAINS_SEQUENCE:
|
||||
messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
|
||||
break;
|
||||
case RECENTLY_USED:
|
||||
messages.add(getString(mIsAlphaMode
|
||||
? R.string.lockpassword_password_recently_used
|
||||
: R.string.lockpassword_pin_recently_used));
|
||||
break;
|
||||
default:
|
||||
Log.wtf(TAG, "unknown error validating password: " + error);
|
||||
}
|
||||
}
|
||||
if ((errorCode & CONTAIN_NON_DIGITS) > 0) {
|
||||
messages.add(getString(R.string.lockpassword_pin_contains_non_digits));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_UPPER_CASE) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase,
|
||||
mPasswordMinUpperCase));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_LOWER_CASE) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase,
|
||||
mPasswordMinLowerCase));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_LETTER) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters,
|
||||
mPasswordMinLetters));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_DIGITS) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric,
|
||||
mPasswordMinNumeric));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_SYMBOLS) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols,
|
||||
mPasswordMinSymbols));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_NON_LETTER) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter,
|
||||
mPasswordMinNonLetter));
|
||||
}
|
||||
if ((errorCode & TOO_SHORT) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
mIsAlphaMode
|
||||
? R.plurals.lockpassword_password_too_short
|
||||
: R.plurals.lockpassword_pin_too_short,
|
||||
mPasswordMinLength,
|
||||
mPasswordMinLength));
|
||||
}
|
||||
if ((errorCode & TOO_LONG) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
mIsAlphaMode
|
||||
? R.plurals.lockpassword_password_too_long
|
||||
: R.plurals.lockpassword_pin_too_long,
|
||||
mPasswordMaxLength + 1,
|
||||
mPasswordMaxLength + 1));
|
||||
}
|
||||
if ((errorCode & CONTAIN_SEQUENTIAL_DIGITS) > 0) {
|
||||
messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
|
||||
}
|
||||
if ((errorCode & RECENTLY_USED) > 0) {
|
||||
messages.add(getString((mIsAlphaMode) ? R.string.lockpassword_password_recently_used
|
||||
: R.string.lockpassword_pin_recently_used));
|
||||
}
|
||||
return messages.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private int getMinLengthToFulfillAllPolicies() {
|
||||
final int minLengthForLetters = Math.max(mPasswordMinLetters,
|
||||
mPasswordMinUpperCase + mPasswordMinLowerCase);
|
||||
final int minLengthForNonLetters = Math.max(mPasswordMinNonLetter,
|
||||
mPasswordMinSymbols + mPasswordMinNumeric);
|
||||
return minLengthForLetters + minLengthForNonLetters;
|
||||
return messages.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -947,21 +794,24 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
*/
|
||||
protected void updateUi() {
|
||||
final boolean canInput = mSaveAndFinishWorker == null;
|
||||
byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText());
|
||||
final int length = password.length;
|
||||
|
||||
LockscreenCredential password = mIsAlphaMode
|
||||
? LockscreenCredential.createPasswordOrNone(mPasswordEntry.getText())
|
||||
: LockscreenCredential.createPinOrNone(mPasswordEntry.getText());
|
||||
final int length = password.size();
|
||||
if (mUiStage == Stage.Introduction) {
|
||||
mPasswordRestrictionView.setVisibility(View.VISIBLE);
|
||||
final int errorCode = validatePassword(password);
|
||||
String[] messages = convertErrorCodeToMessages(errorCode);
|
||||
final boolean passwordCompliant = validatePassword(password);
|
||||
String[] messages = convertErrorCodeToMessages();
|
||||
// Update the fulfillment of requirements.
|
||||
mPasswordRequirementAdapter.setRequirements(messages);
|
||||
// Enable/Disable the next button accordingly.
|
||||
setNextEnabled(errorCode == NO_ERROR);
|
||||
setNextEnabled(passwordCompliant);
|
||||
} else {
|
||||
// Hide password requirement view when we are just asking user to confirm the pw.
|
||||
mPasswordRestrictionView.setVisibility(View.GONE);
|
||||
setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, getStageType())));
|
||||
setNextEnabled(canInput && length >= mPasswordMinLength);
|
||||
setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
|
||||
mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
|
||||
}
|
||||
int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
|
||||
@@ -974,7 +824,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
|
||||
setNextText(mUiStage.buttonText);
|
||||
mPasswordEntryInputDisabler.setInputEnabled(canInput);
|
||||
Arrays.fill(password, (byte) 0);
|
||||
password.zeroize();
|
||||
}
|
||||
|
||||
protected int toVisibility(boolean visibleOrGone) {
|
||||
@@ -1023,10 +873,18 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
|
||||
getFragmentManager().executePendingTransactions();
|
||||
|
||||
final boolean required = getActivity().getIntent().getBooleanExtra(
|
||||
final Intent intent = getActivity().getIntent();
|
||||
final boolean required = intent.getBooleanExtra(
|
||||
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
|
||||
if (mUnificationProfileId != UserHandle.USER_NULL) {
|
||||
try (LockscreenCredential profileCredential = (LockscreenCredential)
|
||||
intent.getParcelableExtra(EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL)) {
|
||||
mSaveAndFinishWorker.setProfileToUnify(mUnificationProfileId,
|
||||
profileCredential);
|
||||
}
|
||||
}
|
||||
mSaveAndFinishWorker.start(mLockPatternUtils, required, mHasChallenge, mChallenge,
|
||||
mChosenPassword, mCurrentPassword, mRequestedQuality, mUserId);
|
||||
mChosenPassword, mCurrentCredential, mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1034,13 +892,13 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
getActivity().setResult(RESULT_FINISHED, resultData);
|
||||
|
||||
if (mChosenPassword != null) {
|
||||
Arrays.fill(mChosenPassword, (byte) 0);
|
||||
mChosenPassword.zeroize();
|
||||
}
|
||||
if (mCurrentPassword != null) {
|
||||
Arrays.fill(mCurrentPassword, (byte) 0);
|
||||
if (mCurrentCredential != null) {
|
||||
mCurrentCredential.zeroize();
|
||||
}
|
||||
if (mFirstPin != null) {
|
||||
Arrays.fill(mFirstPin, (byte) 0);
|
||||
if (mFirstPassword != null) {
|
||||
mFirstPassword.zeroize();
|
||||
}
|
||||
|
||||
mPasswordEntry.setText("");
|
||||
@@ -1081,18 +939,18 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
|
||||
public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
|
||||
|
||||
private byte[] mChosenPassword;
|
||||
private byte[] mCurrentPassword;
|
||||
private int mRequestedQuality;
|
||||
private LockscreenCredential mChosenPassword;
|
||||
private LockscreenCredential mCurrentCredential;
|
||||
|
||||
public void start(LockPatternUtils utils, boolean required,
|
||||
boolean hasChallenge, long challenge,
|
||||
byte[] chosenPassword, byte[] currentPassword, int requestedQuality, int userId) {
|
||||
LockscreenCredential chosenPassword, LockscreenCredential currentCredential,
|
||||
int userId) {
|
||||
prepare(utils, required, hasChallenge, challenge, userId);
|
||||
|
||||
mChosenPassword = chosenPassword;
|
||||
mCurrentPassword = currentPassword;
|
||||
mRequestedQuality = requestedQuality;
|
||||
mCurrentCredential = currentCredential != null ? currentCredential
|
||||
: LockscreenCredential.createNone();
|
||||
mUserId = userId;
|
||||
|
||||
start();
|
||||
@@ -1100,13 +958,16 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
|
||||
@Override
|
||||
protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
|
||||
final boolean success = mUtils.saveLockPassword(
|
||||
mChosenPassword, mCurrentPassword, mRequestedQuality, mUserId);
|
||||
final boolean success = mUtils.setLockCredential(
|
||||
mChosenPassword, mCurrentCredential, mUserId);
|
||||
if (success) {
|
||||
unifyProfileCredentialIfRequested();
|
||||
}
|
||||
Intent result = null;
|
||||
if (success && mHasChallenge) {
|
||||
byte[] token;
|
||||
try {
|
||||
token = mUtils.verifyPassword(mChosenPassword, mChallenge, mUserId);
|
||||
token = mUtils.verifyCredential(mChosenPassword, mChallenge, mUserId);
|
||||
} catch (RequestThrottledException e) {
|
||||
token = null;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
@@ -23,6 +26,7 @@ import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.TypedValue;
|
||||
@@ -30,18 +34,19 @@ import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
|
||||
import com.android.internal.widget.LockPatternView;
|
||||
import com.android.internal.widget.LockPatternView.Cell;
|
||||
import com.android.internal.widget.LockPatternView.DisplayMode;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.settings.EncryptionInterstitial;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
@@ -49,14 +54,13 @@ import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.notification.RedactionInterstitial;
|
||||
import com.android.settings.password.ChooseLockPassword.IntentBuilder;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -116,7 +120,7 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setPattern(byte[] pattern) {
|
||||
public IntentBuilder setPattern(LockscreenCredential pattern) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pattern);
|
||||
return this;
|
||||
}
|
||||
@@ -131,6 +135,19 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the launch such that at the end of the pattern enrollment, one of its
|
||||
* managed profile (specified by {@code profileId}) will have its lockscreen unified
|
||||
* to the parent user. The profile's current lockscreen credential needs to be specified by
|
||||
* {@code credential}.
|
||||
*/
|
||||
public IntentBuilder setProfileToUnify(int profileId, LockscreenCredential credential) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID, profileId);
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL,
|
||||
credential);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Intent build() {
|
||||
return mIntent;
|
||||
}
|
||||
@@ -188,7 +205,7 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
|
||||
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
|
||||
|
||||
private byte[] mCurrentPattern;
|
||||
private LockscreenCredential mCurrentCredential;
|
||||
private boolean mHasChallenge;
|
||||
private long mChallenge;
|
||||
protected TextView mTitleText;
|
||||
@@ -198,7 +215,7 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
protected TextView mFooterText;
|
||||
protected FooterButton mSkipOrClearButton;
|
||||
private FooterButton mNextButton;
|
||||
protected List<LockPatternView.Cell> mChosenPattern = null;
|
||||
@VisibleForTesting protected LockscreenCredential mChosenPattern;
|
||||
private ColorStateList mDefaultHeaderColorList;
|
||||
|
||||
// ScrollView that contains title and header, only exist in land mode
|
||||
@@ -225,7 +242,7 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
getActivity().setResult(RESULT_FINISHED);
|
||||
getActivity().finish();
|
||||
} else {
|
||||
mCurrentPattern = data.getByteArrayExtra(
|
||||
mCurrentCredential = data.getParcelableExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
}
|
||||
|
||||
@@ -262,16 +279,19 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
|
||||
if (mChosenPattern == null) throw new IllegalStateException(
|
||||
"null chosen pattern in stage 'need to confirm");
|
||||
if (mChosenPattern.equals(pattern)) {
|
||||
updateStage(Stage.ChoiceConfirmed);
|
||||
} else {
|
||||
updateStage(Stage.ConfirmWrong);
|
||||
try (LockscreenCredential confirmPattern =
|
||||
LockscreenCredential.createPattern(pattern)) {
|
||||
if (mChosenPattern.equals(confirmPattern)) {
|
||||
updateStage(Stage.ChoiceConfirmed);
|
||||
} else {
|
||||
updateStage(Stage.ConfirmWrong);
|
||||
}
|
||||
}
|
||||
} else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
|
||||
if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
|
||||
updateStage(Stage.ChoiceTooShort);
|
||||
} else {
|
||||
mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
|
||||
mChosenPattern = LockscreenCredential.createPattern(pattern);
|
||||
updateStage(Stage.FirstChoiceValid);
|
||||
}
|
||||
} else {
|
||||
@@ -458,12 +478,12 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
SaveAndFinishWorker w = new SaveAndFinishWorker();
|
||||
final boolean required = getActivity().getIntent().getBooleanExtra(
|
||||
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
|
||||
byte[] current = intent.getByteArrayExtra(
|
||||
LockscreenCredential current = intent.getParcelableExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
w.setBlocking(true);
|
||||
w.setListener(this);
|
||||
w.start(mChooseLockSettingsHelper.utils(), required,
|
||||
false, 0, LockPatternUtils.byteArrayToPattern(current), current, mUserId);
|
||||
false, 0, current, current, mUserId);
|
||||
}
|
||||
mForFingerprint = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
@@ -541,8 +561,8 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
final boolean confirmCredentials = getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
|
||||
Intent intent = getActivity().getIntent();
|
||||
mCurrentPattern =
|
||||
intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
mCurrentCredential =
|
||||
intent.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
mHasChallenge = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
|
||||
@@ -565,13 +585,10 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
}
|
||||
} else {
|
||||
// restore from previous state
|
||||
final byte[] pattern = savedInstanceState.getByteArray(KEY_PATTERN_CHOICE);
|
||||
if (pattern != null) {
|
||||
mChosenPattern = LockPatternUtils.byteArrayToPattern(pattern);
|
||||
}
|
||||
mChosenPattern = savedInstanceState.getParcelable(KEY_PATTERN_CHOICE);
|
||||
|
||||
if (mCurrentPattern == null) {
|
||||
mCurrentPattern = savedInstanceState.getByteArray(KEY_CURRENT_PATTERN);
|
||||
if (mCurrentCredential == null) {
|
||||
mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_PATTERN);
|
||||
}
|
||||
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
|
||||
|
||||
@@ -600,13 +617,29 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mCurrentCredential != null) {
|
||||
mCurrentCredential.zeroize();
|
||||
}
|
||||
// Force a garbage collection immediately to remove remnant of user password shards
|
||||
// from memory.
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
}
|
||||
|
||||
protected Intent getRedactionInterstitialIntent(Context context) {
|
||||
return RedactionInterstitial.createStartIntent(context, mUserId);
|
||||
}
|
||||
|
||||
public void handleLeftButton() {
|
||||
if (mUiStage.leftMode == LeftButtonMode.Retry) {
|
||||
mChosenPattern = null;
|
||||
if (mChosenPattern != null) {
|
||||
mChosenPattern.zeroize();
|
||||
mChosenPattern = null;
|
||||
}
|
||||
mLockPatternView.clearPattern();
|
||||
updateStage(Stage.Introduction);
|
||||
} else {
|
||||
@@ -667,12 +700,11 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
|
||||
outState.putInt(KEY_UI_STAGE, mUiStage.ordinal());
|
||||
if (mChosenPattern != null) {
|
||||
outState.putByteArray(KEY_PATTERN_CHOICE,
|
||||
LockPatternUtils.patternToByteArray(mChosenPattern));
|
||||
outState.putParcelable(KEY_PATTERN_CHOICE, mChosenPattern);
|
||||
}
|
||||
|
||||
if (mCurrentPattern != null) {
|
||||
outState.putByteArray(KEY_CURRENT_PATTERN, mCurrentPattern);
|
||||
if (mCurrentCredential != null) {
|
||||
outState.putParcelable(KEY_CURRENT_PATTERN, mCurrentCredential);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,18 +841,31 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
|
||||
getFragmentManager().executePendingTransactions();
|
||||
|
||||
final boolean required = getActivity().getIntent().getBooleanExtra(
|
||||
final Intent intent = getActivity().getIntent();
|
||||
final boolean required = intent.getBooleanExtra(
|
||||
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
|
||||
if (intent.hasExtra(EXTRA_KEY_UNIFICATION_PROFILE_ID)) {
|
||||
try (LockscreenCredential profileCredential = (LockscreenCredential)
|
||||
intent.getParcelableExtra(EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL)) {
|
||||
mSaveAndFinishWorker.setProfileToUnify(
|
||||
intent.getIntExtra(EXTRA_KEY_UNIFICATION_PROFILE_ID,
|
||||
UserHandle.USER_NULL),
|
||||
profileCredential);
|
||||
}
|
||||
}
|
||||
mSaveAndFinishWorker.start(mChooseLockSettingsHelper.utils(), required,
|
||||
mHasChallenge, mChallenge, mChosenPattern, mCurrentPattern, mUserId);
|
||||
mHasChallenge, mChallenge, mChosenPattern, mCurrentCredential, mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
|
||||
getActivity().setResult(RESULT_FINISHED, resultData);
|
||||
|
||||
if (mCurrentPattern != null) {
|
||||
Arrays.fill(mCurrentPattern, (byte) 0);
|
||||
if (mChosenPattern != null) {
|
||||
mChosenPattern.zeroize();
|
||||
}
|
||||
if (mCurrentCredential != null) {
|
||||
mCurrentCredential.zeroize();
|
||||
}
|
||||
|
||||
if (!wasSecureBefore) {
|
||||
@@ -835,16 +880,17 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
|
||||
public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
|
||||
|
||||
private List<LockPatternView.Cell> mChosenPattern;
|
||||
private byte[] mCurrentPattern;
|
||||
private LockscreenCredential mChosenPattern;
|
||||
private LockscreenCredential mCurrentCredential;
|
||||
private boolean mLockVirgin;
|
||||
|
||||
public void start(LockPatternUtils utils, boolean credentialRequired,
|
||||
boolean hasChallenge, long challenge,
|
||||
List<LockPatternView.Cell> chosenPattern, byte[] currentPattern, int userId) {
|
||||
public void start(LockPatternUtils utils, boolean credentialRequired, boolean hasChallenge,
|
||||
long challenge, LockscreenCredential chosenPattern,
|
||||
LockscreenCredential currentCredential, int userId) {
|
||||
prepare(utils, credentialRequired, hasChallenge, challenge, userId);
|
||||
|
||||
mCurrentPattern = currentPattern;
|
||||
mCurrentCredential = currentCredential != null ? currentCredential
|
||||
: LockscreenCredential.createNone();
|
||||
mChosenPattern = chosenPattern;
|
||||
mUserId = userId;
|
||||
|
||||
@@ -856,12 +902,16 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
@Override
|
||||
protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
|
||||
final int userId = mUserId;
|
||||
final boolean success = mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId);
|
||||
final boolean success = mUtils.setLockCredential(mChosenPattern, mCurrentCredential,
|
||||
userId);
|
||||
if (success) {
|
||||
unifyProfileCredentialIfRequested();
|
||||
}
|
||||
Intent result = null;
|
||||
if (success && mHasChallenge) {
|
||||
byte[] token;
|
||||
try {
|
||||
token = mUtils.verifyPattern(mChosenPattern, mChallenge, userId);
|
||||
token = mUtils.verifyCredential(mChosenPattern, mChallenge, userId);
|
||||
} catch (RequestThrottledException e) {
|
||||
token = null;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import androidx.fragment.app.Fragment;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
@@ -49,6 +50,17 @@ public final class ChooseLockSettingsHelper {
|
||||
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
|
||||
public static final String EXTRA_KEY_FOREGROUND_ONLY = "foreground_only";
|
||||
|
||||
/**
|
||||
* When EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL and EXTRA_KEY_UNIFICATION_PROFILE_ID are
|
||||
* provided to ChooseLockGeneric as fragment arguments {@link SubSettingLauncher#setArguments},
|
||||
* at the end of the password change flow, the supplied profile user
|
||||
* (EXTRA_KEY_UNIFICATION_PROFILE_ID) will be unified to its parent. The current profile
|
||||
* password is supplied by EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL.
|
||||
*/
|
||||
public static final String EXTRA_KEY_UNIFICATION_PROFILE_ID = "unification_profile_id";
|
||||
public static final String EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL =
|
||||
"unification_profile_credential";
|
||||
|
||||
/**
|
||||
* Intent extra for passing the requested min password complexity to later steps in the set new
|
||||
* screen lock flow.
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.app.admin.DevicePolicyManager;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
@@ -35,6 +36,7 @@ import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
@@ -52,6 +54,13 @@ import java.util.concurrent.Executor;
|
||||
public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
public static final String TAG = ConfirmDeviceCredentialActivity.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* If the intent is sent from {@link com.android.systemui.keyguard.WorkLockActivityController}
|
||||
* then check for device policy management flags.
|
||||
*/
|
||||
public static final String EXTRA_FROM_WORK_LOCK_ACTIVITY_CONTROLLER =
|
||||
"from_work_lock_activity_controller";
|
||||
|
||||
// The normal flow that apps go through
|
||||
private static final int CREDENTIAL_NORMAL = 1;
|
||||
// Unlocks the managed profile when the primary profile is unlocked
|
||||
@@ -90,51 +99,68 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
private TrustManager mTrustManager;
|
||||
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
|
||||
private Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private boolean mIsFallback; // BiometricPrompt fallback
|
||||
private boolean mCCLaunched;
|
||||
private Context mContext;
|
||||
private boolean mCheckDevicePolicyManager;
|
||||
|
||||
private String mTitle;
|
||||
private String mDetails;
|
||||
private int mUserId;
|
||||
private int mCredentialMode;
|
||||
private boolean mGoingToBackground;
|
||||
private boolean mWaitingForBiometricCallback;
|
||||
|
||||
private Executor mExecutor = (runnable -> {
|
||||
mHandler.post(runnable);
|
||||
});
|
||||
|
||||
private AuthenticationCallback mAuthenticationCallback = new AuthenticationCallback() {
|
||||
@Override
|
||||
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
|
||||
if (!mGoingToBackground) {
|
||||
mWaitingForBiometricCallback = false;
|
||||
if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED
|
||||
|| errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) {
|
||||
if (mIsFallback) {
|
||||
mBiometricManager.onConfirmDeviceCredentialError(
|
||||
errorCode, getStringForError(errorCode));
|
||||
}
|
||||
finish();
|
||||
} else {
|
||||
// All other errors go to some version of CC
|
||||
showConfirmCredentials();
|
||||
}
|
||||
} else if (mWaitingForBiometricCallback) { // mGoingToBackground is true
|
||||
mWaitingForBiometricCallback = false;
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
|
||||
mWaitingForBiometricCallback = false;
|
||||
mTrustManager.setDeviceLockedForUser(mUserId, false);
|
||||
|
||||
final boolean isStrongAuth = result.getAuthenticationType()
|
||||
== BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL;
|
||||
ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils, mUserManager,
|
||||
mUserId);
|
||||
mDevicePolicyManager, mUserId, isStrongAuth);
|
||||
ConfirmDeviceCredentialUtils.checkForPendingIntent(
|
||||
ConfirmDeviceCredentialActivity.this);
|
||||
|
||||
if (mIsFallback) {
|
||||
mBiometricManager.onConfirmDeviceCredentialSuccess();
|
||||
}
|
||||
|
||||
setResult(Activity.RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
mWaitingForBiometricCallback = false;
|
||||
mDevicePolicyManager.reportFailedBiometricAttempt(mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSystemEvent(int event) {
|
||||
Log.d(TAG, "SystemEvent: " + event);
|
||||
switch (event) {
|
||||
case BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL:
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private String getStringForError(int errorCode) {
|
||||
@@ -152,6 +178,9 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
|
||||
mBiometricManager = getSystemService(BiometricManager.class);
|
||||
mDevicePolicyManager = getSystemService(DevicePolicyManager.class);
|
||||
mUserManager = UserManager.get(this);
|
||||
@@ -159,6 +188,9 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
mLockPatternUtils = new LockPatternUtils(this);
|
||||
|
||||
Intent intent = getIntent();
|
||||
mContext = this;
|
||||
mCheckDevicePolicyManager = intent
|
||||
.getBooleanExtra(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
|
||||
mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
|
||||
mDetails = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
|
||||
String alternateButton = intent.getStringExtra(
|
||||
@@ -180,19 +212,26 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
if ((mTitle == null) && isManagedProfile) {
|
||||
mTitle = getTitleFromOrganizationName(mUserId);
|
||||
}
|
||||
|
||||
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
|
||||
final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
|
||||
|
||||
Bundle bpBundle =
|
||||
intent.getBundleExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE);
|
||||
if (bpBundle != null) {
|
||||
mIsFallback = true;
|
||||
mTitle = bpBundle.getString(BiometricPrompt.KEY_TITLE);
|
||||
mDetails = bpBundle.getString(BiometricPrompt.KEY_SUBTITLE);
|
||||
} else {
|
||||
bpBundle = new Bundle();
|
||||
bpBundle.putString(BiometricPrompt.KEY_TITLE, mTitle);
|
||||
bpBundle.putString(BiometricPrompt.KEY_DESCRIPTION, mDetails);
|
||||
final Bundle bpBundle = new Bundle();
|
||||
bpBundle.putString(BiometricPrompt.KEY_TITLE, mTitle);
|
||||
bpBundle.putString(BiometricPrompt.KEY_DESCRIPTION, mDetails);
|
||||
bpBundle.putBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS,
|
||||
mCheckDevicePolicyManager);
|
||||
|
||||
final @LockPatternUtils.CredentialType int credentialType = Utils.getCredentialType(
|
||||
mContext, effectiveUserId);
|
||||
if (mTitle == null) {
|
||||
bpBundle.putString(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE,
|
||||
getTitleFromCredentialType(credentialType, isManagedProfile));
|
||||
}
|
||||
if (mDetails == null) {
|
||||
bpBundle.putString(BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE,
|
||||
getDetailsFromCredentialType(credentialType, isManagedProfile));
|
||||
}
|
||||
|
||||
boolean launchedBiometric = false;
|
||||
@@ -230,6 +269,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
finish();
|
||||
} else if (launchedBiometric) {
|
||||
// Keep this activity alive until BiometricPrompt goes away
|
||||
mWaitingForBiometricCallback = true;
|
||||
} else {
|
||||
Log.d(TAG, "No pattern, password or PIN set.");
|
||||
setResult(Activity.RESULT_OK);
|
||||
@@ -237,6 +277,44 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private String getTitleFromCredentialType(@LockPatternUtils.CredentialType int credentialType,
|
||||
boolean isManagedProfile) {
|
||||
switch (credentialType) {
|
||||
case LockPatternUtils.CREDENTIAL_TYPE_PIN:
|
||||
return isManagedProfile
|
||||
? getString(R.string.lockpassword_confirm_your_work_pin_header)
|
||||
: getString(R.string.lockpassword_confirm_your_pin_header);
|
||||
case LockPatternUtils.CREDENTIAL_TYPE_PATTERN:
|
||||
return isManagedProfile
|
||||
? getString(R.string.lockpassword_confirm_your_work_pattern_header)
|
||||
: getString(R.string.lockpassword_confirm_your_pattern_header);
|
||||
case LockPatternUtils.CREDENTIAL_TYPE_PASSWORD:
|
||||
return isManagedProfile
|
||||
? getString(R.string.lockpassword_confirm_your_work_password_header)
|
||||
: getString(R.string.lockpassword_confirm_your_password_header);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getDetailsFromCredentialType(@LockPatternUtils.CredentialType int credentialType,
|
||||
boolean isManagedProfile) {
|
||||
switch (credentialType) {
|
||||
case LockPatternUtils.CREDENTIAL_TYPE_PIN:
|
||||
return isManagedProfile
|
||||
? getString(R.string.lockpassword_confirm_your_pin_generic_profile)
|
||||
: getString(R.string.lockpassword_confirm_your_pin_generic);
|
||||
case LockPatternUtils.CREDENTIAL_TYPE_PATTERN:
|
||||
return isManagedProfile
|
||||
? getString(R.string.lockpassword_confirm_your_pattern_generic_profile)
|
||||
: getString(R.string.lockpassword_confirm_your_pattern_generic);
|
||||
case LockPatternUtils.CREDENTIAL_TYPE_PASSWORD:
|
||||
return isManagedProfile
|
||||
? getString(R.string.lockpassword_confirm_your_password_generic_profile)
|
||||
: getString(R.string.lockpassword_confirm_your_password_generic);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
@@ -250,20 +328,9 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
super.onPause();
|
||||
if (!isChangingConfigurations()) {
|
||||
mGoingToBackground = true;
|
||||
if (mBiometricFragment != null) {
|
||||
Log.d(TAG, "Authenticating: " + mBiometricFragment.isAuthenticating());
|
||||
if (mBiometricFragment.isAuthenticating()) {
|
||||
mBiometricFragment.cancel();
|
||||
}
|
||||
if (!mWaitingForBiometricCallback) {
|
||||
finish();
|
||||
}
|
||||
|
||||
if (mIsFallback && !mCCLaunched) {
|
||||
mBiometricManager.onConfirmDeviceCredentialError(
|
||||
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
|
||||
getString(com.android.internal.R.string.biometric_error_user_canceled));
|
||||
}
|
||||
|
||||
finish();
|
||||
} else {
|
||||
mGoingToBackground = false;
|
||||
}
|
||||
@@ -278,16 +345,9 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
|| !mUserManager.isUserUnlocked(mUserId);
|
||||
}
|
||||
|
||||
private boolean isBiometricDisabledByAdmin(int effectiveUserId) {
|
||||
final int disabledFeatures =
|
||||
mDevicePolicyManager.getKeyguardDisabledFeatures(null, effectiveUserId);
|
||||
return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS) != 0;
|
||||
}
|
||||
|
||||
private boolean isBiometricAllowed(int effectiveUserId, int realUserId) {
|
||||
return !isStrongAuthRequired(effectiveUserId)
|
||||
&& !isBiometricDisabledByAdmin(effectiveUserId)
|
||||
&& !mLockPatternUtils.hasPendingEscrowToken(realUserId);
|
||||
return !isStrongAuthRequired(effectiveUserId) && !mLockPatternUtils
|
||||
.hasPendingEscrowToken(realUserId);
|
||||
}
|
||||
|
||||
private void showBiometricPrompt(Bundle bundle) {
|
||||
@@ -314,7 +374,6 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
* Shows ConfirmDeviceCredentials for normal apps.
|
||||
*/
|
||||
private void showConfirmCredentials() {
|
||||
mCCLaunched = true;
|
||||
boolean launched = false;
|
||||
// The only difference between CREDENTIAL_MANAGED and CREDENTIAL_NORMAL is that for
|
||||
// CREDENTIAL_MANAGED, we launch the real confirm credential activity with an explicit
|
||||
@@ -332,7 +391,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
.launchConfirmationActivityWithExternalAndChallenge(
|
||||
0 /* request code */, null /* title */, mTitle, mDetails,
|
||||
true /* isExternal */, 0L /* challenge */, mUserId);
|
||||
} else if (mCredentialMode == CREDENTIAL_NORMAL){
|
||||
} else if (mCredentialMode == CREDENTIAL_NORMAL) {
|
||||
launched = mChooseLockSettingsHelper.launchConfirmationActivity(
|
||||
0 /* request code */, null /* title */,
|
||||
mTitle, mDetails, false /* returnCredentials */, true /* isExternal */,
|
||||
@@ -345,14 +404,6 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
// Finish without animation since the activity is just there so we can launch
|
||||
// BiometricPrompt.
|
||||
overridePendingTransition(R.anim.confirm_credential_biometric_transition_enter, 0);
|
||||
}
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return this instanceof ConfirmDeviceCredentialActivity.InternalActivity;
|
||||
}
|
||||
|
||||
@@ -17,15 +17,11 @@
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
@@ -50,16 +46,6 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
|
||||
private boolean mFirstTimeVisible = true;
|
||||
private boolean mIsKeyguardLocked = false;
|
||||
private ConfirmCredentialTheme mConfirmCredentialTheme;
|
||||
private BiometricManager mBiometricManager;
|
||||
|
||||
// TODO(b/123378871): Remove when moved.
|
||||
private final IBiometricConfirmDeviceCredentialCallback mCancelCallback
|
||||
= new IBiometricConfirmDeviceCredentialCallback.Stub() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return (this instanceof ConfirmLockPassword.InternalActivity)
|
||||
@@ -78,7 +64,7 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
|
||||
return;
|
||||
}
|
||||
if (UserManager.get(this).isManagedProfile(credentialOwnerUserId)) {
|
||||
setTheme(R.style.Theme_ConfirmDeviceCredentialsWork);
|
||||
setTheme(SetupWizardUtils.getTheme(getIntent()));
|
||||
mConfirmCredentialTheme = ConfirmCredentialTheme.WORK;
|
||||
} else if (getIntent().getBooleanExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.DARK_THEME, false)) {
|
||||
@@ -90,9 +76,6 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
|
||||
}
|
||||
super.onCreate(savedState);
|
||||
|
||||
mBiometricManager = getSystemService(BiometricManager.class);
|
||||
mBiometricManager.registerCancellationCallback(mCancelCallback);
|
||||
|
||||
if (mConfirmCredentialTheme == ConfirmCredentialTheme.NORMAL) {
|
||||
// Prevent the content parent from consuming the window insets because GlifLayout uses
|
||||
// it to show the status bar background.
|
||||
@@ -167,18 +150,23 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
// TODO(b/123378871): Remove when moved.
|
||||
if (!isChangingConfigurations()) {
|
||||
mBiometricManager.onConfirmDeviceCredentialError(
|
||||
BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
|
||||
getString(com.android.internal.R.string.biometric_error_user_canceled));
|
||||
if (getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOREGROUND_ONLY, false)) {
|
||||
finish();
|
||||
}
|
||||
final boolean foregroundOnly = getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOREGROUND_ONLY, false);
|
||||
if (!isChangingConfigurations() && foregroundOnly) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Force a garbage collection immediately to remove remnant of user password shards
|
||||
// from memory.
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
@@ -188,11 +176,6 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLaunchableInTaskModePinned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void prepareEnterAnimation() {
|
||||
getFragment().prepareEnterAnimation();
|
||||
}
|
||||
|
||||
@@ -34,8 +34,10 @@ import android.graphics.drawable.Drawable;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
@@ -56,7 +58,7 @@ import com.android.settings.core.InstrumentedFragment;
|
||||
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
|
||||
*/
|
||||
public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFragment {
|
||||
|
||||
public static final String TAG = ConfirmDeviceCredentialBaseFragment.class.getSimpleName();
|
||||
public static final String TITLE_TEXT = SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.title";
|
||||
public static final String HEADER_TEXT = SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.header";
|
||||
public static final String DETAILS_TEXT = SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.details";
|
||||
@@ -77,6 +79,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
|
||||
protected boolean mReturnCredentials = false;
|
||||
protected Button mCancelButton;
|
||||
/** Button allowing managed profile password reset, null when is not shown. */
|
||||
@Nullable protected Button mForgotButton;
|
||||
protected int mEffectiveUserId;
|
||||
protected int mUserId;
|
||||
protected UserManager mUserManager;
|
||||
@@ -116,8 +120,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mCancelButton = (Button) view.findViewById(R.id.cancelButton);
|
||||
|
||||
mCancelButton = view.findViewById(R.id.cancelButton);
|
||||
boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
|
||||
SHOW_CANCEL_BUTTON, false);
|
||||
boolean hasAlternateButton = mFrp && !TextUtils.isEmpty(mFrpAlternateButtonText);
|
||||
@@ -126,22 +129,32 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
if (hasAlternateButton) {
|
||||
mCancelButton.setText(mFrpAlternateButtonText);
|
||||
}
|
||||
mCancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (hasAlternateButton) {
|
||||
getActivity().setResult(KeyguardManager.RESULT_ALTERNATE);
|
||||
}
|
||||
getActivity().finish();
|
||||
mCancelButton.setOnClickListener(v -> {
|
||||
if (hasAlternateButton) {
|
||||
getActivity().setResult(KeyguardManager.RESULT_ALTERNATE);
|
||||
}
|
||||
getActivity().finish();
|
||||
});
|
||||
int credentialOwnerUserId = Utils.getCredentialOwnerUserId(
|
||||
getActivity(),
|
||||
Utils.getUserIdFromBundle(
|
||||
getActivity(),
|
||||
getActivity().getIntent().getExtras(), isInternalActivity()));
|
||||
if (mUserManager.isManagedProfile(credentialOwnerUserId)) {
|
||||
setWorkChallengeBackground(view, credentialOwnerUserId);
|
||||
setupForgotButtonIfManagedProfile(view);
|
||||
}
|
||||
|
||||
private void setupForgotButtonIfManagedProfile(View view) {
|
||||
if (mUserManager.isManagedProfile(mUserId)
|
||||
&& mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))
|
||||
&& mDevicePolicyManager.canProfileOwnerResetPasswordWhenLocked(mUserId)) {
|
||||
mForgotButton = view.findViewById(R.id.forgotButton);
|
||||
if (mForgotButton == null) {
|
||||
Log.wtf(TAG, "Forgot button not found in managed profile credential dialog");
|
||||
return;
|
||||
}
|
||||
mForgotButton.setVisibility(View.VISIBLE);
|
||||
mForgotButton.setOnClickListener(v -> {
|
||||
final Intent intent = new Intent();
|
||||
intent.setClassName(SETTINGS_PACKAGE_NAME, ForgotPasswordActivity.class.getName());
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
|
||||
getActivity().startActivity(intent);
|
||||
getActivity().finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.os.RemoteException;
|
||||
@@ -54,8 +55,12 @@ public class ConfirmDeviceCredentialUtils {
|
||||
}
|
||||
|
||||
public static void reportSuccessfulAttempt(LockPatternUtils utils, UserManager userManager,
|
||||
int userId) {
|
||||
utils.reportSuccessfulPasswordAttempt(userId);
|
||||
DevicePolicyManager dpm, int userId, boolean isStrongAuth) {
|
||||
if (isStrongAuth) {
|
||||
utils.reportSuccessfulPasswordAttempt(userId);
|
||||
} else {
|
||||
dpm.reportSuccessfulBiometricAttempt(userId);
|
||||
}
|
||||
if (userManager.isManagedProfile(userId)) {
|
||||
// Keyguard is responsible to disable StrongAuth for primary user. Disable StrongAuth
|
||||
// for work challenge only here.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
@@ -27,6 +28,7 @@ import android.os.CountDownTimer;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.view.KeyEvent;
|
||||
@@ -37,6 +39,7 @@ import android.view.ViewGroup;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.ImeAwareEditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
@@ -44,9 +47,9 @@ import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.internal.widget.LockPatternChecker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.internal.widget.TextViewInputDisabler;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.widget.ImeAwareEditText;
|
||||
import com.android.settingslib.animation.AppearAnimationUtils;
|
||||
import com.android.settingslib.animation.DisappearAnimationUtils;
|
||||
|
||||
@@ -54,7 +57,7 @@ import java.util.ArrayList;
|
||||
|
||||
public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
// The index of the array is isStrongAuth << 2 + isProfile << 1 + isAlpha.
|
||||
// The index of the array is isStrongAuth << 2 + isManagedProfile << 1 + isAlpha.
|
||||
private static final int[] DETAIL_TEXTS = new int[] {
|
||||
R.string.lockpassword_confirm_your_pin_generic,
|
||||
R.string.lockpassword_confirm_your_password_generic,
|
||||
@@ -63,7 +66,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
R.string.lockpassword_strong_auth_required_device_pin,
|
||||
R.string.lockpassword_strong_auth_required_device_password,
|
||||
R.string.lockpassword_strong_auth_required_work_pin,
|
||||
R.string.lockpassword_strong_auth_required_work_password,
|
||||
R.string.lockpassword_strong_auth_required_work_password
|
||||
};
|
||||
|
||||
public static class InternalActivity extends ConfirmLockPassword {
|
||||
@@ -107,6 +110,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
private InputMethodManager mImm;
|
||||
private AppearAnimationUtils mAppearAnimationUtils;
|
||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||
private boolean mIsManagedProfile;
|
||||
|
||||
// required constructor for fragments
|
||||
public ConfirmLockPasswordFragment() {
|
||||
@@ -147,12 +151,17 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
mImm = (InputMethodManager) getActivity().getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
|
||||
|
||||
Intent intent = getActivity().getIntent();
|
||||
if (intent != null) {
|
||||
CharSequence headerMessage = intent.getCharSequenceExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
|
||||
CharSequence detailsMessage = intent.getCharSequenceExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
|
||||
if (TextUtils.isEmpty(headerMessage) && mIsManagedProfile) {
|
||||
headerMessage = mDevicePolicyManager.getOrganizationNameForUser(mUserId);
|
||||
}
|
||||
if (TextUtils.isEmpty(headerMessage)) {
|
||||
headerMessage = getString(getDefaultHeader());
|
||||
}
|
||||
@@ -163,8 +172,16 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
mDetailsTextView.setText(detailsMessage);
|
||||
}
|
||||
int currentType = mPasswordEntry.getInputType();
|
||||
mPasswordEntry.setInputType(mIsAlpha ? currentType
|
||||
: (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
|
||||
if (mIsAlpha) {
|
||||
mPasswordEntry.setInputType(currentType);
|
||||
mPasswordEntry.setContentDescription(
|
||||
getContext().getString(R.string.unlock_set_unlock_password_title));
|
||||
} else {
|
||||
mPasswordEntry.setInputType(
|
||||
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
||||
mPasswordEntry.setContentDescription(
|
||||
getContext().getString(R.string.unlock_set_unlock_pin_title));
|
||||
}
|
||||
// Can't set via XML since setInputType resets the fontFamily to null
|
||||
mPasswordEntry.setTypeface(Typeface.create(
|
||||
getContext().getString(com.android.internal.R.string.config_headlineFontFamily),
|
||||
@@ -190,11 +207,36 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (mForgotButton != null) {
|
||||
mForgotButton.setText(mIsAlpha
|
||||
? R.string.lockpassword_forgot_password
|
||||
: R.string.lockpassword_forgot_pin);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mPasswordEntry.setText(null);
|
||||
// Force a garbage collection immediately to remove remnant of user password shards
|
||||
// from memory.
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
}
|
||||
|
||||
private int getDefaultHeader() {
|
||||
if (mFrp) {
|
||||
return mIsAlpha ? R.string.lockpassword_confirm_your_password_header_frp
|
||||
: R.string.lockpassword_confirm_your_pin_header_frp;
|
||||
}
|
||||
if (mIsManagedProfile) {
|
||||
return mIsAlpha ? R.string.lockpassword_confirm_your_work_password_header
|
||||
: R.string.lockpassword_confirm_your_work_pin_header;
|
||||
}
|
||||
return mIsAlpha ? R.string.lockpassword_confirm_your_password_header
|
||||
: R.string.lockpassword_confirm_your_pin_header;
|
||||
}
|
||||
@@ -205,9 +247,8 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
: R.string.lockpassword_confirm_your_pin_details_frp;
|
||||
}
|
||||
boolean isStrongAuthRequired = isStrongAuthRequired();
|
||||
boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
|
||||
// Map boolean flags to an index by isStrongAuth << 2 + isProfile << 1 + isAlpha.
|
||||
int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((isProfile ? 1 : 0) << 1)
|
||||
// Map boolean flags to an index by isStrongAuth << 2 + isManagedProfile << 1 + isAlpha.
|
||||
int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((mIsManagedProfile ? 1 : 0) << 1)
|
||||
+ (mIsAlpha ? 1 : 0);
|
||||
return DETAIL_TEXTS[index];
|
||||
}
|
||||
@@ -240,6 +281,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
mHeaderTextView.setAlpha(0f);
|
||||
mDetailsTextView.setAlpha(0f);
|
||||
mCancelButton.setAlpha(0f);
|
||||
if (mForgotButton != null) {
|
||||
mForgotButton.setAlpha(0f);
|
||||
}
|
||||
mPasswordEntry.setAlpha(0f);
|
||||
mErrorTextView.setAlpha(0f);
|
||||
}
|
||||
@@ -251,6 +295,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
if (mCancelButton.getVisibility() == View.VISIBLE) {
|
||||
result.add(mCancelButton);
|
||||
}
|
||||
if (mForgotButton != null) {
|
||||
result.add(mForgotButton);
|
||||
}
|
||||
result.add(mPasswordEntry);
|
||||
result.add(mErrorTextView);
|
||||
return result.toArray(new View[] {});
|
||||
@@ -324,10 +371,13 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
|
||||
// TODO(b/120484642): This is a point of entry for passwords from the UI
|
||||
final byte[] pin = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText());
|
||||
if (pin == null || pin.length == 0) {
|
||||
final Editable passwordText = mPasswordEntry.getText();
|
||||
if (TextUtils.isEmpty(passwordText)) {
|
||||
return;
|
||||
}
|
||||
final LockscreenCredential credential =
|
||||
mIsAlpha ? LockscreenCredential.createPassword(passwordText)
|
||||
: LockscreenCredential.createPin(passwordText);
|
||||
|
||||
mPasswordEntryInputDisabler.setInputEnabled(false);
|
||||
final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
|
||||
@@ -336,11 +386,11 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
Intent intent = new Intent();
|
||||
if (verifyChallenge) {
|
||||
if (isInternalActivity()) {
|
||||
startVerifyPassword(pin, intent);
|
||||
startVerifyPassword(credential, intent);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
startCheckPassword(pin, intent);
|
||||
startCheckPassword(credential, intent);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -351,7 +401,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
return getActivity() instanceof ConfirmLockPassword.InternalActivity;
|
||||
}
|
||||
|
||||
private void startVerifyPassword(final byte[] pin, final Intent intent) {
|
||||
private void startVerifyPassword(LockscreenCredential credential, final Intent intent) {
|
||||
long challenge = getActivity().getIntent().getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
|
||||
final int localEffectiveUserId = mEffectiveUserId;
|
||||
@@ -375,29 +425,32 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
};
|
||||
mPendingLockCheck = (localEffectiveUserId == localUserId)
|
||||
? LockPatternChecker.verifyPassword(
|
||||
mLockPatternUtils, pin, challenge, localUserId, onVerifyCallback)
|
||||
? LockPatternChecker.verifyCredential(
|
||||
mLockPatternUtils, credential, challenge, localUserId, onVerifyCallback)
|
||||
: LockPatternChecker.verifyTiedProfileChallenge(
|
||||
mLockPatternUtils, pin, false, challenge, localUserId,
|
||||
mLockPatternUtils, credential, challenge, localUserId,
|
||||
onVerifyCallback);
|
||||
}
|
||||
|
||||
private void startCheckPassword(final byte[] pin, final Intent intent) {
|
||||
private void startCheckPassword(final LockscreenCredential credential,
|
||||
final Intent intent) {
|
||||
final int localEffectiveUserId = mEffectiveUserId;
|
||||
mPendingLockCheck = LockPatternChecker.checkPassword(
|
||||
mPendingLockCheck = LockPatternChecker.checkCredential(
|
||||
mLockPatternUtils,
|
||||
pin,
|
||||
credential,
|
||||
localEffectiveUserId,
|
||||
new LockPatternChecker.OnCheckCallback() {
|
||||
@Override
|
||||
public void onChecked(boolean matched, int timeoutMs) {
|
||||
mPendingLockCheck = null;
|
||||
if (matched && isInternalActivity() && mReturnCredentials) {
|
||||
// TODO: get rid of EXTRA_KEY_TYPE, since EXTRA_KEY_PASSWORD already
|
||||
// distinguishes beteween PIN and password.
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
|
||||
mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
|
||||
: StorageManager.CRYPT_TYPE_PIN);
|
||||
intent.putExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, credential);
|
||||
}
|
||||
mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
|
||||
localEffectiveUserId);
|
||||
@@ -436,9 +489,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
if (matched) {
|
||||
if (newResult) {
|
||||
ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
|
||||
mUserManager, mEffectiveUserId);
|
||||
mUserManager, mDevicePolicyManager, mEffectiveUserId,
|
||||
/* isStrongAuth */ true);
|
||||
}
|
||||
mBiometricManager.onConfirmDeviceCredentialSuccess();
|
||||
startDisappearAnimation(intent);
|
||||
ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
|
||||
} else {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Intent;
|
||||
@@ -25,6 +26,7 @@ import android.os.CountDownTimer;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -37,6 +39,7 @@ import com.android.internal.widget.LockPatternChecker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternView;
|
||||
import com.android.internal.widget.LockPatternView.Cell;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.animation.AppearAnimationCreator;
|
||||
import com.android.settingslib.animation.AppearAnimationUtils;
|
||||
@@ -89,8 +92,6 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
private TextView mHeaderTextView;
|
||||
private TextView mDetailsTextView;
|
||||
private View mLeftSpacerLandscape;
|
||||
private View mRightSpacerLandscape;
|
||||
|
||||
// caller-supplied text for various prompts
|
||||
private CharSequence mHeaderText;
|
||||
@@ -99,6 +100,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
private AppearAnimationUtils mAppearAnimationUtils;
|
||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||
|
||||
private boolean mIsManagedProfile;
|
||||
|
||||
// required constructor for fragments
|
||||
public ConfirmLockPatternFragment() {
|
||||
|
||||
@@ -118,8 +121,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
|
||||
mDetailsTextView = (TextView) view.findViewById(R.id.sud_layout_description);
|
||||
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
|
||||
mLeftSpacerLandscape = view.findViewById(R.id.leftSpacer);
|
||||
mRightSpacerLandscape = view.findViewById(R.id.rightSpacer);
|
||||
|
||||
mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
|
||||
|
||||
// make it so unhandled touch events within the unlock screen go to the
|
||||
// lock pattern view.
|
||||
@@ -134,6 +137,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
mDetailsText = intent.getCharSequenceExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
|
||||
}
|
||||
if (TextUtils.isEmpty(mHeaderText) && mIsManagedProfile) {
|
||||
mHeaderText = mDevicePolicyManager.getOrganizationNameForUser(mUserId);
|
||||
}
|
||||
|
||||
mLockPatternView.setTactileFeedbackEnabled(
|
||||
mLockPatternUtils.isTactileFeedbackEnabled());
|
||||
@@ -180,9 +186,18 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
|
||||
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (mForgotButton != null) {
|
||||
mForgotButton.setText(R.string.lockpassword_forgot_pattern);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
// deliberately not calling super since we are managing this in full
|
||||
@@ -229,6 +244,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
super.prepareEnterAnimation();
|
||||
mHeaderTextView.setAlpha(0f);
|
||||
mCancelButton.setAlpha(0f);
|
||||
if (mForgotButton != null) {
|
||||
mForgotButton.setAlpha(0f);
|
||||
}
|
||||
mLockPatternView.setAlpha(0f);
|
||||
mDetailsTextView.setAlpha(0f);
|
||||
}
|
||||
@@ -238,7 +256,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
return R.string.lockpassword_confirm_your_pattern_details_frp;
|
||||
}
|
||||
final boolean isStrongAuthRequired = isStrongAuthRequired();
|
||||
if (UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId)) {
|
||||
if (mIsManagedProfile) {
|
||||
return isStrongAuthRequired
|
||||
? R.string.lockpassword_strong_auth_required_work_pattern
|
||||
: R.string.lockpassword_confirm_your_pattern_generic_profile;
|
||||
@@ -251,10 +269,13 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
private Object[][] getActiveViews() {
|
||||
ArrayList<ArrayList<Object>> result = new ArrayList<>();
|
||||
result.add(new ArrayList<Object>(Collections.singletonList(mHeaderTextView)));
|
||||
result.add(new ArrayList<Object>(Collections.singletonList(mDetailsTextView)));
|
||||
result.add(new ArrayList<>(Collections.singletonList(mHeaderTextView)));
|
||||
result.add(new ArrayList<>(Collections.singletonList(mDetailsTextView)));
|
||||
if (mCancelButton.getVisibility() == View.VISIBLE) {
|
||||
result.add(new ArrayList<Object>(Collections.singletonList(mCancelButton)));
|
||||
result.add(new ArrayList<>(Collections.singletonList(mCancelButton)));
|
||||
}
|
||||
if (mForgotButton != null) {
|
||||
result.add(new ArrayList<>(Collections.singletonList(mForgotButton)));
|
||||
}
|
||||
LockPatternView.CellState[][] cellStates = mLockPatternView.getCellStates();
|
||||
for (int i = 0; i < cellStates.length; i++) {
|
||||
@@ -324,7 +345,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
|
||||
private int getDefaultHeader() {
|
||||
return mFrp ? R.string.lockpassword_confirm_your_pattern_header_frp
|
||||
if (mFrp) return R.string.lockpassword_confirm_your_pattern_header_frp;
|
||||
return mIsManagedProfile
|
||||
? R.string.lockpassword_confirm_your_work_pattern_header
|
||||
: R.string.lockpassword_confirm_your_pattern_header;
|
||||
}
|
||||
|
||||
@@ -401,14 +424,16 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
final LockscreenCredential credential = LockscreenCredential.createPattern(pattern);
|
||||
//TODO: how to sanitize this?
|
||||
Intent intent = new Intent();
|
||||
if (verifyChallenge) {
|
||||
if (isInternalActivity()) {
|
||||
startVerifyPattern(pattern, intent);
|
||||
startVerifyPattern(credential, intent);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
startCheckPattern(pattern, intent);
|
||||
startCheckPattern(credential, intent);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -419,7 +444,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
return getActivity() instanceof ConfirmLockPattern.InternalActivity;
|
||||
}
|
||||
|
||||
private void startVerifyPattern(final List<LockPatternView.Cell> pattern,
|
||||
private void startVerifyPattern(final LockscreenCredential pattern,
|
||||
final Intent intent) {
|
||||
final int localEffectiveUserId = mEffectiveUserId;
|
||||
final int localUserId = mUserId;
|
||||
@@ -444,15 +469,15 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
};
|
||||
mPendingLockCheck = (localEffectiveUserId == localUserId)
|
||||
? LockPatternChecker.verifyPattern(
|
||||
? LockPatternChecker.verifyCredential(
|
||||
mLockPatternUtils, pattern, challenge, localUserId,
|
||||
onVerifyCallback)
|
||||
: LockPatternChecker.verifyTiedProfileChallenge(
|
||||
mLockPatternUtils, LockPatternUtils.patternToByteArray(pattern),
|
||||
true, challenge, localUserId, onVerifyCallback);
|
||||
mLockPatternUtils, pattern,
|
||||
challenge, localUserId, onVerifyCallback);
|
||||
}
|
||||
|
||||
private void startCheckPattern(final List<LockPatternView.Cell> pattern,
|
||||
private void startCheckPattern(final LockscreenCredential pattern,
|
||||
final Intent intent) {
|
||||
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
|
||||
// Pattern size is less than the minimum, do not count it as an fail attempt.
|
||||
@@ -461,7 +486,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
|
||||
final int localEffectiveUserId = mEffectiveUserId;
|
||||
mPendingLockCheck = LockPatternChecker.checkPattern(
|
||||
mPendingLockCheck = LockPatternChecker.checkCredential(
|
||||
mLockPatternUtils,
|
||||
pattern,
|
||||
localEffectiveUserId,
|
||||
@@ -473,7 +498,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
|
||||
StorageManager.CRYPT_TYPE_PATTERN);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
|
||||
LockPatternUtils.patternToByteArray(pattern));
|
||||
pattern);
|
||||
}
|
||||
mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
|
||||
localEffectiveUserId);
|
||||
@@ -488,9 +513,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
if (matched) {
|
||||
if (newResult) {
|
||||
ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
|
||||
mUserManager, mEffectiveUserId);
|
||||
mUserManager, mDevicePolicyManager, mEffectiveUserId,
|
||||
/* isStrongAuth */ true);
|
||||
}
|
||||
mBiometricManager.onConfirmDeviceCredentialSuccess();
|
||||
startDisappearAnimation(intent);
|
||||
ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
/**
|
||||
* An activity that asks the user to contact their admin to get assistance with forgotten password.
|
||||
*/
|
||||
public class ForgotPasswordActivity extends Activity {
|
||||
public static final String TAG = ForgotPasswordActivity.class.getSimpleName();
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
int userId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, -1);
|
||||
if (userId < 0) {
|
||||
Log.e(TAG, "No valid userId supplied, exiting");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
setContentView(R.layout.forgot_password_activity);
|
||||
|
||||
final GlifLayout layout = findViewById(R.id.setup_wizard_layout);
|
||||
layout.getMixin(FooterBarMixin.class).setPrimaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(android.R.string.ok)
|
||||
.setListener(v -> finish())
|
||||
.setButtonType(FooterButton.ButtonType.DONE)
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
.build()
|
||||
);
|
||||
|
||||
UserManager.get(this).requestQuietModeEnabled(
|
||||
false, UserHandle.of(userId), UserManager.QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ package com.android.settings.password;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
|
||||
/**
|
||||
* Helper for handling managed passwords in security settings UI.
|
||||
* It provides resources that should be shown in settings UI when lock password quality is set to
|
||||
@@ -59,7 +61,7 @@ public class ManagedLockPasswordProvider {
|
||||
* @param password Current lock password.
|
||||
* @return Intent that should update lock password to a managed password.
|
||||
*/
|
||||
Intent createIntent(boolean requirePasswordToDecrypt, byte[] password) {
|
||||
Intent createIntent(boolean requirePasswordToDecrypt, LockscreenCredential password) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Pair;
|
||||
import android.widget.Toast;
|
||||
@@ -27,6 +28,7 @@ import android.widget.Toast;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
@@ -44,6 +46,8 @@ abstract class SaveChosenLockWorkerBase extends Fragment {
|
||||
protected long mChallenge;
|
||||
protected boolean mWasSecureBefore;
|
||||
protected int mUserId;
|
||||
protected int mUnificationProfileId = UserHandle.USER_NULL;
|
||||
protected LockscreenCredential mUnificationProfileCredential;
|
||||
|
||||
private boolean mBlocking;
|
||||
|
||||
@@ -106,12 +110,27 @@ abstract class SaveChosenLockWorkerBase extends Fragment {
|
||||
if (mListener != null) {
|
||||
mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
|
||||
}
|
||||
if (mUnificationProfileCredential != null) {
|
||||
mUnificationProfileCredential.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlocking(boolean blocking) {
|
||||
mBlocking = blocking;
|
||||
}
|
||||
|
||||
public void setProfileToUnify(int profileId, LockscreenCredential credential) {
|
||||
mUnificationProfileId = profileId;
|
||||
mUnificationProfileCredential = credential.duplicate();
|
||||
}
|
||||
|
||||
protected void unifyProfileCredentialIfRequested() {
|
||||
if (mUnificationProfileId != UserHandle.USER_NULL) {
|
||||
mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false,
|
||||
mUnificationProfileCredential);
|
||||
}
|
||||
}
|
||||
|
||||
private class Task extends AsyncTask<Void, Void, Pair<Boolean, Intent>> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,16 +32,16 @@ import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -110,7 +110,7 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
|
||||
|
||||
@Override
|
||||
public void launchChooseLock(Bundle chooseLockFingerprintExtras) {
|
||||
final boolean isInSetupWizard = !Utils.isDeviceProvisioned(this);
|
||||
final boolean isInSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
|
||||
Intent intent = isInSetupWizard ? new Intent(this, SetupChooseLockGeneric.class)
|
||||
: new Intent(this, ChooseLockGeneric.class);
|
||||
intent.setAction(mNewPasswordAction);
|
||||
|
||||
Reference in New Issue
Block a user