Merge Android U (ab/10368041)

Bug: 291102124
Merged-In: I17a6c8a571b4a0b7d943dfd710cde0f18d03da39
Change-Id: I4ed5b2e4c6c59527bb544e8b6dff2b9d4cee9025
This commit is contained in:
Xin Li
2023-08-25 13:50:56 -07:00
2542 changed files with 197328 additions and 207148 deletions

View File

@@ -129,6 +129,7 @@ public class BiometricFragment extends InstrumentedFragment {
mBiometricPrompt = new BiometricPrompt.Builder(getContext())
.setTitle(promptInfo.getTitle())
.setUseDefaultTitle() // use default title if title is null/empty
.setUseDefaultSubtitle() // use default subtitle if subtitle is null/empty
.setDeviceCredentialAllowed(true)
.setSubtitle(promptInfo.getSubtitle())
.setDescription(promptInfo.getDescription())
@@ -140,6 +141,7 @@ public class BiometricFragment extends InstrumentedFragment {
.setDisallowBiometricsIfPolicyExists(
promptInfo.isDisallowBiometricsIfPolicyExists())
.setReceiveSystemEvents(true)
.setAllowBackgroundAuthentication(true)
.build();
}

View File

@@ -54,9 +54,9 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
@@ -67,7 +67,6 @@ 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;
import com.android.settings.SettingsActivity;
@@ -76,6 +75,7 @@ import com.android.settings.SetupWizardUtils;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollActivity;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.safetycenter.LockScreenSafetySource;
@@ -144,8 +144,6 @@ public class ChooseLockGeneric extends SettingsActivity {
@VisibleForTesting
static final int CONFIRM_EXISTING_REQUEST = 100;
@VisibleForTesting
static final int ENABLE_ENCRYPTION_REQUEST = 101;
@VisibleForTesting
static final int CHOOSE_LOCK_REQUEST = 102;
@VisibleForTesting
static final int CHOOSE_LOCK_BEFORE_BIOMETRIC_REQUEST = 103;
@@ -157,7 +155,6 @@ public class ChooseLockGeneric extends SettingsActivity {
private boolean mRequestGatekeeperPasswordHandle = false;
private boolean mPasswordConfirmed = false;
private boolean mWaitingForConfirmation = false;
private boolean mForChangeCredRequiredForBoot = false;
private LockscreenCredential mUserPassword;
private FingerprintManager mFingerprintManager;
private FaceManager mFaceManager;
@@ -251,8 +248,6 @@ public class ChooseLockGeneric extends SettingsActivity {
mIsCallingAppAdmin = intent
.getBooleanExtra(EXTRA_KEY_IS_CALLING_APP_ADMIN, /* defValue= */ false);
mForChangeCredRequiredForBoot = arguments != null && arguments.getBoolean(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
mUserManager = UserManager.get(activity);
if (arguments != null) {
@@ -300,10 +295,6 @@ public class ChooseLockGeneric extends SettingsActivity {
if (mPasswordConfirmed) {
updatePreferencesOrFinish(savedInstanceState != null);
if (mForChangeCredRequiredForBoot) {
maybeEnableEncryption(mLockPatternUtils.getKeyguardStoredPasswordQuality(
mUserId), false);
}
} else if (!mWaitingForConfirmation) {
final ChooseLockSettingsHelper.Builder builder =
new ChooseLockSettingsHelper.Builder(activity, this);
@@ -451,58 +442,6 @@ public class ChooseLockGeneric extends SettingsActivity {
}
}
/**
* If the device has encryption already enabled, then ask the user if they
* also want to encrypt the phone with this password.
*
* @param quality
* @param disabled
*/
// TODO: why does this take disabled, its always called with a quality higher than
// what makes sense with disabled == true
private void maybeEnableEncryption(int quality, boolean disabled) {
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
if (UserManager.get(getActivity()).isAdminUser()
&& mUserId == UserHandle.myUserId()
&& LockPatternUtils.isDeviceEncryptionEnabled()
&& !LockPatternUtils.isFileEncryptionEnabled()
&& !dpm.getDoNotAskCredentialsOnBoot()) {
// Get the intent that the encryption interstitial should start for creating
// the new unlock method.
Intent unlockMethodIntent = getIntentForUnlockMethod(quality);
unlockMethodIntent.putExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT,
mForChangeCredRequiredForBoot);
final Context context = getActivity();
// If accessibility is enabled and the user hasn't seen this dialog before, set the
// default state to agree with that which is compatible with accessibility
// (password not required).
final boolean accEn = AccessibilityManager.getInstance(context).isEnabled();
final boolean required = mLockPatternUtils.isCredentialRequiredToDecrypt(!accEn);
Intent intent = getEncryptionInterstitialIntent(context, quality, required,
unlockMethodIntent);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
mForFingerprint);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, mForFace);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, mForBiometrics);
// If the caller requested Gatekeeper Password to be returned, we assume it came
// from biometric enrollment. This should be cleaned up, since requesting
// Gatekeeper Password should not imply it came from biometric setup/settings.
startActivityForResult(
intent,
mIsSetNewPassword && mRequestGatekeeperPasswordHandle
? CHOOSE_LOCK_BEFORE_BIOMETRIC_REQUEST
: ENABLE_ENCRYPTION_REQUEST);
} else {
if (mForChangeCredRequiredForBoot) {
// Welp, couldn't change it. Oh well.
finish();
return;
}
updateUnlockMethodAndFinish(quality, disabled, false /* chooseLockSkipped */);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
@@ -513,17 +452,8 @@ public class ChooseLockGeneric extends SettingsActivity {
? data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)
: null;
updatePreferencesOrFinish(false /* isRecreatingActivity */);
if (mForChangeCredRequiredForBoot) {
if (mUserPassword != null && !mUserPassword.isNone()) {
maybeEnableEncryption(
mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId), false);
} else {
finish();
}
}
} else if (requestCode == CHOOSE_LOCK_REQUEST
|| requestCode == ENABLE_ENCRYPTION_REQUEST) {
if (resultCode != RESULT_CANCELED || mForChangeCredRequiredForBoot) {
} else if (requestCode == CHOOSE_LOCK_REQUEST) {
if (resultCode != RESULT_CANCELED) {
getActivity().setResult(resultCode, data);
finish();
} else {
@@ -563,9 +493,6 @@ public class ChooseLockGeneric extends SettingsActivity {
getActivity().setResult(Activity.RESULT_CANCELED);
finish();
}
if (requestCode == Activity.RESULT_CANCELED && mForChangeCredRequiredForBoot) {
finish();
}
}
protected Intent getBiometricEnrollIntent(Context context) {
@@ -591,11 +518,11 @@ public class ChooseLockGeneric extends SettingsActivity {
void updatePreferencesOrFinish(boolean isRecreatingActivity) {
Intent intent = getActivity().getIntent();
int quality = -1;
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
if (StorageManager.isFileEncrypted()) {
quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
} else {
// For non-file encrypted devices we need to show encryption interstitial, so always
// show the lock type picker and ignore PASSWORD_TYPE_KEY.
// For unencrypted devices, always show the lock type picker and ignore
// PASSWORD_TYPE_KEY.
Log.i(TAG, "Ignoring PASSWORD_TYPE_KEY because device is not file encrypted");
}
if (quality == -1) {
@@ -703,10 +630,11 @@ public class ChooseLockGeneric extends SettingsActivity {
R.string.face_unlock_set_unlock_password);
} else if (mForBiometrics) {
setPreferenceTitle(ScreenLockType.PATTERN,
R.string.biometrics_unlock_set_unlock_pattern);
setPreferenceTitle(ScreenLockType.PIN, R.string.biometrics_unlock_set_unlock_pin);
getBiometricsPreferenceTitle(ScreenLockType.PATTERN));
setPreferenceTitle(ScreenLockType.PIN,
getBiometricsPreferenceTitle(ScreenLockType.PIN));
setPreferenceTitle(ScreenLockType.PASSWORD,
R.string.biometrics_unlock_set_unlock_password);
getBiometricsPreferenceTitle(ScreenLockType.PASSWORD));
}
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
@@ -727,6 +655,24 @@ public class ChooseLockGeneric extends SettingsActivity {
}
}
@VisibleForTesting
String getBiometricsPreferenceTitle(@NonNull ScreenLockType secureType) {
final boolean hasFingerprint = Utils.hasFingerprintHardware(getContext());
final boolean hasFace = Utils.hasFaceHardware(getContext());
final boolean isSuw = WizardManagerHelper.isAnySetupWizard(getIntent());
final boolean isFaceSupported =
hasFace && (!isSuw || BiometricUtils.isFaceSupportedInSuw(getContext()));
// Assume the flow is "Screen Lock" + "Face" + "Fingerprint"
if (mController != null) {
return BiometricUtils.getCombinedScreenLockOptions(getContext(),
mController.getTitle(secureType), hasFingerprint, isFaceSupported);
} else {
Log.e(TAG, "ChooseLockGenericController is null!");
return getResources().getString(R.string.error_title);
}
}
private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) {
Preference preference = findPreference(lock.preferenceKey);
if (preference != null) {
@@ -825,12 +771,6 @@ public class ChooseLockGeneric extends SettingsActivity {
return builder.build();
}
protected Intent getEncryptionInterstitialIntent(Context context, int quality,
boolean required, Intent unlockMethodIntent) {
return EncryptionInterstitial.createStartIntent(context, quality, required,
unlockMethodIntent);
}
/**
* 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
@@ -896,6 +836,19 @@ public class ChooseLockGeneric extends SettingsActivity {
return intent;
}
@Override
public void onStop() {
super.onStop();
// hasCredential checks to see if user chooses a password for screen lock. If the
// screen lock is None or Swipe, we do not want to call getActivity().finish().
// Otherwise, bugs would be caused. (e.g. b/278488549, b/278530059)
final boolean hasCredential = mLockPatternUtils.isSecure(mUserId);
if (!getActivity().isChangingConfigurations()
&& !mWaitingForConfirmation && hasCredential) {
getActivity().finish();
}
}
@Override
public void onDestroy() {
super.onDestroy();
@@ -996,16 +949,14 @@ public class ChooseLockGeneric extends SettingsActivity {
switch (lock) {
case NONE:
case SWIPE:
updateUnlockMethodAndFinish(
lock.defaultQuality,
lock == ScreenLockType.NONE,
false /* chooseLockSkipped */);
return true;
case PATTERN:
case PIN:
case PASSWORD:
case MANAGED:
maybeEnableEncryption(lock.defaultQuality, false);
updateUnlockMethodAndFinish(
lock.defaultQuality,
lock == ScreenLockType.NONE,
false /* chooseLockSkipped */);
return true;
}
}

View File

@@ -25,6 +25,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.REENTER_W
import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PASSWORD_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PIN_HEADER;
import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
@@ -71,6 +72,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.CheckBox;
import android.widget.ImeAwareEditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -86,7 +88,6 @@ import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.PasswordValidationError;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.EncryptionInterstitial;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SetupWizardUtils;
@@ -102,7 +103,9 @@ import com.google.android.setupdesign.util.ThemeHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ChooseLockPassword extends SettingsActivity {
private static final String TAG = "ChooseLockPassword";
@@ -124,7 +127,6 @@ public class ChooseLockPassword extends SettingsActivity {
public IntentBuilder(Context context) {
mIntent = new Intent(context, ChooseLockPassword.class);
mIntent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false);
mIntent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, false);
}
/**
@@ -224,6 +226,10 @@ public class ChooseLockPassword extends SettingsActivity {
private static final String KEY_UI_STAGE = "ui_stage";
private static final String KEY_CURRENT_CREDENTIAL = "current_credential";
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
private static final String KEY_IS_AUTO_CONFIRM_CHECK_MANUALLY_CHANGED =
"auto_confirm_option_set_manually";
private static final int MIN_AUTO_PIN_REQUIREMENT_LENGTH = 6;
private LockscreenCredential mCurrentCredential;
private LockscreenCredential mChosenPassword;
@@ -257,6 +263,9 @@ public class ChooseLockPassword extends SettingsActivity {
protected FooterButton mSkipOrClearButton;
private FooterButton mNextButton;
private TextView mMessage;
protected CheckBox mAutoPinConfirmOption;
protected TextView mAutoConfirmSecurityMessage;
protected boolean mIsAutoPinConfirmOptionSetManually;
private TextChangedHandler mTextChangedHandler;
@@ -461,21 +470,6 @@ public class ChooseLockPassword extends SettingsActivity {
mMinMetrics = intent.getParcelableExtra(EXTRA_KEY_MIN_METRICS);
if (mMinMetrics == null) mMinMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE);
if (intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, false)) {
SaveAndFinishWorker w = new SaveAndFinishWorker();
final boolean required = getActivity().getIntent().getBooleanExtra(
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
LockscreenCredential currentCredential = intent.getParcelableExtra(
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
final LockPatternUtils utils = new LockPatternUtils(getActivity());
w.setBlocking(true);
w.setListener(this);
w.start(utils, required, false /* requestGatekeeperPassword */, currentCredential,
currentCredential, mUserId);
}
mTextChangedHandler = new TextChangedHandler();
}
@@ -532,6 +526,17 @@ public class ChooseLockPassword extends SettingsActivity {
mPasswordEntry.requestFocus();
mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
// Fetch the AutoPinConfirmOption
mAutoPinConfirmOption = view.findViewById(R.id.auto_pin_confirm_enabler);
mAutoConfirmSecurityMessage = view.findViewById(R.id.auto_pin_confirm_security_message);
mIsAutoPinConfirmOptionSetManually = false;
setOnAutoConfirmOptionClickListener();
if (mAutoPinConfirmOption != null) {
mAutoPinConfirmOption.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
mAutoPinConfirmOption.setVisibility(View.GONE);
mAutoPinConfirmOption.setChecked(false);
}
final Activity activity = getActivity();
int currentType = mPasswordEntry.getInputType();
@@ -577,6 +582,8 @@ public class ChooseLockPassword extends SettingsActivity {
mUiStage = Stage.valueOf(state);
updateStage(mUiStage);
}
mIsAutoPinConfirmOptionSetManually =
savedInstanceState.getBoolean(KEY_IS_AUTO_CONFIRM_CHECK_MANUALLY_CHANGED);
mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_CREDENTIAL);
@@ -659,6 +666,8 @@ public class ChooseLockPassword extends SettingsActivity {
if (mCurrentCredential != null) {
outState.putParcelable(KEY_CURRENT_CREDENTIAL, mCurrentCredential.duplicate());
}
outState.putBoolean(KEY_IS_AUTO_CONFIRM_CHECK_MANUALLY_CHANGED,
mIsAutoPinConfirmOptionSetManually);
}
@Override
@@ -797,46 +806,50 @@ public class ChooseLockPassword extends SettingsActivity {
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));
messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
R.string.lockpassword_password_requires_uppercase));
break;
case NOT_ENOUGH_LOWER_CASE:
messages.add(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_lowercase,
error.requirement, error.requirement));
messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
R.string.lockpassword_password_requires_lowercase));
break;
case NOT_ENOUGH_LETTERS:
messages.add(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_letters,
error.requirement, error.requirement));
messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
R.string.lockpassword_password_requires_letters));
break;
case NOT_ENOUGH_DIGITS:
messages.add(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_numeric,
error.requirement, error.requirement));
messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
R.string.lockpassword_password_requires_numeric));
break;
case NOT_ENOUGH_SYMBOLS:
messages.add(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_symbols,
error.requirement, error.requirement));
messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
R.string.lockpassword_password_requires_symbols));
break;
case NOT_ENOUGH_NON_LETTER:
messages.add(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_nonletter,
error.requirement, error.requirement));
messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
R.string.lockpassword_password_requires_nonletter));
break;
case NOT_ENOUGH_NON_DIGITS:
messages.add(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_nonnumerical,
error.requirement, error.requirement));
messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
R.string.lockpassword_password_requires_nonnumerical));
break;
case TOO_SHORT:
messages.add(getResources().getQuantityString(
String message = StringUtil.getIcuPluralsString(getContext(),
error.requirement,
mIsAlphaMode
? R.plurals.lockpassword_password_too_short
: R.plurals.lockpassword_pin_too_short,
error.requirement, error.requirement));
? R.string.lockpassword_password_too_short
: R.string.lockpassword_pin_too_short);
if (LockPatternUtils.isAutoPinConfirmFeatureAvailable()
&& !mIsAlphaMode
&& error.requirement < MIN_AUTO_PIN_REQUIREMENT_LENGTH) {
Map<String, Object> arguments = new HashMap<>();
arguments.put("count", error.requirement);
arguments.put("minAutoConfirmLen", MIN_AUTO_PIN_REQUIREMENT_LENGTH);
message = StringUtil.getIcuPluralsString(getContext(),
arguments,
R.string.lockpassword_pin_too_short_autoConfirm_extra_message);
}
messages.add(message);
break;
case TOO_SHORT_WHEN_ALL_NUMERIC:
messages.add(
@@ -844,11 +857,10 @@ public class ChooseLockPassword extends SettingsActivity {
R.string.lockpassword_password_too_short_all_numeric));
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));
messages.add(StringUtil.getIcuPluralsString(getContext(),
error.requirement + 1, mIsAlphaMode
? R.string.lockpassword_password_too_long
: R.string.lockpassword_pin_too_long));
break;
case CONTAINS_SEQUENCE:
messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
@@ -890,6 +902,8 @@ public class ChooseLockPassword extends SettingsActivity {
String[] messages = convertErrorCodeToMessages();
// Update the fulfillment of requirements.
mPasswordRequirementAdapter.setRequirements(messages);
// set the visibility of pin_auto_confirm option accordingly
setAutoPinConfirmOption(passwordCompliant, length);
// Enable/Disable the next button accordingly.
setNextEnabled(passwordCompliant);
} else {
@@ -899,6 +913,10 @@ public class ChooseLockPassword extends SettingsActivity {
mIsManagedProfile));
setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
// Hide the pin_confirm option when we are just asking user to confirm the pwd.
mAutoPinConfirmOption.setVisibility(View.GONE);
mAutoConfirmSecurityMessage.setVisibility(View.GONE);
}
final int stage = getStageType();
if (getStageType() != Stage.TYPE_NONE) {
@@ -922,6 +940,36 @@ public class ChooseLockPassword extends SettingsActivity {
return visibleOrGone ? View.VISIBLE : View.GONE;
}
private void setAutoPinConfirmOption(boolean enabled, int length) {
if (!LockPatternUtils.isAutoPinConfirmFeatureAvailable()
|| mAutoPinConfirmOption == null) {
return;
}
if (enabled && !mIsAlphaMode && isAutoPinConfirmPossible(length)) {
mAutoPinConfirmOption.setVisibility(View.VISIBLE);
mAutoConfirmSecurityMessage.setVisibility(View.VISIBLE);
if (!mIsAutoPinConfirmOptionSetManually) {
mAutoPinConfirmOption.setChecked(length == MIN_AUTO_PIN_REQUIREMENT_LENGTH);
}
} else {
mAutoPinConfirmOption.setVisibility(View.GONE);
mAutoConfirmSecurityMessage.setVisibility(View.GONE);
mAutoPinConfirmOption.setChecked(false);
}
}
private boolean isAutoPinConfirmPossible(int currentPinLength) {
return currentPinLength >= MIN_AUTO_PIN_REQUIREMENT_LENGTH;
}
private void setOnAutoConfirmOptionClickListener() {
if (mAutoPinConfirmOption != null) {
mAutoPinConfirmOption.setOnClickListener((v) -> {
mIsAutoPinConfirmOptionSetManually = true;
});
}
}
private void setHeaderText(String text) {
// Only set the text if it is different than the existing one to avoid announcing again.
if (!TextUtils.isEmpty(mLayout.getHeaderText())
@@ -954,6 +1002,9 @@ public class ChooseLockPassword extends SettingsActivity {
return;
}
ConfirmDeviceCredentialUtils.hideImeImmediately(
getActivity().getWindow().getDecorView());
mPasswordEntryInputDisabler.setInputEnabled(false);
setNextEnabled(false);
@@ -965,8 +1016,6 @@ public class ChooseLockPassword extends SettingsActivity {
getFragmentManager().executePendingTransactions();
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)) {
@@ -974,7 +1023,13 @@ public class ChooseLockPassword extends SettingsActivity {
profileCredential);
}
}
mSaveAndFinishWorker.start(mLockPatternUtils, required, mRequestGatekeeperPassword,
// update the setting before triggering the password save workflow,
// so that pinLength information is stored accordingly when setting is turned on.
mLockPatternUtils.setAutoPinConfirm(
(mAutoPinConfirmOption != null && mAutoPinConfirmOption.isChecked()),
mUserId);
mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword,
mChosenPassword, mCurrentCredential, mUserId);
}
@@ -1033,10 +1088,10 @@ public class ChooseLockPassword extends SettingsActivity {
private LockscreenCredential mChosenPassword;
private LockscreenCredential mCurrentCredential;
public void start(LockPatternUtils utils, boolean required,
boolean requestGatekeeperPassword, LockscreenCredential chosenPassword,
LockscreenCredential currentCredential, int userId) {
prepare(utils, required, requestGatekeeperPassword, userId);
public void start(LockPatternUtils utils, boolean requestGatekeeperPassword,
LockscreenCredential chosenPassword, LockscreenCredential currentCredential,
int userId) {
prepare(utils, requestGatekeeperPassword, userId);
mChosenPassword = chosenPassword;
mCurrentCredential = currentCredential != null ? currentCredential

View File

@@ -54,7 +54,6 @@ import com.android.internal.widget.LockPatternView.Cell;
import com.android.internal.widget.LockPatternView.DisplayMode;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.EncryptionInterstitial;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SetupWizardUtils;
@@ -106,7 +105,6 @@ public class ChooseLockPattern extends SettingsActivity {
public IntentBuilder(Context context) {
mIntent = new Intent(context, ChooseLockPattern.class);
mIntent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, false);
mIntent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false);
}
@@ -461,18 +459,6 @@ public class ChooseLockPattern extends SettingsActivity {
mLockPatternUtils = new LockPatternUtils(getActivity());
if (intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, false)) {
SaveAndFinishWorker w = new SaveAndFinishWorker();
final boolean required = getActivity().getIntent().getBooleanExtra(
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
LockscreenCredential current = intent.getParcelableExtra(
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
w.setBlocking(true);
w.setListener(this);
w.start(mLockPatternUtils, required, false /* requestGatekeeperPassword */, current,
current, mUserId);
}
mForFingerprint = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
mForFace = intent.getBooleanExtra(
@@ -812,7 +798,12 @@ public class ChooseLockPattern extends SettingsActivity {
// If the stage changed, announce the header for accessibility. This
// is a no-op when accessibility is disabled.
if (previousStage != stage || announceAlways) {
mHeaderText.announceForAccessibility(mHeaderText.getText());
if (stage == Stage.NeedToConfirm) {
// If the Stage is NeedToConfirm, move the a11y focus to the header.
mHeaderText.requestAccessibilityFocus();
} else {
mHeaderText.announceForAccessibility(mHeaderText.getText());
}
}
}
@@ -849,8 +840,6 @@ public class ChooseLockPattern extends SettingsActivity {
getFragmentManager().executePendingTransactions();
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)) {
@@ -860,8 +849,8 @@ public class ChooseLockPattern extends SettingsActivity {
profileCredential);
}
}
mSaveAndFinishWorker.start(mLockPatternUtils, required,
mRequestGatekeeperPassword, mChosenPattern, mCurrentCredential, mUserId);
mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword,
mChosenPattern, mCurrentCredential, mUserId);
}
@Override
@@ -890,10 +879,10 @@ public class ChooseLockPattern extends SettingsActivity {
private LockscreenCredential mChosenPattern;
private LockscreenCredential mCurrentCredential;
public void start(LockPatternUtils utils, boolean credentialRequired,
boolean requestGatekeeperPassword, LockscreenCredential chosenPattern,
LockscreenCredential currentCredential, int userId) {
prepare(utils, credentialRequired, requestGatekeeperPassword, userId);
public void start(LockPatternUtils utils, boolean requestGatekeeperPassword,
LockscreenCredential chosenPattern, LockscreenCredential currentCredential,
int userId) {
prepare(utils, requestGatekeeperPassword, userId);
mCurrentCredential = currentCredential != null ? currentCredential
: LockscreenCredential.createNone();

View File

@@ -21,10 +21,14 @@ import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.RemoteLockscreenValidationSession;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.UserManager;
import android.util.Log;
@@ -41,6 +45,8 @@ import com.android.settingslib.transition.SettingsTransitionHelper;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.Optional;
public final class ChooseLockSettingsHelper {
private static final String TAG = "ChooseLockSettingsHelper";
@@ -58,7 +64,8 @@ public final class ChooseLockSettingsHelper {
public static final String EXTRA_KEY_FOR_FACE = "for_face";
// For the paths where multiple biometric sensors exist
public static final String EXTRA_KEY_FOR_BIOMETRICS = "for_biometrics";
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
// For the paths where setup biometrics in suw flow
public static final String EXTRA_KEY_IS_SUW = "is_suw";
public static final String EXTRA_KEY_FOREGROUND_ONLY = "foreground_only";
public static final String EXTRA_KEY_REQUEST_GK_PW_HANDLE = "request_gk_pw_handle";
// Gatekeeper password handle, which can subsequently be used to generate Gatekeeper
@@ -133,6 +140,7 @@ public final class ChooseLockSettingsHelper {
@Nullable private CharSequence mHeader;
@Nullable private CharSequence mDescription;
@Nullable private CharSequence mAlternateButton;
@Nullable private CharSequence mCheckBoxLabel;
private boolean mReturnCredentials;
private boolean mExternal;
private boolean mForegroundOnly;
@@ -140,7 +148,11 @@ public final class ChooseLockSettingsHelper {
private int mUserId;
private boolean mAllowAnyUserId;
private boolean mForceVerifyPath;
boolean mRequestGatekeeperPasswordHandle;
private boolean mRemoteLockscreenValidation;
@Nullable private RemoteLockscreenValidationSession mRemoteLockscreenValidationSession;
@Nullable private ComponentName mRemoteLockscreenValidationServiceComponent;
private boolean mRequestGatekeeperPasswordHandle;
private boolean mTaskOverlay;
public Builder(@NonNull Activity activity) {
mActivity = activity;
@@ -192,6 +204,15 @@ public final class ChooseLockSettingsHelper {
return this;
}
/**
* @param checkboxLabel text for the checkbox
*/
@NonNull
public Builder setCheckboxLabel(@Nullable CharSequence checkboxLabel) {
mCheckBoxLabel = checkboxLabel;
return this;
}
/**
* @param returnCredentials if true, puts the following credentials into intent for
* onActivityResult with the following keys:
@@ -236,6 +257,14 @@ public final class ChooseLockSettingsHelper {
return this;
}
/**
* @param taskOverlay specifies whether the activity should be launched as a task overlay.
*/
@NonNull public Builder setTaskOverlay(boolean taskOverlay) {
mTaskOverlay = taskOverlay;
return this;
}
/**
* @param foregroundOnly if true, the confirmation activity will be finished if it loses
* foreground.
@@ -254,6 +283,42 @@ public final class ChooseLockSettingsHelper {
return this;
}
/**
* @param isRemoteLockscreenValidation if true, remote device validation flow will be
* started. {@link #setRemoteLockscreenValidationSession},
* {@link #setRemoteLockscreenValidationServiceComponent}
* must also be used to set the required data.
*/
@NonNull public Builder setRemoteLockscreenValidation(
boolean isRemoteLockscreenValidation) {
mRemoteLockscreenValidation = isRemoteLockscreenValidation;
return this;
}
/**
* @param remoteLockscreenValidationSession contains information necessary to perform remote
* lockscreen validation such as the remote device's
* lockscreen type, public key to be used for
* encryption, and remaining attempts.
*/
@NonNull public Builder setRemoteLockscreenValidationSession(
RemoteLockscreenValidationSession remoteLockscreenValidationSession) {
mRemoteLockscreenValidationSession = remoteLockscreenValidationSession;
return this;
}
/**
* @param remoteLockscreenValidationServiceComponent the {@link ComponentName} of the
* {@link android.service.remotelockscreenvalidation.RemoteLockscreenValidationService}
* that will be used to validate the lockscreen guess.
*/
@NonNull public Builder setRemoteLockscreenValidationServiceComponent(
ComponentName remoteLockscreenValidationServiceComponent) {
mRemoteLockscreenValidationServiceComponent =
remoteLockscreenValidationServiceComponent;
return this;
}
/**
* Requests that LockSettingsService return a handle to the Gatekeeper Password (instead of
* the Gatekeeper HAT). This allows us to use a single entry of the user's credential
@@ -287,7 +352,7 @@ public final class ChooseLockSettingsHelper {
Utils.enforceSameOwner(mActivity, mUserId);
}
if (mExternal && mReturnCredentials) {
if (mExternal && mReturnCredentials && !mRemoteLockscreenValidation) {
throw new IllegalArgumentException("External and ReturnCredentials specified. "
+ " External callers should never be allowed to receive credentials in"
+ " onActivityResult");
@@ -316,49 +381,44 @@ public final class ChooseLockSettingsHelper {
return launchConfirmationActivity(mBuilder.mRequestCode, mBuilder.mTitle, mBuilder.mHeader,
mBuilder.mDescription, mBuilder.mReturnCredentials, mBuilder.mExternal,
mBuilder.mForceVerifyPath, mBuilder.mUserId, mBuilder.mAlternateButton,
mBuilder.mAllowAnyUserId, mBuilder.mForegroundOnly,
mBuilder.mRequestGatekeeperPasswordHandle);
mBuilder.mCheckBoxLabel, mBuilder.mRemoteLockscreenValidation,
mBuilder.mRemoteLockscreenValidationSession,
mBuilder.mRemoteLockscreenValidationServiceComponent, mBuilder.mAllowAnyUserId,
mBuilder.mForegroundOnly, mBuilder.mRequestGatekeeperPasswordHandle,
mBuilder.mTaskOverlay);
}
private boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
@Nullable CharSequence header, @Nullable CharSequence description,
boolean returnCredentials, boolean external, boolean forceVerifyPath,
int userId, @Nullable CharSequence alternateButton, boolean allowAnyUser,
boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) {
final int effectiveUserId = UserManager.get(mActivity).getCredentialOwnerProfile(userId);
boolean launched = false;
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId)) {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
launched = launchConfirmationActivity(request, title, header, description,
returnCredentials || forceVerifyPath
? ConfirmLockPattern.InternalActivity.class
: ConfirmLockPattern.class, returnCredentials, external,
forceVerifyPath, userId, alternateButton, allowAnyUser,
foregroundOnly, requestGatekeeperPasswordHandle);
break;
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
launched = launchConfirmationActivity(request, title, header, description,
returnCredentials || forceVerifyPath
? ConfirmLockPassword.InternalActivity.class
: ConfirmLockPassword.class, returnCredentials, external,
forceVerifyPath, userId, alternateButton, allowAnyUser,
foregroundOnly, requestGatekeeperPasswordHandle);
break;
int userId, @Nullable CharSequence alternateButton,
@Nullable CharSequence checkboxLabel, boolean remoteLockscreenValidation,
@Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession,
@Nullable ComponentName remoteLockscreenValidationServiceComponent,
boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle,
boolean taskOverlay) {
Optional<Class<?>> activityClass = determineAppropriateActivityClass(
returnCredentials, forceVerifyPath, userId, remoteLockscreenValidationSession);
if (activityClass.isEmpty()) {
return false;
}
return launched;
return launchConfirmationActivity(request, title, header, description, activityClass.get(),
returnCredentials, external, forceVerifyPath, userId, alternateButton,
checkboxLabel, remoteLockscreenValidation, remoteLockscreenValidationSession,
remoteLockscreenValidationServiceComponent, allowAnyUser, foregroundOnly,
requestGatekeeperPasswordHandle, taskOverlay);
}
private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
CharSequence message, Class<?> activityClass, boolean returnCredentials,
boolean external, boolean forceVerifyPath, int userId,
@Nullable CharSequence alternateButton, boolean allowAnyUser,
boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) {
@Nullable CharSequence alternateButton, @Nullable CharSequence checkbox,
boolean remoteLockscreenValidation,
@Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession,
@Nullable ComponentName remoteLockscreenValidationServiceComponent,
boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle,
boolean taskOverlay) {
final Intent intent = new Intent();
intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
@@ -368,10 +428,16 @@ public final class ChooseLockSettingsHelper {
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, false);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.USE_FADE_ANIMATION, external);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION,
remoteLockscreenValidation);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, forceVerifyPath);
intent.putExtra(Intent.EXTRA_USER_ID, userId);
intent.putExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL, alternateButton);
intent.putExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL, checkbox);
intent.putExtra(KeyguardManager.EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION,
remoteLockscreenValidationSession);
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, remoteLockscreenValidationServiceComponent);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOREGROUND_ONLY, foregroundOnly);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, allowAnyUser);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE,
@@ -384,28 +450,91 @@ public final class ChooseLockSettingsHelper {
Intent inIntent = mFragment != null ? mFragment.getActivity().getIntent() :
mActivity.getIntent();
copyInternalExtras(inIntent, intent);
Bundle launchOptions = createLaunchOptions(taskOverlay);
if (external) {
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
copyOptionalExtras(inIntent, intent);
if (mActivityResultLauncher != null) {
mActivityResultLauncher.launch(intent);
} else if (mFragment != null) {
mFragment.startActivity(intent);
mFragment.startActivity(intent, launchOptions);
} else {
mActivity.startActivity(intent);
mActivity.startActivity(intent, launchOptions);
}
} else {
if (mActivityResultLauncher != null) {
mActivityResultLauncher.launch(intent);
} else if (mFragment != null) {
mFragment.startActivityForResult(intent, request);
mFragment.startActivityForResult(intent, request, launchOptions);
} else {
mActivity.startActivityForResult(intent, request);
mActivity.startActivityForResult(intent, request, launchOptions);
}
}
return true;
}
private Bundle createLaunchOptions(boolean taskOverlay) {
if (!taskOverlay) {
return null;
}
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchTaskId(mActivity.getTaskId());
options.setTaskOverlay(true /* taskOverlay */, true /* canResume */);
return options.toBundle();
}
private Optional<Integer> passwordQualityToLockTypes(int quality) {
switch (quality) {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
return Optional.of(KeyguardManager.PATTERN);
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
return Optional.of(KeyguardManager.PIN);
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
return Optional.of(KeyguardManager.PASSWORD);
}
Log.e(TAG, String.format(
"Cannot determine appropriate activity class for password quality %d",
quality));
return Optional.empty();
}
private Optional<Class<?>> determineAppropriateActivityClass(boolean returnCredentials,
boolean forceVerifyPath, int userId,
@Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession) {
int lockType;
if (remoteLockscreenValidationSession != null) {
lockType = remoteLockscreenValidationSession.getLockType();
} else {
final int effectiveUserId = UserManager
.get(mActivity).getCredentialOwnerProfile(userId);
Optional<Integer> lockTypeOptional = passwordQualityToLockTypes(
mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId));
if (lockTypeOptional.isEmpty()) {
return Optional.empty();
}
lockType = lockTypeOptional.get();
}
switch (lockType) {
case KeyguardManager.PASSWORD:
case KeyguardManager.PIN:
return Optional.of(returnCredentials || forceVerifyPath
? ConfirmLockPassword.InternalActivity.class
: ConfirmLockPassword.class);
case KeyguardManager.PATTERN:
return Optional.of(returnCredentials || forceVerifyPath
? ConfirmLockPattern.InternalActivity.class
: ConfirmLockPattern.class);
}
Log.e(TAG, String.format("Cannot determine appropriate activity class for lock type %d",
lockType));
return Optional.empty();
}
private void copyOptionalExtras(Intent inIntent, Intent outIntent) {
IntentSender intentSender = inIntent.getParcelableExtra(Intent.EXTRA_INTENT);
if (intentSender != null) {

View File

@@ -24,12 +24,12 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROF
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import android.app.Activity;
import android.app.KeyguardManager;
import android.app.RemoteLockscreenValidationSession;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
@@ -61,13 +61,6 @@ 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
@@ -78,15 +71,6 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
public static class InternalActivity extends ConfirmDeviceCredentialActivity {
}
public static Intent createIntent(CharSequence title, CharSequence details) {
Intent intent = new Intent();
intent.setClassName(SETTINGS_PACKAGE_NAME,
ConfirmDeviceCredentialActivity.class.getName());
intent.putExtra(KeyguardManager.EXTRA_TITLE, title);
intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION, details);
return intent;
}
private BiometricFragment mBiometricFragment;
private DevicePolicyManager mDevicePolicyManager;
private LockPatternUtils mLockPatternUtils;
@@ -95,9 +79,10 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
private Handler mHandler = new Handler(Looper.getMainLooper());
private Context mContext;
private boolean mCheckDevicePolicyManager;
private boolean mTaskOverlay;
private String mTitle;
private String mDetails;
private CharSequence mDetails;
private int mUserId;
private int mCredentialMode;
private boolean mGoingToBackground;
@@ -178,10 +163,14 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
mCheckDevicePolicyManager = intent
.getBooleanExtra(KeyguardManager.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
mDetails = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
mDetails = intent.getCharSequenceExtra(KeyguardManager.EXTRA_DESCRIPTION);
String alternateButton = intent.getStringExtra(
KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
boolean remoteValidation =
KeyguardManager.ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL.equals(intent.getAction());
mTaskOverlay = isInternalActivity()
&& intent.getBooleanExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, false);
mUserId = UserHandle.myUserId();
if (isInternalActivity()) {
@@ -230,6 +219,31 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
.setExternal(true)
.setUserId(LockPatternUtils.USER_FRP)
.show();
} else if (remoteValidation) {
RemoteLockscreenValidationSession remoteLockscreenValidationSession =
intent.getParcelableExtra(
KeyguardManager.EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION,
RemoteLockscreenValidationSession.class);
ComponentName remoteLockscreenValidationServiceComponent =
intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName.class);
String checkboxLabel = intent.getStringExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
final ChooseLockSettingsHelper.Builder builder =
new ChooseLockSettingsHelper.Builder(this);
launchedCDC = builder
.setRemoteLockscreenValidation(true)
.setRemoteLockscreenValidationSession(remoteLockscreenValidationSession)
.setRemoteLockscreenValidationServiceComponent(
remoteLockscreenValidationServiceComponent)
.setRequestGatekeeperPasswordHandle(true)
.setReturnCredentials(true) // returns only password handle.
.setHeader(mTitle) // Show the title in the header location
.setDescription(mDetails)
.setCheckboxLabel(checkboxLabel)
.setAlternateButton(alternateButton)
.setExternal(true)
.show();
return;
} else if (isEffectiveUserManagedProfile && isInternalActivity()) {
mCredentialMode = CREDENTIAL_MANAGED;
if (isBiometricAllowed(effectiveUserId, mUserId)) {
@@ -391,6 +405,12 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
*/
private void showConfirmCredentials() {
boolean launched = false;
ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this)
.setHeader(mTitle)
.setDescription(mDetails)
.setExternal(true)
.setUserId(mUserId)
.setTaskOverlay(mTaskOverlay);
// The only difference between CREDENTIAL_MANAGED and CREDENTIAL_NORMAL is that for
// CREDENTIAL_MANAGED, we launch the real confirm credential activity with an explicit
// but fake challenge value (0L). This will result in ConfirmLockPassword calling
@@ -403,22 +423,9 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
// LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to use,
// which optionally accepts a challenge.
if (mCredentialMode == CREDENTIAL_MANAGED) {
final ChooseLockSettingsHelper.Builder builder =
new ChooseLockSettingsHelper.Builder(this);
launched = builder.setHeader(mTitle)
.setDescription(mDetails)
.setExternal(true)
.setUserId(mUserId)
.setForceVerifyPath(true)
.show();
launched = builder.setForceVerifyPath(true).show();
} else if (mCredentialMode == CREDENTIAL_NORMAL) {
final ChooseLockSettingsHelper.Builder builder =
new ChooseLockSettingsHelper.Builder(this);
launched = builder.setHeader(mTitle) // Show the title string in the header area
.setDescription(mDetails)
.setExternal(true)
.setUserId(mUserId)
.show();
launched = builder.show();
}
if (!launched) {
Log.d(TAG, "No pin/pattern/pass set");

View File

@@ -17,6 +17,7 @@
// TODO (b/35202196): move this class out of the root of the package.
package com.android.settings.password;
import static android.app.Activity.RESULT_FIRST_USER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_ATTEMPTS_FAILED;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
@@ -24,7 +25,10 @@ import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import android.annotation.Nullable;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.app.RemoteLockscreenValidationSession;
import android.app.admin.DevicePolicyManager;
import android.app.admin.ManagedSubscriptionsPolicy;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -34,10 +38,14 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
@@ -45,10 +53,13 @@ import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.InstrumentedFragment;
import com.google.android.setupdesign.GlifLayout;
/**
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
*/
@@ -64,6 +75,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.showWhenLocked";
public static final String USE_FADE_ANIMATION =
SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.useFadeAnimation";
public static final String IS_REMOTE_LOCKSCREEN_VALIDATION =
SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.isRemoteLockscreenValidation";
protected static final int USER_TYPE_PRIMARY = 1;
protected static final int USER_TYPE_MANAGED_PROFILE = 2;
@@ -72,9 +85,14 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
/** Time we wait before clearing a wrong input attempt (e.g. pattern) and the error message. */
protected static final long CLEAR_WRONG_ATTEMPT_TIMEOUT_MS = 3000;
protected static final String FRAGMENT_TAG_REMOTE_LOCKSCREEN_VALIDATION =
"remote_lockscreen_validation";
protected boolean mReturnCredentials = false;
protected boolean mReturnGatekeeperPassword = false;
protected boolean mForceVerifyPath = false;
protected GlifLayout mGlifLayout;
protected CheckBox mCheckBox;
protected Button mCancelButton;
/** Button allowing managed profile password reset, null when is not shown. */
@Nullable protected Button mForgotButton;
@@ -86,8 +104,13 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
protected TextView mErrorTextView;
protected final Handler mHandler = new Handler();
protected boolean mFrp;
private CharSequence mFrpAlternateButtonText;
protected boolean mRemoteValidation;
protected CharSequence mAlternateButtonText;
protected BiometricManager mBiometricManager;
@Nullable protected RemoteLockscreenValidationSession mRemoteLockscreenValidationSession;
/** Credential saved so the credential can be set for device if remote validation passes */
@Nullable protected RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
protected RemoteLockscreenValidationFragment mRemoteLockscreenValidationFragment;
private boolean isInternalActivity() {
return (getActivity() instanceof ConfirmLockPassword.InternalActivity)
@@ -98,7 +121,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getActivity().getIntent();
mFrpAlternateButtonText = intent.getCharSequenceExtra(
mAlternateButtonText = intent.getCharSequenceExtra(
KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
mReturnCredentials = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, false);
@@ -108,6 +131,49 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
mForceVerifyPath = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, false);
if (intent.getBooleanExtra(IS_REMOTE_LOCKSCREEN_VALIDATION, false)) {
if (FeatureFlagUtils.isEnabled(getContext(),
FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION)) {
mRemoteValidation = true;
} else {
onRemoteLockscreenValidationFailure(
"Remote lockscreen validation not enabled.");
}
}
if (mRemoteValidation) {
mRemoteLockscreenValidationSession = intent.getParcelableExtra(
KeyguardManager.EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION,
RemoteLockscreenValidationSession.class);
if (mRemoteLockscreenValidationSession == null
|| mRemoteLockscreenValidationSession.getRemainingAttempts() == 0) {
onRemoteLockscreenValidationFailure("RemoteLockscreenValidationSession is null or "
+ "no more attempts for remote lockscreen validation.");
}
ComponentName remoteLockscreenValidationServiceComponent =
intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName.class);
if (remoteLockscreenValidationServiceComponent == null) {
onRemoteLockscreenValidationFailure(
"RemoteLockscreenValidationService ComponentName is null");
}
mRemoteLockscreenValidationClient = RemoteLockscreenValidationClient
.create(getContext(), remoteLockscreenValidationServiceComponent);
if (!mRemoteLockscreenValidationClient.isServiceAvailable()) {
onRemoteLockscreenValidationFailure(String.format(
"RemoteLockscreenValidationService at %s is not available",
remoteLockscreenValidationServiceComponent.getClassName()));
}
mRemoteLockscreenValidationFragment =
(RemoteLockscreenValidationFragment) getFragmentManager()
.findFragmentByTag(FRAGMENT_TAG_REMOTE_LOCKSCREEN_VALIDATION);
if (mRemoteLockscreenValidationFragment == null) {
mRemoteLockscreenValidationFragment = new RemoteLockscreenValidationFragment();
getFragmentManager().beginTransaction().add(mRemoteLockscreenValidationFragment,
FRAGMENT_TAG_REMOTE_LOCKSCREEN_VALIDATION).commit();
}
}
// Only take this argument into account if it belongs to the current profile.
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras(),
isInternalActivity());
@@ -124,21 +190,54 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mCancelButton = view.findViewById(R.id.cancelButton);
boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
boolean showCancelButton = mRemoteValidation || getActivity().getIntent().getBooleanExtra(
SHOW_CANCEL_BUTTON, false);
boolean hasAlternateButton = mFrp && !TextUtils.isEmpty(mFrpAlternateButtonText);
boolean hasAlternateButton = (mFrp || mRemoteValidation) && !TextUtils.isEmpty(
mAlternateButtonText);
mCancelButton.setVisibility(showCancelButton || hasAlternateButton
? View.VISIBLE : View.GONE);
if (hasAlternateButton) {
mCancelButton.setText(mFrpAlternateButtonText);
mCancelButton.setText(mAlternateButtonText);
}
mCancelButton.setOnClickListener(v -> {
if (hasAlternateButton) {
getActivity().setResult(KeyguardManager.RESULT_ALTERNATE);
getActivity().finish();
} else if (mRemoteValidation) {
onRemoteLockscreenValidationFailure("Forgot lockscreen credential button pressed.");
}
getActivity().finish();
});
setupForgotButtonIfManagedProfile(view);
mCheckBox = view.findViewById(R.id.checkbox);
if (mCheckBox != null && mRemoteValidation) {
mCheckBox.setVisibility(View.VISIBLE);
}
setupEmergencyCallButtonIfManagedSubscription(view);
}
private void setupEmergencyCallButtonIfManagedSubscription(View view) {
int policyType = getContext().getSystemService(
DevicePolicyManager.class).getManagedSubscriptionsPolicy().getPolicyType();
if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
Button emergencyCallButton = view.findViewById(R.id.emergencyCallButton);
if (emergencyCallButton == null) {
Log.wtf(TAG,
"Emergency call button not found in managed profile credential dialog");
return;
}
emergencyCallButton.setVisibility(View.VISIBLE);
emergencyCallButton.setOnClickListener(v -> {
final Intent intent = getActivity()
.getSystemService(TelecomManager.class)
.createLaunchEmergencyDialerIntent(null)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
getActivity().startActivity(intent);
getActivity().finish();
});
}
}
private void setupForgotButtonIfManagedProfile(View view) {
@@ -205,8 +304,15 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
super.onPause();
}
protected abstract void authenticationSucceeded();
@Override
public void onDestroy() {
if (mRemoteLockscreenValidationClient != null) {
mRemoteLockscreenValidationClient.disconnect();
}
super.onDestroy();
}
protected abstract void authenticationSucceeded();
public void prepareEnterAnimation() {
}
@@ -283,8 +389,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
case USER_TYPE_MANAGED_PROFILE:
return mDevicePolicyManager.getResources().getString(
WORK_PROFILE_LOCK_ATTEMPTS_FAILED,
() -> getString(com.android.settingslib
.R.string.failed_attempts_now_wiping_profile));
() -> getString(
com.android.settingslib.R.string.failed_attempts_now_wiping_profile));
case USER_TYPE_SECONDARY:
return getString(com.android.settingslib.R.string.failed_attempts_now_wiping_user);
default:
@@ -308,6 +414,36 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
}
}
protected void validateGuess(LockscreenCredential credentialGuess) {
mRemoteLockscreenValidationFragment.validateLockscreenGuess(
mRemoteLockscreenValidationClient, credentialGuess,
mRemoteLockscreenValidationSession.getSourcePublicKey(), mCheckBox.isChecked());
}
protected void updateRemoteLockscreenValidationViews() {
if (!mRemoteValidation || mRemoteLockscreenValidationFragment == null) {
return;
}
boolean enable = mRemoteLockscreenValidationFragment.isRemoteValidationInProgress();
mGlifLayout.setProgressBarShown(enable);
mCheckBox.setEnabled(!enable);
mCancelButton.setEnabled(!enable);
}
/**
* Finishes the activity with result code {@link android.app.Activity#RESULT_FIRST_USER}
* after logging the error message.
* @param message Optional message to log.
*/
public void onRemoteLockscreenValidationFailure(String message) {
if (!TextUtils.isEmpty(message)) {
Log.w(TAG, message);
}
getActivity().setResult(RESULT_FIRST_USER);
getActivity().finish();
}
protected abstract void onShowError();
protected void showError(int msg, long timeout) {

View File

@@ -25,6 +25,11 @@ import android.content.Intent;
import android.content.IntentSender;
import android.os.RemoteException;
import android.os.UserManager;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import androidx.annotation.NonNull;
import com.android.internal.widget.LockPatternUtils;
@@ -47,7 +52,12 @@ public class ConfirmDeviceCredentialUtils {
IntentSender intentSender = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
if (intentSender != null) {
try {
activity.startIntentSenderForResult(intentSender, -1, null, 0, 0, 0);
ActivityOptions activityOptions =
ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
activity.startIntentSenderForResult(intentSender, -1, null, 0, 0, 0,
activityOptions.toBundle());
} catch (IntentSender.SendIntentException e) {
/* ignore */
}
@@ -67,4 +77,16 @@ public class ConfirmDeviceCredentialUtils {
utils.userPresent(userId);
}
}
/**
* Request hiding soft-keyboard before animating away credential UI, in case IME
* insets animation get delayed by dismissing animation.
* @param view used to get root {@link WindowInsets} and {@link WindowInsetsController}.
*/
public static void hideImeImmediately(@NonNull View view) {
if (view.isAttachedToWindow()
&& view.getRootWindowInsets().isVisible(WindowInsets.Type.ime())) {
view.getWindowInsetsController().hide(WindowInsets.Type.ime());
}
}
}

View File

@@ -26,7 +26,13 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROF
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PIN_REQUIRED;
import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static com.android.settings.biometrics.GatekeeperPasswordProvider.containsGatekeeperPasswordHandle;
import static com.android.settings.biometrics.GatekeeperPasswordProvider.getGatekeeperPasswordHandle;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.app.RemoteLockscreenValidationResult;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -42,6 +48,7 @@ import android.os.UserManager;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -64,8 +71,6 @@ import com.android.settings.R;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.google.android.setupdesign.GlifLayout;
import java.util.ArrayList;
public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
@@ -114,13 +119,14 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
super.onWindowFocusChanged(hasFocus);
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_content);
if (fragment != null && fragment instanceof ConfirmLockPasswordFragment) {
((ConfirmLockPasswordFragment)fragment).onWindowFocusChanged(hasFocus);
((ConfirmLockPasswordFragment) fragment).onWindowFocusChanged(hasFocus);
}
}
public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
implements OnClickListener, OnEditorActionListener,
CredentialCheckResultTracker.Listener {
CredentialCheckResultTracker.Listener, SaveChosenLockWorkerBase.Listener,
RemoteLockscreenValidationFragment.Listener {
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
private ImeAwareEditText mPasswordEntry;
private TextViewInputDisabler mPasswordEntryInputDisabler;
@@ -133,7 +139,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
private AppearAnimationUtils mAppearAnimationUtils;
private DisappearAnimationUtils mDisappearAnimationUtils;
private boolean mIsManagedProfile;
private GlifLayout mGlifLayout;
private CharSequence mCheckBoxLabel;
// required constructor for fragments
public ConfirmLockPasswordFragment() {
@@ -160,11 +166,19 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
mPasswordEntry.requestFocus();
mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
if (mRemoteValidation) {
mIsAlpha = mRemoteLockscreenValidationSession.getLockType()
== KeyguardManager.PASSWORD;
// ProgressBar visibility is set to GONE until interacted with.
// Set progress bar to INVISIBLE, so the EditText does not get bumped down later.
mGlifLayout.setProgressBarShown(false);
} else {
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
}
mImm = (InputMethodManager) getActivity().getSystemService(
Context.INPUT_METHOD_SERVICE);
@@ -187,6 +201,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
}
mGlifLayout.setHeaderText(headerMessage);
mGlifLayout.setDescriptionText(detailsMessage);
mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
}
int currentType = mPasswordEntry.getInputType();
if (mIsAlpha) {
@@ -227,6 +242,20 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mRemoteValidation) {
if (mCheckBox != null) {
mCheckBox.setText(TextUtils.isEmpty(mCheckBoxLabel)
? getDefaultCheckboxLabel()
: mCheckBoxLabel);
}
if (mCancelButton != null && TextUtils.isEmpty(mAlternateButtonText)) {
mCancelButton.setText(mIsAlpha
? R.string.lockpassword_forgot_password
: R.string.lockpassword_forgot_pin);
}
updateRemoteLockscreenValidationViews();
}
if (mForgotButton != null) {
mForgotButton.setText(mIsAlpha
? R.string.lockpassword_forgot_password
@@ -237,7 +266,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
@Override
public void onDestroy() {
super.onDestroy();
mPasswordEntry.setText(null);
if (mPasswordEntry != null) {
mPasswordEntry.setText(null);
}
// Force a garbage collection to remove remnant of user password shards from memory.
// Execute this with a slight delay to allow the activity lifecycle to complete and
// the instance to become gc-able.
@@ -253,6 +284,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_header_frp)
: getString(R.string.lockpassword_confirm_your_pin_header_frp);
}
if (mRemoteValidation) {
return getString(R.string.lockpassword_remote_validation_header);
}
if (mIsManagedProfile) {
if (mIsAlpha) {
return mDevicePolicyManager.getResources().getString(
@@ -273,6 +307,11 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_details_frp)
: getString(R.string.lockpassword_confirm_your_pin_details_frp);
}
if (mRemoteValidation) {
return getContext().getString(mIsAlpha
? R.string.lockpassword_remote_validation_password_details
: R.string.lockpassword_remote_validation_pin_details);
}
boolean isStrongAuthRequired = isStrongAuthRequired();
// Map boolean flags to an index by isStrongAuth << 2 + isManagedProfile << 1 + isAlpha.
int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((mIsManagedProfile ? 1 : 0) << 1)
@@ -281,6 +320,16 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
DETAIL_TEXT_OVERRIDES[index], () -> getString(DETAIL_TEXTS[index]));
}
private String getDefaultCheckboxLabel() {
if (mRemoteValidation) {
return getString(mIsAlpha
? R.string.lockpassword_remote_validation_set_password_as_screenlock
: R.string.lockpassword_remote_validation_set_pin_as_screenlock);
}
throw new IllegalStateException(
"Trying to get default checkbox label for illegal flow");
}
private int getErrorMessage() {
return mIsAlpha ? R.string.lockpassword_invalid_password
: R.string.lockpassword_invalid_pin;
@@ -355,6 +404,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
mCountdownTimer = null;
}
mCredentialCheckResultTracker.setListener(null);
if (mRemoteLockscreenValidationFragment != null) {
mRemoteLockscreenValidationFragment.setListener(null, /* handler= */ null);
}
}
@Override
@@ -376,6 +428,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
}
mCredentialCheckResultTracker.setListener(this);
if (mRemoteLockscreenValidationFragment != null) {
mRemoteLockscreenValidationFragment.setListener(this, mHandler);
}
}
@Override
@@ -386,12 +441,17 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
private void updatePasswordEntry() {
final boolean isLockedOut =
mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId) != 0;
mPasswordEntry.setEnabled(!isLockedOut);
mPasswordEntryInputDisabler.setInputEnabled(!isLockedOut);
if (isLockedOut) {
mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), 0 /*flags*/);
} else {
final boolean isRemoteLockscreenValidationInProgress =
mRemoteLockscreenValidationFragment != null
&& mRemoteLockscreenValidationFragment.isRemoteValidationInProgress();
boolean shouldEnableInput = !isLockedOut && !isRemoteLockscreenValidationInProgress;
mPasswordEntry.setEnabled(shouldEnableInput);
mPasswordEntryInputDisabler.setInputEnabled(shouldEnableInput);
if (shouldEnableInput) {
mPasswordEntry.scheduleShowSoftInput();
mPasswordEntry.requestFocus();
} else {
mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), /* flags= */0);
}
}
@@ -413,12 +473,19 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
if (TextUtils.isEmpty(passwordText)) {
return;
}
final LockscreenCredential credential =
mIsAlpha ? LockscreenCredential.createPassword(passwordText)
final LockscreenCredential credential = mIsAlpha
? LockscreenCredential.createPassword(passwordText)
: LockscreenCredential.createPin(passwordText);
mPasswordEntryInputDisabler.setInputEnabled(false);
if (mRemoteValidation) {
validateGuess(credential);
updateRemoteLockscreenValidationViews();
updatePasswordEntry();
return;
}
Intent intent = new Intent();
// TODO(b/161956762): Sanitize this
if (mReturnGatekeeperPassword) {
@@ -493,6 +560,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
}
private void startDisappearAnimation(final Intent intent) {
ConfirmDeviceCredentialUtils.hideImeImmediately(
getActivity().getWindow().getDecorView());
if (mDisappearing) {
return;
}
@@ -543,6 +613,51 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
}
}
@Override
public void onRemoteLockscreenValidationResult(
RemoteLockscreenValidationResult result) {
switch (result.getResultCode()) {
case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
.getLockscreenCredential() != null) {
Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
ChooseLockPassword.SaveAndFinishWorker saveAndFinishWorker =
new ChooseLockPassword.SaveAndFinishWorker();
getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
.commit();
getFragmentManager().executePendingTransactions();
saveAndFinishWorker.setListener(this);
saveAndFinishWorker.start(
mLockPatternUtils,
/* requestGatekeeperPassword= */ true,
mRemoteLockscreenValidationFragment.getLockscreenCredential(),
/* currentCredential= */ null,
mEffectiveUserId);
} else {
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
/* timeoutMs= */ 0, mEffectiveUserId);
}
return;
case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
/* timeoutMs= */ 0, mEffectiveUserId);
break;
case RemoteLockscreenValidationResult.RESULT_LOCKOUT:
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
(int) result.getTimeoutMillis(), mEffectiveUserId);
break;
case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
case RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED:
onRemoteLockscreenValidationFailure(String.format(
"Cannot continue remote lockscreen validation. ResultCode=%d",
result.getResultCode()));
break;
}
updateRemoteLockscreenValidationViews();
updatePasswordEntry();
mRemoteLockscreenValidationFragment.clearLockscreenCredential();
}
@Override
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
int effectiveUserId, boolean newResult) {
@@ -598,5 +713,22 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
}
return false;
}
/**
* Callback for when the current device's lockscreen was set to the guess used for
* remote lockscreen validation.
*/
@Override
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
Log.i(TAG, "Device lockscreen has been set to remote device's lockscreen.");
mRemoteLockscreenValidationFragment.clearLockscreenCredential();
Intent result = new Intent();
if (mRemoteValidation && containsGatekeeperPasswordHandle(resultData)) {
result.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGatekeeperPasswordHandle(resultData));
}
mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
/* timeoutMs= */ 0, mEffectiveUserId);
}
}
}

