Add RemoteLockscreenValidationFragment to help retain remote lockscreen am: 2eb8ed2488
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/22390454 Change-Id: I58487aef2bc252448119e48c7a2f2bedc3a710b9 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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,6 @@ 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.RemoteLockscreenValidationSession;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.ManagedSubscriptionsPolicy;
|
||||
@@ -38,7 +38,6 @@ 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;
|
||||
@@ -55,14 +54,11 @@ 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;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
/**
|
||||
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
|
||||
@@ -89,9 +85,13 @@ 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. */
|
||||
@@ -109,8 +109,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
protected BiometricManager mBiometricManager;
|
||||
@Nullable protected RemoteLockscreenValidationSession mRemoteLockscreenValidationSession;
|
||||
/** Credential saved so the credential can be set for device if remote validation passes */
|
||||
@Nullable protected LockscreenCredential mDeviceCredentialGuess;
|
||||
@Nullable protected RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
|
||||
protected RemoteLockscreenValidationFragment mRemoteLockscreenValidationFragment;
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return (getActivity() instanceof ConfirmLockPassword.InternalActivity)
|
||||
@@ -136,8 +136,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION)) {
|
||||
mRemoteValidation = true;
|
||||
} else {
|
||||
Log.e(TAG, "Remote device credential validation not enabled.");
|
||||
getActivity().finish();
|
||||
onRemoteLockscreenValidationFailure(
|
||||
"Remote lockscreen validation not enabled.");
|
||||
}
|
||||
}
|
||||
if (mRemoteValidation) {
|
||||
@@ -146,23 +146,31 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
RemoteLockscreenValidationSession.class);
|
||||
if (mRemoteLockscreenValidationSession == null
|
||||
|| mRemoteLockscreenValidationSession.getRemainingAttempts() == 0) {
|
||||
Log.e(TAG, "RemoteLockscreenValidationSession is null or "
|
||||
onRemoteLockscreenValidationFailure("RemoteLockscreenValidationSession 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();
|
||||
onRemoteLockscreenValidationFailure(
|
||||
"RemoteLockscreenValidationService ComponentName is null");
|
||||
}
|
||||
mRemoteLockscreenValidationClient = RemoteLockscreenValidationClient
|
||||
.create(getContext(), remoteLockscreenValidationServiceComponent);
|
||||
if (!mRemoteLockscreenValidationClient.isServiceAvailable()) {
|
||||
Log.e(TAG, String.format("RemoteLockscreenValidationService at %s is not available",
|
||||
onRemoteLockscreenValidationFailure(String.format(
|
||||
"RemoteLockscreenValidationService at %s is not available",
|
||||
remoteLockscreenValidationServiceComponent.getClassName()));
|
||||
getActivity().finish();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,8 +202,10 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
mCancelButton.setOnClickListener(v -> {
|
||||
if (hasAlternateButton) {
|
||||
getActivity().setResult(KeyguardManager.RESULT_ALTERNATE);
|
||||
}
|
||||
getActivity().finish();
|
||||
} else if (mRemoteValidation) {
|
||||
onRemoteLockscreenValidationFailure("Forgot lockscreen credential button pressed.");
|
||||
}
|
||||
});
|
||||
setupForgotButtonIfManagedProfile(view);
|
||||
|
||||
@@ -299,17 +309,11 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
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() {
|
||||
}
|
||||
|
||||
@@ -411,43 +415,33 @@ 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();
|
||||
mRemoteLockscreenValidationFragment.validateLockscreenGuess(
|
||||
mRemoteLockscreenValidationClient, credentialGuess,
|
||||
mRemoteLockscreenValidationSession.getSourcePublicKey(), mCheckBox.isChecked());
|
||||
}
|
||||
|
||||
mRemoteLockscreenValidationClient.validateLockscreenGuess(
|
||||
encryptDeviceCredentialGuess(credentialGuess.getCredential()),
|
||||
new IRemoteLockscreenValidationCallback.Stub() {
|
||||
@Override
|
||||
public void onSuccess(RemoteLockscreenValidationResult result) {
|
||||
mHandler.post(()->onRemoteDeviceCredentialValidationResult(result));
|
||||
protected void updateRemoteLockscreenValidationViews() {
|
||||
if (!mRemoteValidation || mRemoteLockscreenValidationFragment == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String message) {
|
||||
Log.e(TAG, "A failure occurred while trying "
|
||||
+ "to validate lockscreen guess: " + message);
|
||||
mHandler.post(()->getActivity().finish());
|
||||
}
|
||||
});
|
||||
boolean enable = mRemoteLockscreenValidationFragment.isRemoteValidationInProgress();
|
||||
mGlifLayout.setProgressBarShown(enable);
|
||||
mCheckBox.setEnabled(!enable);
|
||||
mCancelButton.setEnabled(!enable);
|
||||
}
|
||||
|
||||
private byte[] encryptDeviceCredentialGuess(byte[] guess) {
|
||||
try {
|
||||
byte[] encodedPublicKey = mRemoteLockscreenValidationSession.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];
|
||||
/**
|
||||
* 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();
|
||||
|
@@ -71,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 {
|
||||
@@ -127,7 +125,8 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
|
||||
implements OnClickListener, OnEditorActionListener,
|
||||
CredentialCheckResultTracker.Listener, SaveChosenLockWorkerBase.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;
|
||||
@@ -140,7 +139,6 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
private AppearAnimationUtils mAppearAnimationUtils;
|
||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||
private boolean mIsManagedProfile;
|
||||
private GlifLayout mGlifLayout;
|
||||
private CharSequence mCheckBoxLabel;
|
||||
|
||||
// required constructor for fragments
|
||||
@@ -255,6 +253,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
? R.string.lockpassword_forgot_password
|
||||
: R.string.lockpassword_forgot_pin);
|
||||
}
|
||||
updateRemoteLockscreenValidationViews();
|
||||
}
|
||||
|
||||
if (mForgotButton != null) {
|
||||
@@ -405,6 +404,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
mCountdownTimer = null;
|
||||
}
|
||||
mCredentialCheckResultTracker.setListener(null);
|
||||
if (mRemoteLockscreenValidationFragment != null) {
|
||||
mRemoteLockscreenValidationFragment.setListener(null, /* handler= */ null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -426,6 +428,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
|
||||
}
|
||||
mCredentialCheckResultTracker.setListener(this);
|
||||
if (mRemoteLockscreenValidationFragment != null) {
|
||||
mRemoteLockscreenValidationFragment.setListener(this, mHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -436,13 +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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,7 +481,8 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
if (mRemoteValidation) {
|
||||
validateGuess(credential);
|
||||
mGlifLayout.setProgressBarShown(true);
|
||||
updateRemoteLockscreenValidationViews();
|
||||
updatePasswordEntry();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -604,14 +614,15 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemoteDeviceCredentialValidationResult(
|
||||
public void onRemoteLockscreenValidationResult(
|
||||
RemoteLockscreenValidationResult result) {
|
||||
switch (result.getResultCode()) {
|
||||
case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
|
||||
if (mCheckBox.isChecked()) {
|
||||
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();
|
||||
Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
|
||||
getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
|
||||
.commit();
|
||||
getFragmentManager().executePendingTransactions();
|
||||
@@ -619,14 +630,14 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
saveAndFinishWorker.start(
|
||||
mLockPatternUtils,
|
||||
/* requestGatekeeperPassword= */ true,
|
||||
mDeviceCredentialGuess,
|
||||
mRemoteLockscreenValidationFragment.getLockscreenCredential(),
|
||||
/* currentCredential= */ null,
|
||||
mEffectiveUserId);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
@@ -636,12 +647,15 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
(int) result.getTimeoutMillis(), mEffectiveUserId);
|
||||
break;
|
||||
case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
|
||||
getActivity().finish();
|
||||
break;
|
||||
case RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED:
|
||||
getActivity().finish();
|
||||
onRemoteLockscreenValidationFailure(String.format(
|
||||
"Cannot continue remote lockscreen validation. ResultCode=%d",
|
||||
result.getResultCode()));
|
||||
break;
|
||||
}
|
||||
mGlifLayout.setProgressBarShown(false);
|
||||
updateRemoteLockscreenValidationViews();
|
||||
updatePasswordEntry();
|
||||
mRemoteLockscreenValidationFragment.clearLockscreenCredential();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -701,21 +715,18 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the device credential guess used for remote validation was set as the
|
||||
* current device's device credential.
|
||||
* 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) {
|
||||
if (mDeviceCredentialGuess != null) {
|
||||
mDeviceCredentialGuess.zeroize();
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
mGlifLayout.setProgressBarShown(false);
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
}
|
||||
|
@@ -59,8 +59,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;
|
||||
@@ -97,7 +95,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
|
||||
implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener,
|
||||
SaveChosenLockWorkerBase.Listener {
|
||||
SaveChosenLockWorkerBase.Listener, RemoteLockscreenValidationFragment.Listener {
|
||||
|
||||
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
||||
|
||||
@@ -107,7 +105,6 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
private boolean mDisappearing = false;
|
||||
private CountDownTimer mCountdownTimer;
|
||||
|
||||
private GlifLayout mGlifLayout;
|
||||
private View mSudContent;
|
||||
|
||||
// caller-supplied text for various prompts
|
||||
@@ -239,6 +236,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
if (mCancelButton != null && TextUtils.isEmpty(mAlternateButtonText)) {
|
||||
mCancelButton.setText(R.string.lockpassword_forgot_pattern);
|
||||
}
|
||||
updateRemoteLockscreenValidationViews();
|
||||
}
|
||||
|
||||
if (mForgotButton != null) {
|
||||
@@ -259,6 +257,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
mCountdownTimer.cancel();
|
||||
}
|
||||
mCredentialCheckResultTracker.setListener(null);
|
||||
if (mRemoteLockscreenValidationFragment != null) {
|
||||
mRemoteLockscreenValidationFragment.setListener(null, /* handler= */ null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -281,6 +282,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
|
||||
@@ -502,7 +509,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
if (mRemoteValidation) {
|
||||
validateGuess(credential);
|
||||
mGlifLayout.setProgressBarShown(true);
|
||||
updateRemoteLockscreenValidationViews();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -617,11 +624,12 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemoteDeviceCredentialValidationResult(
|
||||
public void onRemoteLockscreenValidationResult(
|
||||
RemoteLockscreenValidationResult result) {
|
||||
switch (result.getResultCode()) {
|
||||
case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
|
||||
if (mCheckBox.isChecked()) {
|
||||
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();
|
||||
@@ -632,14 +640,14 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
saveAndFinishWorker.start(
|
||||
mLockPatternUtils,
|
||||
/* requestGatekeeperPassword= */ true,
|
||||
mDeviceCredentialGuess,
|
||||
mRemoteLockscreenValidationFragment.getLockscreenCredential(),
|
||||
/* currentCredential= */ null,
|
||||
mEffectiveUserId);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
@@ -649,12 +657,14 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
(int) result.getTimeoutMillis(), mEffectiveUserId);
|
||||
break;
|
||||
case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
|
||||
getActivity().finish();
|
||||
break;
|
||||
case RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED:
|
||||
getActivity().finish();
|
||||
onRemoteLockscreenValidationFailure(String.format(
|
||||
"Cannot continue remote lockscreen validation. ResultCode=%d",
|
||||
result.getResultCode()));
|
||||
break;
|
||||
}
|
||||
mGlifLayout.setProgressBarShown(false);
|
||||
updateRemoteLockscreenValidationViews();
|
||||
mRemoteLockscreenValidationFragment.clearLockscreenCredential();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -728,21 +738,18 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the device credential guess used for remote validation was set as the
|
||||
* current device's device credential.
|
||||
* Callback for when the current device's lockscreen to the guess used for
|
||||
* remote lockscreen validation.
|
||||
*/
|
||||
@Override
|
||||
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
|
||||
if (mDeviceCredentialGuess != null) {
|
||||
mDeviceCredentialGuess.zeroize();
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
mGlifLayout.setProgressBarShown(false);
|
||||
mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
|
||||
/* timeoutMs= */ 0, mEffectiveUserId);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -197,7 +197,7 @@ public class ConfirmLockPasswordTest {
|
||||
verify(mCredentialCheckResultTracker).setResult(
|
||||
eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isTrue();
|
||||
assertThat(fragment.mDeviceCredentialGuess).isNotNull();
|
||||
assertThat(fragment.mRemoteLockscreenValidationFragment.getLockscreenCredential()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -223,7 +223,7 @@ public class ConfirmLockPasswordTest {
|
||||
verify(mCredentialCheckResultTracker).setResult(
|
||||
eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||
assertThat(fragment.mDeviceCredentialGuess).isNull();
|
||||
assertThat(fragment.mRemoteLockscreenValidationFragment.getLockscreenCredential()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -177,7 +177,7 @@ public class ConfirmLockPatternTest {
|
||||
verify(mCredentialCheckResultTracker).setResult(
|
||||
eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isTrue();
|
||||
assertThat(fragment.mDeviceCredentialGuess).isNotNull();
|
||||
assertThat(fragment.mRemoteLockscreenValidationFragment.getLockscreenCredential()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -203,7 +203,7 @@ public class ConfirmLockPatternTest {
|
||||
verify(mCredentialCheckResultTracker).setResult(
|
||||
eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
|
||||
assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
|
||||
assertThat(fragment.mDeviceCredentialGuess).isNull();
|
||||
assertThat(fragment.mRemoteLockscreenValidationFragment.getLockscreenCredential()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user