Support remote device credentials validation in UI.
Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.password Test: Manual Bug: 258505917 Change-Id: Ifb9f15728eb8396b34c844d28f71a8e6e1aad837
This commit is contained in:
@@ -22,7 +22,9 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.StartLockscreenValidationRequest;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.os.UserManager;
|
||||
@@ -41,6 +43,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";
|
||||
@@ -132,6 +136,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;
|
||||
@@ -139,6 +144,9 @@ public final class ChooseLockSettingsHelper {
|
||||
private int mUserId;
|
||||
private boolean mAllowAnyUserId;
|
||||
private boolean mForceVerifyPath;
|
||||
private boolean mRemoteLockscreenValidation;
|
||||
@Nullable private StartLockscreenValidationRequest mStartLockscreenValidationRequest;
|
||||
@Nullable private ComponentName mRemoteLockscreenValidationServiceComponent;
|
||||
boolean mRequestGatekeeperPasswordHandle;
|
||||
|
||||
public Builder(@NonNull Activity activity) {
|
||||
@@ -191,6 +199,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:
|
||||
@@ -253,6 +270,42 @@ public final class ChooseLockSettingsHelper {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isRemoteLockscreenValidation if true, remote device validation flow will be
|
||||
* started. {@link #setStartLockscreenValidationRequest} and
|
||||
* {@link #setRemoteLockscreenValidationServiceComponent}
|
||||
* must also be used to set the required data.
|
||||
*/
|
||||
@NonNull public Builder setRemoteLockscreenValidation(
|
||||
boolean isRemoteLockscreenValidation) {
|
||||
mRemoteLockscreenValidation = isRemoteLockscreenValidation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startLockScreenValidationRequest 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 setStartLockscreenValidationRequest(
|
||||
StartLockscreenValidationRequest startLockScreenValidationRequest) {
|
||||
mStartLockscreenValidationRequest = startLockScreenValidationRequest;
|
||||
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
|
||||
@@ -315,49 +368,41 @@ 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.mStartLockscreenValidationRequest,
|
||||
mBuilder.mRemoteLockscreenValidationServiceComponent, mBuilder.mAllowAnyUserId,
|
||||
mBuilder.mForegroundOnly, mBuilder.mRequestGatekeeperPasswordHandle);
|
||||
}
|
||||
|
||||
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 StartLockscreenValidationRequest startLockScreenValidationRequest,
|
||||
@Nullable ComponentName remoteLockscreenValidationServiceComponent,
|
||||
boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) {
|
||||
Optional<Class<?>> activityClass = determineAppropriateActivityClass(
|
||||
returnCredentials, forceVerifyPath, userId, startLockScreenValidationRequest);
|
||||
if (activityClass.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return launched;
|
||||
|
||||
return launchConfirmationActivity(request, title, header, description, activityClass.get(),
|
||||
returnCredentials, external, forceVerifyPath, userId, alternateButton,
|
||||
checkboxLabel, remoteLockscreenValidation, startLockScreenValidationRequest,
|
||||
remoteLockscreenValidationServiceComponent, allowAnyUser, foregroundOnly,
|
||||
requestGatekeeperPasswordHandle);
|
||||
}
|
||||
|
||||
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 StartLockscreenValidationRequest startLockScreenValidationRequest,
|
||||
@Nullable ComponentName remoteLockscreenValidationServiceComponent,
|
||||
boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
|
||||
@@ -367,10 +412,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_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||
startLockScreenValidationRequest);
|
||||
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,
|
||||
@@ -405,6 +456,58 @@ public final class ChooseLockSettingsHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 StartLockscreenValidationRequest startLockscreenValidationRequest) {
|
||||
int lockType;
|
||||
if (startLockscreenValidationRequest != null) {
|
||||
lockType = startLockscreenValidationRequest.getLockscreenUiType();
|
||||
} 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) {
|
||||
|
@@ -28,8 +28,10 @@ import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.StartLockscreenValidationRequest;
|
||||
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;
|
||||
@@ -97,7 +99,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
private boolean mCheckDevicePolicyManager;
|
||||
|
||||
private String mTitle;
|
||||
private String mDetails;
|
||||
private CharSequence mDetails;
|
||||
private int mUserId;
|
||||
private int mCredentialMode;
|
||||
private boolean mGoingToBackground;
|
||||
@@ -178,10 +180,12 @@ 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());
|
||||
|
||||
mUserId = UserHandle.myUserId();
|
||||
if (isInternalActivity()) {
|
||||
@@ -230,6 +234,28 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
.setExternal(true)
|
||||
.setUserId(LockPatternUtils.USER_FRP)
|
||||
.show();
|
||||
} else if (remoteValidation) {
|
||||
StartLockscreenValidationRequest startLockScreenValidationRequest =
|
||||
intent.getParcelableExtra(
|
||||
KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||
StartLockscreenValidationRequest.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)
|
||||
.setStartLockscreenValidationRequest(startLockScreenValidationRequest)
|
||||
.setRemoteLockscreenValidationServiceComponent(
|
||||
remoteLockscreenValidationServiceComponent)
|
||||
.setHeader(mTitle) // Show the title in the header location
|
||||
.setDescription(mDetails)
|
||||
.setCheckboxLabel(checkboxLabel)
|
||||
.setAlternateButton(alternateButton)
|
||||
.setExternal(true)
|
||||
.show();
|
||||
} else if (isEffectiveUserManagedProfile && isInternalActivity()) {
|
||||
mCredentialMode = CREDENTIAL_MANAGED;
|
||||
if (isBiometricAllowed(effectiveUserId, mUserId)) {
|
||||
|
@@ -24,8 +24,11 @@ import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Dialog;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.RemoteLockscreenValidationResult;
|
||||
import android.app.StartLockscreenValidationRequest;
|
||||
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;
|
||||
@@ -35,11 +38,15 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
|
||||
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;
|
||||
@@ -47,10 +54,16 @@ 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.security.SecureBox;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
|
||||
*/
|
||||
@@ -66,6 +79,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;
|
||||
@@ -77,6 +92,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
protected boolean mReturnCredentials = false;
|
||||
protected boolean mReturnGatekeeperPassword = false;
|
||||
protected boolean mForceVerifyPath = false;
|
||||
protected CheckBox mCheckBox;
|
||||
protected Button mCancelButton;
|
||||
/** Button allowing managed profile password reset, null when is not shown. */
|
||||
@Nullable protected Button mForgotButton;
|
||||
@@ -88,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 StartLockscreenValidationRequest mStartLockscreenValidationRequest;
|
||||
/** Credential saved so the credential can be set for device if remote validation passes */
|
||||
@Nullable protected LockscreenCredential mDeviceCredentialGuess;
|
||||
@Nullable protected RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return (getActivity() instanceof ConfirmLockPassword.InternalActivity)
|
||||
@@ -100,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);
|
||||
@@ -110,6 +131,41 @@ 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 {
|
||||
Log.e(TAG, "Remote device credential validation not enabled.");
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
if (mRemoteValidation) {
|
||||
mStartLockscreenValidationRequest = intent.getParcelableExtra(
|
||||
KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
|
||||
StartLockscreenValidationRequest.class);
|
||||
if (mStartLockscreenValidationRequest == null
|
||||
|| mStartLockscreenValidationRequest.getRemainingAttempts() == 0) {
|
||||
Log.e(TAG, "StartLockscreenValidationRequest is null or "
|
||||
+ "no more attempts for remote lockscreen validation.");
|
||||
getActivity().finish();
|
||||
}
|
||||
|
||||
ComponentName remoteLockscreenValidationServiceComponent =
|
||||
intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName.class);
|
||||
if (remoteLockscreenValidationServiceComponent == null) {
|
||||
Log.e(TAG, "RemoteLockscreenValidationService ComponentName is null");
|
||||
getActivity().finish();
|
||||
}
|
||||
mRemoteLockscreenValidationClient = RemoteLockscreenValidationClient
|
||||
.create(getContext(), remoteLockscreenValidationServiceComponent);
|
||||
if (!mRemoteLockscreenValidationClient.isServiceAvailable()) {
|
||||
Log.e(TAG, String.format("RemoteLockscreenValidationService at %s is not available",
|
||||
remoteLockscreenValidationServiceComponent.getClassName()));
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
|
||||
// Only take this argument into account if it belongs to the current profile.
|
||||
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras(),
|
||||
isInternalActivity());
|
||||
@@ -126,13 +182,14 @@ 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) {
|
||||
@@ -141,6 +198,11 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
getActivity().finish();
|
||||
});
|
||||
setupForgotButtonIfManagedProfile(view);
|
||||
|
||||
mCheckBox = view.findViewById(R.id.checkbox);
|
||||
if (mCheckBox != null && mRemoteValidation) {
|
||||
mCheckBox.setVisibility(View.VISIBLE);
|
||||
}
|
||||
setupEmergencyCallButtonIfManagedSubscription(view);
|
||||
}
|
||||
|
||||
@@ -232,8 +294,21 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (mRemoteLockscreenValidationClient != null) {
|
||||
mRemoteLockscreenValidationClient.disconnect();
|
||||
}
|
||||
if (mDeviceCredentialGuess != null) {
|
||||
mDeviceCredentialGuess.zeroize();
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
protected abstract void authenticationSucceeded();
|
||||
|
||||
protected abstract void onRemoteDeviceCredentialValidationResult(
|
||||
RemoteLockscreenValidationResult result);
|
||||
|
||||
public void prepareEnterAnimation() {
|
||||
}
|
||||
@@ -335,6 +410,46 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
}
|
||||
}
|
||||
|
||||
protected void validateGuess(LockscreenCredential credentialGuess) {
|
||||
if (mCheckBox.isChecked()) {
|
||||
// Keep credential in memory since user wants to set guess as screen lock.
|
||||
mDeviceCredentialGuess = credentialGuess;
|
||||
} else if (mDeviceCredentialGuess != null) {
|
||||
mDeviceCredentialGuess.zeroize();
|
||||
}
|
||||
|
||||
mRemoteLockscreenValidationClient.validateLockscreenGuess(
|
||||
encryptDeviceCredentialGuess(credentialGuess.getCredential()),
|
||||
new IRemoteLockscreenValidationCallback.Stub() {
|
||||
@Override
|
||||
public void onSuccess(RemoteLockscreenValidationResult result) {
|
||||
mHandler.post(()->onRemoteDeviceCredentialValidationResult(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String message) {
|
||||
Log.e(TAG, "A failure occurred while trying "
|
||||
+ "to validate lockscreen guess: " + message);
|
||||
mHandler.post(()->getActivity().finish());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private byte[] encryptDeviceCredentialGuess(byte[] guess) {
|
||||
try {
|
||||
byte[] encodedPublicKey = mStartLockscreenValidationRequest.getSourcePublicKey();
|
||||
PublicKey publicKey = SecureBox.decodePublicKey(encodedPublicKey);
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void onShowError();
|
||||
|
||||
protected void showError(int msg, long timeout) {
|
||||
|
@@ -27,6 +27,8 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROF
|
||||
import static android.app.admin.DevicePolicyResources.UNDEFINED;
|
||||
|
||||
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 +44,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;
|
||||
@@ -114,13 +117,13 @@ 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 {
|
||||
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
||||
private ImeAwareEditText mPasswordEntry;
|
||||
private TextViewInputDisabler mPasswordEntryInputDisabler;
|
||||
@@ -134,6 +137,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||
private boolean mIsManagedProfile;
|
||||
private GlifLayout mGlifLayout;
|
||||
private CharSequence mCheckBoxLabel;
|
||||
|
||||
// required constructor for fragments
|
||||
public ConfirmLockPasswordFragment() {
|
||||
@@ -160,11 +164,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 = mStartLockscreenValidationRequest.getLockscreenUiType()
|
||||
== 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 +199,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 +240,19 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (mForgotButton != null) {
|
||||
mForgotButton.setText(mIsAlpha
|
||||
? R.string.lockpassword_forgot_password
|
||||
@@ -237,7 +263,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 +281,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 +304,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 +317,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;
|
||||
@@ -392,6 +438,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), 0 /*flags*/);
|
||||
} else {
|
||||
mPasswordEntry.scheduleShowSoftInput();
|
||||
mPasswordEntry.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,12 +460,18 @@ 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);
|
||||
mGlifLayout.setProgressBarShown(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent();
|
||||
// TODO(b/161956762): Sanitize this
|
||||
if (mReturnGatekeeperPassword) {
|
||||
@@ -546,6 +599,44 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemoteDeviceCredentialValidationResult(
|
||||
RemoteLockscreenValidationResult result) {
|
||||
switch (result.getResultCode()) {
|
||||
case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
|
||||
if (mCheckBox.isChecked()) {
|
||||
ChooseLockPassword.SaveAndFinishWorker saveAndFinishWorker =
|
||||
new ChooseLockPassword.SaveAndFinishWorker();
|
||||
Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
|
||||
getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
|
||||
.commit();
|
||||
getFragmentManager().executePendingTransactions();
|
||||
saveAndFinishWorker.setListener(this);
|
||||
saveAndFinishWorker.start(
|
||||
mLockPatternUtils,
|
||||
/* requestGatekeeperPassword= */ false,
|
||||
mDeviceCredentialGuess,
|
||||
/* currentCredential= */ null,
|
||||
mEffectiveUserId);
|
||||
return;
|
||||
}
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
break;
|
||||
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:
|
||||
getActivity().finish();
|
||||
}
|
||||
mGlifLayout.setProgressBarShown(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
||||
int effectiveUserId, boolean newResult) {
|
||||
@@ -601,5 +692,19 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the device credential guess used for remote validation was set as the
|
||||
* current device's device credential.
|
||||
*/
|
||||
@Override
|
||||
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
|
||||
if (mDeviceCredentialGuess != null) {
|
||||
mDeviceCredentialGuess.zeroize();
|
||||
}
|
||||
mGlifLayout.setProgressBarShown(false);
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyResources.UNDEFINED;
|
||||
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 +35,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;
|
||||
@@ -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 {
|
||||
|
||||
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
||||
|
||||
@@ -105,6 +109,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
// caller-supplied text for various prompts
|
||||
private CharSequence mHeaderText;
|
||||
private CharSequence mDetailsText;
|
||||
private CharSequence mCheckBoxLabel;
|
||||
|
||||
private AppearAnimationUtils mAppearAnimationUtils;
|
||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||
@@ -148,6 +153,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 +180,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 +210,33 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (mForgotButton != null) {
|
||||
mForgotButton.setText(R.string.lockpassword_forgot_pattern);
|
||||
}
|
||||
@@ -271,6 +299,10 @@ 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) {
|
||||
@@ -335,11 +367,11 @@ 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;
|
||||
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);
|
||||
mGlifLayout.setProgressBarShown(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(b/161956762): Sanitize this
|
||||
Intent intent = new Intent();
|
||||
if (mReturnGatekeeperPassword) {
|
||||
@@ -563,6 +612,44 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemoteDeviceCredentialValidationResult(
|
||||
RemoteLockscreenValidationResult result) {
|
||||
switch (result.getResultCode()) {
|
||||
case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
|
||||
if (mCheckBox.isChecked()) {
|
||||
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= */ false,
|
||||
mDeviceCredentialGuess,
|
||||
/* currentCredential= */ null,
|
||||
mEffectiveUserId);
|
||||
return;
|
||||
}
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
break;
|
||||
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:
|
||||
getActivity().finish();
|
||||
}
|
||||
mGlifLayout.setProgressBarShown(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
||||
int effectiveUserId, boolean newResult) {
|
||||
@@ -632,5 +719,19 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
appearing, interpolator, finishListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the device credential guess used for remote validation was set as the
|
||||
* current device's device credential.
|
||||
*/
|
||||
@Override
|
||||
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
|
||||
if (mDeviceCredentialGuess != null) {
|
||||
mDeviceCredentialGuess.zeroize();
|
||||
}
|
||||
mGlifLayout.setProgressBarShown(false);
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user