View File

@@ -17,14 +17,18 @@
package com.android.settings.password;
import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PATTERN_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PATTERN_REQUIRED;
import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static com.android.settings.biometrics.GatekeeperPasswordProvider.containsGatekeeperPasswordHandle;
import static com.android.settings.biometrics.GatekeeperPasswordProvider.getGatekeeperPasswordHandle;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.KeyguardManager;
import android.app.RemoteLockscreenValidationResult;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.os.AsyncTask;
@@ -33,6 +37,7 @@ import android.os.CountDownTimer;
import android.os.SystemClock;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -52,8 +57,6 @@ import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.google.android.setupdesign.GlifLayout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -89,7 +92,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
}
public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener {
implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener,
SaveChosenLockWorkerBase.Listener, RemoteLockscreenValidationFragment.Listener {
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
@@ -99,12 +103,12 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
private boolean mDisappearing = false;
private CountDownTimer mCountdownTimer;
private GlifLayout mGlifLayout;
private View mSudContent;
// caller-supplied text for various prompts
private CharSequence mHeaderText;
private CharSequence mDetailsText;
private CharSequence mCheckBoxLabel;
private AppearAnimationUtils mAppearAnimationUtils;
private DisappearAnimationUtils mDisappearAnimationUtils;
@@ -148,6 +152,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
mDetailsText = intent.getCharSequenceExtra(
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
}
if (TextUtils.isEmpty(mHeaderText) && mIsManagedProfile) {
mHeaderText = mDevicePolicyManager.getOrganizationNameForUser(mUserId);
@@ -174,7 +179,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
// ability to disable the pattern in L. Remove this block after
// ensuring it's safe to do so. (Note that ConfirmLockPassword
// doesn't have this).
if (!mFrp && !mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
if (!mFrp && !mRemoteValidation
&& !mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
}
@@ -203,12 +209,34 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
}
if (mRemoteValidation) {
// ProgressBar visibility is set to GONE until interacted with.
// Set progress bar to INVISIBLE, so the pattern does not get bumped down later.
mGlifLayout.setProgressBarShown(false);
// Lock pattern is generally not visible until the user has set a lockscreen for the
// first time. For a new user, this means that the pattern will always be hidden.
// Despite this prerequisite, we want to show the pattern anyway for this flow.
mLockPatternView.setInStealthMode(false);
}
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mRemoteValidation) {
if (mCheckBox != null) {
mCheckBox.setText(TextUtils.isEmpty(mCheckBoxLabel)
? getDefaultCheckboxLabel()
: mCheckBoxLabel);
}
if (mCancelButton != null && TextUtils.isEmpty(mAlternateButtonText)) {
mCancelButton.setText(R.string.lockpassword_forgot_pattern);
}
updateRemoteLockscreenValidationViews();
}
if (mForgotButton != null) {
mForgotButton.setText(R.string.lockpassword_forgot_pattern);
}
@@ -227,6 +255,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
mCountdownTimer.cancel();
}
mCredentialCheckResultTracker.setListener(null);
if (mRemoteLockscreenValidationFragment != null) {
mRemoteLockscreenValidationFragment.setListener(null, /* handler= */ null);
}
}
@Override
@@ -249,6 +280,12 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
updateStage(Stage.NeedToUnlock);
}
mCredentialCheckResultTracker.setListener(this);
if (mRemoteLockscreenValidationFragment != null) {
mRemoteLockscreenValidationFragment.setListener(this, mHandler);
if (mRemoteLockscreenValidationFragment.isRemoteValidationInProgress()) {
mLockPatternView.setEnabled(false);
}
}
}
@Override
@@ -271,24 +308,17 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
if (mFrp) {
return getString(R.string.lockpassword_confirm_your_pattern_details_frp);
}
if (mRemoteValidation) {
return getString(
R.string.lockpassword_remote_validation_pattern_details);
}
final boolean isStrongAuthRequired = isStrongAuthRequired();
if (mIsManagedProfile) {
if (isStrongAuthRequired) {
return mDevicePolicyManager.getResources().getString(
WORK_PROFILE_PATTERN_REQUIRED,
() -> getString(
R.string.lockpassword_strong_auth_required_work_pattern));
} else {
return mDevicePolicyManager.getResources().getString(
WORK_PROFILE_CONFIRM_PATTERN,
() -> getString(
R.string.lockpassword_confirm_your_pattern_generic_profile));
}
} else {
if (!mIsManagedProfile) {
return isStrongAuthRequired
? getString(R.string.lockpassword_strong_auth_required_device_pattern)
: getString(R.string.lockpassword_confirm_your_pattern_generic);
}
return null;
}
private Object[][] getActiveViews() {
@@ -335,11 +365,13 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
} else {
mGlifLayout.setHeaderText(getDefaultHeader());
}
if (mDetailsText != null) {
mGlifLayout.setDescriptionText(mDetailsText);
} else {
mGlifLayout.setDescriptionText(getDefaultDetails());
CharSequence detailsText =
mDetailsText == null ? getDefaultDetails() : mDetailsText;
if (detailsText != null) {
mGlifLayout.setDescriptionText(detailsText);
}
mErrorTextView.setText("");
updateErrorMessage(
mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
@@ -371,7 +403,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
private String getDefaultHeader() {
if (mFrp) return getString(R.string.lockpassword_confirm_your_pattern_header_frp);
if (mRemoteValidation) {
return getString(R.string.lockpassword_remote_validation_header);
}
if (mIsManagedProfile) {
return mDevicePolicyManager.getResources().getString(
CONFIRM_WORK_PROFILE_PATTERN_HEADER,
@@ -381,6 +415,14 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
return getString(R.string.lockpassword_confirm_your_pattern_header);
}
private String getDefaultCheckboxLabel() {
if (mRemoteValidation) {
return getString(R.string.lockpassword_remote_validation_set_pattern_as_screenlock);
}
throw new IllegalStateException(
"Trying to get default checkbox label for illegal flow");
}
private Runnable mClearPatternRunnable = new Runnable() {
public void run() {
mLockPatternView.clearPattern();
@@ -431,7 +473,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
* an existing lock pattern.
*/
private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener
= new LockPatternView.OnPatternListener() {
= new LockPatternView.OnPatternListener() {
public void onPatternStart() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
@@ -453,6 +495,13 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
mLockPatternView.setEnabled(false);
final LockscreenCredential credential = LockscreenCredential.createPattern(pattern);
if (mRemoteValidation) {
validateGuess(credential);
updateRemoteLockscreenValidationViews();
return;
}
// TODO(b/161956762): Sanitize this
Intent intent = new Intent();
if (mReturnGatekeeperPassword) {
@@ -563,6 +612,50 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
}
}
@Override
public void onRemoteLockscreenValidationResult(
RemoteLockscreenValidationResult result) {
switch (result.getResultCode()) {
case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
.getLockscreenCredential() != null) {
Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
ChooseLockPattern.SaveAndFinishWorker saveAndFinishWorker =
new ChooseLockPattern.SaveAndFinishWorker();
getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
.commit();
getFragmentManager().executePendingTransactions();
saveAndFinishWorker.setListener(this);
saveAndFinishWorker.start(
mLockPatternUtils,
/* requestGatekeeperPassword= */ true,
mRemoteLockscreenValidationFragment.getLockscreenCredential(),
/* currentCredential= */ null,
mEffectiveUserId);
} else {
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
/* timeoutMs= */ 0, mEffectiveUserId);
}
return;
case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
/* timeoutMs= */ 0, mEffectiveUserId);
break;
case RemoteLockscreenValidationResult.RESULT_LOCKOUT:
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
(int) result.getTimeoutMillis(), mEffectiveUserId);
break;
case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
case RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED:
onRemoteLockscreenValidationFailure(String.format(
"Cannot continue remote lockscreen validation. ResultCode=%d",
result.getResultCode()));
break;
}
updateRemoteLockscreenValidationViews();
mRemoteLockscreenValidationFragment.clearLockscreenCredential();
}
@Override
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
int effectiveUserId, boolean newResult) {
@@ -632,5 +725,22 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
appearing, interpolator, finishListener);
}
}
/**
* Callback for when the current device's lockscreen to the guess used for
* remote lockscreen validation.
*/
@Override
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
Log.i(TAG, "Device lockscreen has been set to remote device's lockscreen.");
mRemoteLockscreenValidationFragment.clearLockscreenCredential();
Intent result = new Intent();
if (mRemoteValidation && containsGatekeeperPasswordHandle(resultData)) {
result.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGatekeeperPasswordHandle(resultData));
}
mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
/* timeoutMs= */ 0, mEffectiveUserId);
}
}
}

View File

@@ -33,6 +33,8 @@ 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;
import com.google.android.setupdesign.util.ContentStyler;
import com.google.android.setupdesign.util.ThemeHelper;
/**
* An activity that asks the user to contact their admin to get assistance with forgotten password.
@@ -65,6 +67,11 @@ public class ForgotPasswordActivity extends Activity {
.build()
);
if (ThemeHelper.shouldApplyMaterialYouStyle(this)) {
ContentStyler.applyBodyPartnerCustomizationStyle(
layout.findViewById(R.id.forgot_password_text));
}
layout.setHeaderText(devicePolicyManager.getResources().getString(
FORGOT_PASSWORD_TITLE, () -> getString(R.string.forgot_password_title)));

View File

@@ -0,0 +1,190 @@
/*
* Copyright (C) 2023 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.RemoteLockscreenValidationResult;
import android.os.Bundle;
import android.os.Handler;
import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
import android.util.Log;
import androidx.fragment.app.Fragment;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.security.SecureBox;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
/**
* A fragment used to hold state for remote lockscreen validation.
* If the original listener is ever re-created, the new listener must be set again using
* {@link #setListener} so that the validation result does not get handled by the old listener.
*/
public class RemoteLockscreenValidationFragment extends Fragment {
private static final String TAG = RemoteLockscreenValidationFragment.class.getSimpleName();
private Listener mListener;
private Handler mHandler;
private boolean mIsInProgress;
private RemoteLockscreenValidationResult mResult;
private String mErrorMessage;
private LockscreenCredential mLockscreenCredential;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onDestroy() {
clearLockscreenCredential();
if (mResult != null && mErrorMessage != null) {
Log.w(TAG, "Unprocessed remote lockscreen validation result");
}
super.onDestroy();
}
/**
* @return {@code true} if remote lockscreen guess validation has started or
* the validation result has not yet been handled.
*/
public boolean isRemoteValidationInProgress() {
return mIsInProgress;
}
/**
* Sets the listener and handler that will handle the result of remote lockscreen validation.
* Unprocessed results or failures will be handled after the listener is set.
*/
public void setListener(Listener listener, Handler handler) {
if (mListener == listener) {
return;
}
mListener = listener;
mHandler = handler;
if (mResult != null) {
handleResult();
} else if (mErrorMessage != null) {
handleFailure();
}
}
/**
* @return {@link LockscreenCredential} if it was cached in {@link #validateLockscreenGuess}.
*/
public LockscreenCredential getLockscreenCredential() {
return mLockscreenCredential;
}
/**
* Clears the {@link LockscreenCredential} if it was cached in {@link #validateLockscreenGuess}.
*/
public void clearLockscreenCredential() {
if (mLockscreenCredential != null) {
mLockscreenCredential.zeroize();
mLockscreenCredential = null;
}
}
/**
* Validates the lockscreen guess on the remote device.
* @param remoteLockscreenValidationClient the client that should be used to send the guess to
* for validation
* @param guess the {@link LockscreenCredential} guess that the user entered
* @param encryptionKey the key that should be used to encrypt the guess before validation
* @param shouldCacheGuess whether to cache to guess so it can be used to set the current
* device's lockscreen after validation succeeds.
*/
public void validateLockscreenGuess(
RemoteLockscreenValidationClient remoteLockscreenValidationClient,
LockscreenCredential guess, byte[] encryptionKey, boolean shouldCacheGuess) {
if (shouldCacheGuess) {
mLockscreenCredential = guess;
}
remoteLockscreenValidationClient.validateLockscreenGuess(
encryptDeviceCredentialGuess(guess.getCredential(), encryptionKey),
new IRemoteLockscreenValidationCallback.Stub() {
@Override
public void onSuccess(RemoteLockscreenValidationResult result) {
mResult = result;
handleResult();
}
@Override
public void onFailure(String message) {
mErrorMessage = message;
handleFailure();
}
});
mIsInProgress = true;
}
private byte[] encryptDeviceCredentialGuess(byte[] guess, byte[] encryptionKey) {
try {
PublicKey publicKey = SecureBox.decodePublicKey(encryptionKey);
return SecureBox.encrypt(
publicKey,
/* sharedSecret= */ null,
LockPatternUtils.ENCRYPTED_REMOTE_CREDENTIALS_HEADER,
guess);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
Log.w(TAG, "Error encrypting device credential guess. Returning empty byte[].", e);
return new byte[0];
}
}
private void handleResult() {
if (mHandler != null) {
mHandler.post(()-> {
if (mListener == null || mResult == null) {
return;
}
mIsInProgress = false;
mListener.onRemoteLockscreenValidationResult(mResult);
mResult = null;
});
}
}
private void handleFailure() {
if (mHandler != null) {
mHandler.post(()-> {
if (mListener == null || mErrorMessage == null) {
return;
}
mIsInProgress = false;
mListener.onRemoteLockscreenValidationFailure(
String.format("Remote lockscreen validation failed: %s", mErrorMessage));
mErrorMessage = null;
});
}
}
interface Listener {
void onRemoteLockscreenValidationResult(RemoteLockscreenValidationResult result);
void onRemoteLockscreenValidationFailure(String message);
}
}

View File

@@ -16,12 +16,10 @@
package com.android.settings.password;
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;
@@ -68,21 +66,12 @@ abstract class SaveChosenLockWorkerBase extends Fragment {
}
}
protected void prepare(LockPatternUtils utils, boolean credentialRequired,
boolean requestGatekeeperPassword, int userId) {
protected void prepare(LockPatternUtils utils, boolean requestGatekeeperPassword, int userId) {
mUtils = utils;
mUserId = userId;
mRequestGatekeeperPassword = requestGatekeeperPassword;
// This will be a no-op for non managed profiles.
mWasSecureBefore = mUtils.isSecure(mUserId);
Context context = getContext();
// If context is null, we're being invoked to change the setCredentialRequiredToDecrypt,
// and we made sure that this is the primary user already.
if (context == null || UserManager.get(context).getUserInfo(mUserId).isPrimary()) {
mUtils.setCredentialRequiredToDecrypt(credentialRequired);
}
mFinished = false;
mResultData = null;
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.password;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
import android.app.RemoteServiceException.MissingRequestPasswordComplexityPermissionException;
@@ -40,10 +41,10 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.SetupEncryptionInterstitial;
import com.android.settings.SetupWizardUtils;
import com.android.settings.utils.SettingsDividerItemDecoration;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.GlifPreferenceLayout;
import com.google.android.setupdesign.util.ThemeHelper;
@@ -188,14 +189,14 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
final String key = preference.getKey();
if (KEY_UNLOCK_SET_DO_LATER.equals(key)) {
// show warning.
final Intent intent = getActivity().getIntent();
SetupSkipDialog dialog = SetupSkipDialog.newInstance(
getActivity().getIntent()
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false),
/* isPatternMode= */ false,
/* isAlphaMode= */ false,
CREDENTIAL_TYPE_NONE,
intent.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false),
/* forFingerprint= */ false,
/* forFace= */ false,
/* forBiometrics= */ false
/* forBiometrics= */ false,
WizardManagerHelper.isAnySetupWizard(intent)
);
dialog.show(getFragmentManager());
return true;
@@ -219,15 +220,6 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
return intent;
}
@Override
protected Intent getEncryptionInterstitialIntent(Context context, int quality,
boolean required, Intent unlockMethodIntent) {
Intent intent = SetupEncryptionInterstitial.createStartIntent(context, quality,
required, unlockMethodIntent);
SetupWizardUtils.copySetupExtras(getActivity().getIntent(), intent);
return intent;
}
@Override
protected Intent getBiometricEnrollIntent(Context context) {
final Intent intent = super.getBiometricEnrollIntent(context);

View File

@@ -16,13 +16,15 @@
package com.android.settings.password;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import androidx.annotation.Nullable;
@@ -32,6 +34,8 @@ import com.android.settings.R;
import com.android.settings.SetupRedactionInterstitial;
import com.android.settings.password.ChooseLockTypeDialogFragment.OnLockTypeSelectedListener;
import com.google.android.setupcompat.util.WizardManagerHelper;
/**
* Setup Wizard's version of ChooseLockPassword screen. It inherits the logic and basic structure
* from ChooseLockPassword class, and should remain similar to that behaviorally. This class should
@@ -114,16 +118,15 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
final boolean forBiometrics = intent
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
final SetupSkipDialog dialog = SetupSkipDialog.newInstance(
mIsAlphaMode ? CREDENTIAL_TYPE_PASSWORD : CREDENTIAL_TYPE_PIN,
frpSupported,
/* isPatternMode= */ false,
mIsAlphaMode,
forFingerprint,
forFace,
forBiometrics);
forBiometrics,
WizardManagerHelper.isAnySetupWizard(intent));
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
ConfirmDeviceCredentialUtils.hideImeImmediately(
getActivity().getWindow().getDecorView());
dialog.show(getFragmentManager());
return;
@@ -172,6 +175,12 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
mOptionsButton.setVisibility(
mUiStage == Stage.Introduction ? View.VISIBLE : View.GONE);
}
// Visibility of auto pin confirm opt-in/out option should always be invisible.
if (mAutoPinConfirmOption != null) {
mAutoPinConfirmOption.setVisibility(View.GONE);
mAutoConfirmSecurityMessage.setVisibility(View.GONE);
}
}
}
}

View File

@@ -18,6 +18,8 @@ package com.android.settings.password;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -32,6 +34,8 @@ import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.SetupRedactionInterstitial;
import com.google.android.setupcompat.util.WizardManagerHelper;
/**
* Setup Wizard's version of ChooseLockPattern screen. It inherits the logic and basic structure
* from ChooseLockPattern class, and should remain similar to that behaviorally. This class should
@@ -101,14 +105,13 @@ public class SetupChooseLockPattern extends ChooseLockPattern {
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
final boolean forBiometrics = intent
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
final SetupSkipDialog dialog = SetupSkipDialog.newInstance(
CREDENTIAL_TYPE_PATTERN,
frpSupported,
/* isPatternMode= */ true,
/* isAlphaMode= */ false,
forFingerprint,
forFace,
forBiometrics);
forBiometrics,
WizardManagerHelper.isAnySetupWizard(intent));
dialog.show(getFragmentManager());
return;
}

View File

@@ -16,6 +16,14 @@
package com.android.settings.password;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_SUW;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -29,7 +37,10 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.fragment.app.FragmentManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
public class SetupSkipDialog extends InstrumentedDialogFragment
@@ -38,24 +49,23 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
public static final String EXTRA_FRP_SUPPORTED = ":settings:frp_supported";
private static final String ARG_FRP_SUPPORTED = "frp_supported";
// The key indicates type of lock screen is pattern setup.
private static final String ARG_LOCK_TYPE_PATTERN = "lock_type_pattern";
// The key indicates type of screen lock credential types(PIN/Pattern/Password)
private static final String ARG_LOCK_CREDENTIAL_TYPE = "lock_credential_type";
// The key indicates type of lock screen setup is alphanumeric for password setup.
private static final String ARG_LOCK_TYPE_ALPHANUMERIC = "lock_type_alphanumeric";
private static final String TAG_SKIP_DIALOG = "skip_dialog";
public static final int RESULT_SKIP = Activity.RESULT_FIRST_USER + 10;
public static SetupSkipDialog newInstance(boolean isFrpSupported, boolean isPatternMode,
boolean isAlphanumericMode, boolean forFingerprint, boolean forFace,
boolean forBiometrics) {
public static SetupSkipDialog newInstance(@LockPatternUtils.CredentialType int credentialType,
boolean isFrpSupported, boolean forFingerprint, boolean forFace,
boolean forBiometrics, boolean isSuw) {
SetupSkipDialog dialog = new SetupSkipDialog();
Bundle args = new Bundle();
args.putInt(ARG_LOCK_CREDENTIAL_TYPE, credentialType);
args.putBoolean(ARG_FRP_SUPPORTED, isFrpSupported);
args.putBoolean(ARG_LOCK_TYPE_PATTERN, isPatternMode);
args.putBoolean(ARG_LOCK_TYPE_ALPHANUMERIC, isAlphanumericMode);
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, forFingerprint);
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, forFace);
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, forBiometrics);
args.putBoolean(EXTRA_KEY_FOR_FINGERPRINT, forFingerprint);
args.putBoolean(EXTRA_KEY_FOR_FACE, forFace);
args.putBoolean(EXTRA_KEY_FOR_BIOMETRICS, forBiometrics);
args.putBoolean(EXTRA_KEY_IS_SUW, isSuw);
dialog.setArguments(args);
return dialog;
}
@@ -70,59 +80,59 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
return onCreateDialogBuilder().create();
}
private AlertDialog.Builder getBiometricsBuilder(
@LockPatternUtils.CredentialType int credentialType, boolean isSuw, boolean hasFace,
boolean hasFingerprint) {
final boolean isFaceSupported = hasFace && (!isSuw || BiometricUtils.isFaceSupportedInSuw(
getContext()));
final int msgResId;
final int screenLockResId;
switch (credentialType) {
case CREDENTIAL_TYPE_PATTERN:
screenLockResId = R.string.unlock_set_unlock_pattern_title;
msgResId = getPatternSkipMessageRes(hasFace && isFaceSupported, hasFingerprint);
break;
case CREDENTIAL_TYPE_PASSWORD:
screenLockResId = R.string.unlock_set_unlock_password_title;
msgResId = getPasswordSkipMessageRes(hasFace && isFaceSupported, hasFingerprint);
break;
case CREDENTIAL_TYPE_PIN:
default:
screenLockResId = R.string.unlock_set_unlock_pin_title;
msgResId = getPinSkipMessageRes(hasFace && isFaceSupported, hasFingerprint);
break;
}
return new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.skip_lock_screen_dialog_button_label, this)
.setNegativeButton(R.string.cancel_lock_screen_dialog_button_label, this)
.setTitle(getSkipSetupTitle(screenLockResId, hasFingerprint,
hasFace && isFaceSupported))
.setMessage(msgResId);
}
@NonNull
public AlertDialog.Builder onCreateDialogBuilder() {
Bundle args = getArguments();
final boolean forFace =
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE);
final boolean forFingerprint =
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT);
final boolean forBiometrics =
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS);
final boolean isSuw = args.getBoolean(EXTRA_KEY_IS_SUW);
final boolean forBiometrics = args.getBoolean(EXTRA_KEY_FOR_BIOMETRICS);
final boolean forFace = args.getBoolean(EXTRA_KEY_FOR_FACE);
final boolean forFingerprint = args.getBoolean(EXTRA_KEY_FOR_FINGERPRINT);
@LockPatternUtils.CredentialType
final int credentialType = args.getInt(ARG_LOCK_CREDENTIAL_TYPE);
if (forFace || forFingerprint || forBiometrics) {
final boolean hasFace = forFace || forBiometrics;
final boolean hasFingerprint = forFingerprint || forBiometrics;
final int titleId;
final int msgResId;
if (args.getBoolean(ARG_LOCK_TYPE_PATTERN)) {
titleId = getPatternSkipTitleRes(hasFace, hasFingerprint);
msgResId = getPatternSkipMessageRes(hasFace, hasFingerprint);
} else if (args.getBoolean(ARG_LOCK_TYPE_ALPHANUMERIC)) {
titleId = getPasswordSkipTitleRes(hasFace, hasFingerprint);
msgResId = getPasswordSkipMessageRes(hasFace, hasFingerprint);
} else {
titleId = getPinSkipTitleRes(hasFace, hasFingerprint);
msgResId = getPinSkipMessageRes(hasFace, hasFingerprint);
}
return new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.skip_lock_screen_dialog_button_label, this)
.setNegativeButton(R.string.cancel_lock_screen_dialog_button_label, this)
.setTitle(titleId)
.setMessage(msgResId);
} else {
return new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.skip_anyway_button_label, this)
.setNegativeButton(R.string.go_back_button_label, this)
.setTitle(R.string.lock_screen_intro_skip_title)
.setMessage(args.getBoolean(ARG_FRP_SUPPORTED) ?
R.string.lock_screen_intro_skip_dialog_text_frp :
R.string.lock_screen_intro_skip_dialog_text);
final boolean hasFace = Utils.hasFaceHardware(getContext());
final boolean hasFingerprint = Utils.hasFingerprintHardware(getContext());
return getBiometricsBuilder(credentialType, isSuw, hasFace, hasFingerprint);
}
}
@StringRes
private int getPatternSkipTitleRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
return R.string.lock_screen_pattern_skip_biometrics_title;
} else if (hasFace) {
return R.string.lock_screen_pattern_skip_face_title;
} else if (hasFingerprint) {
return R.string.lock_screen_pattern_skip_fingerprint_title;
} else {
return R.string.lock_screen_pattern_skip_title;
}
return new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.skip_anyway_button_label, this)
.setNegativeButton(R.string.go_back_button_label, this)
.setTitle(R.string.lock_screen_intro_skip_title)
.setMessage(args.getBoolean(ARG_FRP_SUPPORTED) ?
R.string.lock_screen_intro_skip_dialog_text_frp :
R.string.lock_screen_intro_skip_dialog_text);
}
@StringRes
@@ -138,19 +148,6 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
}
}
@StringRes
private int getPasswordSkipTitleRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
return R.string.lock_screen_password_skip_biometrics_title;
} else if (hasFace) {
return R.string.lock_screen_password_skip_face_title;
} else if (hasFingerprint) {
return R.string.lock_screen_password_skip_fingerprint_title;
} else {
return R.string.lock_screen_password_skip_title;
}
}
@StringRes
private int getPasswordSkipMessageRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
@@ -164,19 +161,6 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
}
}
@StringRes
private int getPinSkipTitleRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
return R.string.lock_screen_pin_skip_biometrics_title;
} else if (hasFace) {
return R.string.lock_screen_pin_skip_face_title;
} else if (hasFingerprint) {
return R.string.lock_screen_pin_skip_fingerprint_title;
} else {
return R.string.lock_screen_pin_skip_title;
}
}
@StringRes
private int getPinSkipMessageRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
@@ -190,6 +174,13 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
}
}
private String getSkipSetupTitle(int screenTypeResId, boolean hasFingerprint,
boolean hasFace) {
return getString(R.string.lock_screen_skip_setup_title,
BiometricUtils.getCombinedScreenLockOptions(getContext(),
getString(screenTypeResId), hasFingerprint, hasFace));
}
@Override
public void onClick(DialogInterface dialog, int button) {
Activity activity = getActivity();