Support remote device credentials validation in UI.

Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.password
Test: Manual
Bug: 258505917

Change-Id: Ifb9f15728eb8396b34c844d28f71a8e6e1aad837
diff --git a/Android.bp b/Android.bp
index dc7270e..ea2c983 100644
--- a/Android.bp
+++ b/Android.bp
@@ -87,6 +87,7 @@
         "fuelgauge-log-protos-lite",
         "fuelgauge-usage-state-protos-lite",
         "contextualcards",
+        "securebox",
         "settings-logtags",
         "statslog-settings",
         "zxing-core-1.7",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 846d246..fcdb3ff 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -88,6 +88,7 @@
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
     <uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />
     <uses-permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE" />
+    <uses-permission android:name="android.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE" />
     <uses-permission android:name="android.permission.OEM_UNLOCK_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
     <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
@@ -2370,6 +2371,17 @@
         <activity-alias android:name=".ConfirmDeviceCredentialActivity"
             android:targetActivity=".password.ConfirmDeviceCredentialActivity"
             android:exported="true" />
+        <!-- Activity alias for remote lockscreen validation. Enforces required permission -->
+        <activity-alias
+            android:name=".ConfirmRemoteDeviceCredentialActivity"
+            android:targetActivity=".password.ConfirmDeviceCredentialActivity"
+            android:permission="android.permission.CHECK_REMOTE_LOCKSCREEN"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.app.action.CONFIRM_REMOTE_DEVICE_CREDENTIAL"/>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity-alias>
 
         <!-- Note this must not be exported since it authenticates the given user -->
         <activity android:name=".password.ConfirmDeviceCredentialActivity$InternalActivity"
diff --git a/res/layout-land/confirm_lock_pattern_normal_base.xml b/res/layout-land/confirm_lock_pattern_normal_base.xml
index 9a5097e..18fb142 100644
--- a/res/layout-land/confirm_lock_pattern_normal_base.xml
+++ b/res/layout-land/confirm_lock_pattern_normal_base.xml
@@ -15,10 +15,12 @@
 -->
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:icon="@drawable/ic_lock">
+    android:icon="@drawable/ic_lock"
+    app:sudUseBottomProgressBar="true">
 
     <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
         android:id="@+id/topLayout"
@@ -60,6 +62,16 @@
                 android:layout_marginEnd="?attr/sudMarginEnd"
                 android:gravity="center_vertical"/>
 
+            <CheckBox
+                android:id="@+id/checkbox"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="?attr/sudMarginStart"
+                android:layout_marginEnd="?attr/sudMarginEnd"
+                android:layout_marginTop="12dp"
+                android:visibility="gone"
+                android:checked="true" />
+
             <Button
                 android:id="@+id/cancelButton"
                 style="@style/SudGlifButton.Secondary"
diff --git a/res/layout/confirm_lock_password_normal.xml b/res/layout/confirm_lock_password_normal.xml
index c2a7b5d..d4cc332 100644
--- a/res/layout/confirm_lock_password_normal.xml
+++ b/res/layout/confirm_lock_password_normal.xml
@@ -15,11 +15,13 @@
 -->
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:icon="@drawable/ic_lock"
-    android:importantForAutofill="noExcludeDescendants">
+    android:importantForAutofill="noExcludeDescendants"
+    app:sudUseBottomProgressBar="true">
 
     <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
         android:id="@+id/topLayout"
@@ -27,14 +29,6 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <TextView
-            android:id="@+id/sud_layout_description"
-            style="@style/SudDescription.Glif"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="?attr/sudMarginStart"
-            android:layout_marginEnd="?attr/sudMarginEnd" />
-
         <Space
             android:layout_width="match_parent"
             android:layout_height="0dp"
@@ -73,6 +67,17 @@
             android:layout_height="0dp"
             android:layout_weight="1" />
 
+        <CheckBox
+            android:id="@+id/checkbox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="?attr/sudMarginStart"
+            android:layout_marginEnd="?attr/sudMarginEnd"
+            android:layout_marginTop="12dp"
+            android:layout_gravity="center_horizontal"
+            android:visibility="gone"
+            android:checked="true" />
+
         <Button
             android:id="@+id/cancelButton"
             style="@style/SudGlifButton.Secondary"
diff --git a/res/layout/confirm_lock_pattern_normal_base.xml b/res/layout/confirm_lock_pattern_normal_base.xml
index 44d9ab7..51bab67 100644
--- a/res/layout/confirm_lock_pattern_normal_base.xml
+++ b/res/layout/confirm_lock_pattern_normal_base.xml
@@ -15,10 +15,12 @@
 -->
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:icon="@drawable/ic_lock">
+    android:icon="@drawable/ic_lock"
+    app:sudUseBottomProgressBar="true">
 
     <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
         android:id="@+id/topLayout"
@@ -61,6 +63,16 @@
                 android:layout_marginTop="12dp"
                 android:gravity="center_vertical"/>
 
+            <CheckBox
+                android:id="@+id/checkbox"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="?attr/sudMarginStart"
+                android:layout_marginEnd="?attr/sudMarginEnd"
+                android:layout_marginTop="12dp"
+                android:visibility="gone"
+                android:checked="true" />
+
             <Button
                 android:id="@+id/cancelButton"
                 style="@style/SudGlifButton.Secondary"
@@ -68,7 +80,6 @@
                 android:layout_height="wrap_content"
                 android:layout_marginStart="?attr/sudMarginStart"
                 android:layout_marginEnd="?attr/sudMarginEnd"
-                android:layout_marginBottom="80dp"
                 android:text="@string/cancel" />
 
             <Button
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a5271f9..4318f55 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3339,6 +3339,24 @@
     <!-- Header shown when the password needs to be solved because the device was factory reset. [CHAR LIMIT=100] -->
     <string name="lockpassword_confirm_your_password_header_frp">Verify password</string>
 
+    <!-- Header shown when prompted for remote device credential validation. [CHAR LIMIT=17] -->
+    <string name="lockpassword_remote_validation_header">Verify it\u0027s you</string>
+
+    <!-- Details shown when pattern is prompted for remote device credential validation. [CHAR LIMIT=100] -->
+    <string name="lockpassword_remote_validation_pattern_details">Enter your other device\u0027s pattern to securely transfer Google Accounts, settings, and more. Your pattern is encrypted.</string>
+    <!-- Details shown when PIN is prompted for remote device credential validation. [CHAR LIMIT=100] -->
+    <string name="lockpassword_remote_validation_pin_details">Enter your other device\u0027s PIN to securely transfer Google Accounts, settings, and more. Your PIN is encrypted.</string>
+    <!-- Details shown when password is prompted for remote device credential validation. [CHAR LIMIT=100] -->
+    <string name="lockpassword_remote_validation_password_details">Enter your other device\u0027s password to securely transfer Google Accounts, settings, and more. Your password is encrypted.</string>
+
+    <!-- Checkbox label to set pattern as new screen lock if remote device credential validation succeeds. [CHAR LIMIT=43] -->
+    <string name="lockpassword_remote_validation_set_pattern_as_screenlock">Also use pattern to unlock this device</string>
+    <!-- Checkbox label to set PIN as new screen lock if remote device credential validation succeeds. [CHAR LIMIT=43] -->
+    <string name="lockpassword_remote_validation_set_pin_as_screenlock">Also use PIN to unlock this device</string>
+    <!-- Checkbox label to set password as new screen lock if remote device credential validation succeeds. [CHAR LIMIT=43] -->
+    <string name="lockpassword_remote_validation_set_password_as_screenlock">Also use password to unlock this device</string>
+
+
     <!-- Security & location settings screen, change security method screen instruction if user
          enters incorrect PIN [CHAR LIMIT=30] -->
     <string name="lockpassword_invalid_pin">Wrong PIN</string>
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index 6ccf7be..a1cd09f 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -22,7 +22,9 @@
 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.google.android.setupcompat.util.WizardManagerHelper;
 
+import java.util.Optional;
+
 public final class ChooseLockSettingsHelper {
 
     private static final String TAG = "ChooseLockSettingsHelper";
@@ -132,6 +136,7 @@
         @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 @@
         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) {
@@ -192,6 +200,15 @@
         }
 
         /**
+         * @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:
          *                          {@link #EXTRA_KEY_PASSWORD},
@@ -254,6 +271,42 @@
         }
 
         /**
+         * @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
          * to create multiple Gatekeeper HATs containing distinct challenges via
@@ -315,49 +368,41 @@
         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 @@
         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 @@
         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) {
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index e56e86c..31d9c74 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -28,8 +28,10 @@
 
 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 @@
     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 @@
         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 @@
                     .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)) {
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index 7787ae1..7c3df61 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -24,8 +24,11 @@
 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.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.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 @@
             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 @@
     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 @@
     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 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 @@
         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 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 @@
             getActivity().finish();
         });
         setupForgotButtonIfManagedProfile(view);
+
+        mCheckBox = view.findViewById(R.id.checkbox);
+        if (mCheckBox != null && mRemoteValidation) {
+            mCheckBox.setVisibility(View.VISIBLE);
+        }
         setupEmergencyCallButtonIfManagedSubscription(view);
     }
 
@@ -232,8 +294,21 @@
         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 @@
         }
     }
 
+    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) {
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 48c9aa8..83dc85c 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -27,6 +27,8 @@
 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.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 @@
         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 @@
         private DisappearAnimationUtils mDisappearAnimationUtils;
         private boolean mIsManagedProfile;
         private GlifLayout mGlifLayout;
+        private CharSequence mCheckBoxLabel;
 
         // required constructor for fragments
         public ConfirmLockPasswordFragment() {
@@ -160,11 +164,19 @@
             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 @@
                 }
                 mGlifLayout.setHeaderText(headerMessage);
                 mGlifLayout.setDescriptionText(detailsMessage);
+                mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
             }
             int currentType = mPasswordEntry.getInputType();
             if (mIsAlpha) {
@@ -227,6 +240,19 @@
         @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 @@
         @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 @@
                 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 @@
                 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 @@
                     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 @@
                 mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), 0 /*flags*/);
             } else {
                 mPasswordEntry.scheduleShowSoftInput();
+                mPasswordEntry.requestFocus();
             }
         }
 
@@ -413,12 +460,18 @@
             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) {
@@ -547,6 +600,44 @@
         }
 
         @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) {
             onPasswordChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
@@ -601,5 +692,19 @@
             }
             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);
+        }
     }
 }
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index b4f0aa3..0013d7a 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -25,6 +25,8 @@
 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.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 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 @@
         // 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 @@
                         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 @@
                 //              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 @@
                         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 @@
             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 @@
                     } 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 @@
 
         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 @@
             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 @@
          * an existing lock pattern.
          */
         private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener
-                = new LockPatternView.OnPatternListener()  {
+                = new LockPatternView.OnPatternListener() {
 
             public void onPatternStart() {
                 mLockPatternView.removeCallbacks(mClearPatternRunnable);
@@ -453,6 +495,13 @@
                 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) {
@@ -564,6 +613,44 @@
         }
 
         @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) {
             onPatternChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
@@ -632,5 +719,19 @@
                         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);
+        }
     }
 }
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java
index 053bbe0..2777529 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java
@@ -1,5 +1,9 @@
 package com.android.settings.password;
 
+import static com.android.settings.password.TestUtils.COMPONENT_NAME;
+import static com.android.settings.password.TestUtils.VALID_REMAINING_ATTEMPTS;
+import static com.android.settings.password.TestUtils.createStartLockscreenValidationRequest;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -10,6 +14,8 @@
 import static org.mockito.Mockito.when;
 
 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;
@@ -160,6 +166,93 @@
                 startedIntent.getComponent());
     }
 
+    @Test
+    public void launchConfirmPassword_remoteValidation_passwordLockType() throws Exception {
+        Activity activity = Robolectric.setupActivity(Activity.class);
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        StartLockscreenValidationRequest request = createStartLockscreenValidationRequest(
+                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS);
+
+        ChooseLockSettingsHelper chooseLockSettingsHelper = getChooseLockSettingsHelper(
+                new ChooseLockSettingsHelper.Builder(activity)
+                        .setRemoteLockscreenValidation(true)
+                        .setStartLockscreenValidationRequest(request)
+                        .setRemoteLockscreenValidationServiceComponent(COMPONENT_NAME));
+        chooseLockSettingsHelper.launch();
+
+        Intent startedIntent = shadowActivity.getNextStartedActivity();
+        assertEquals(new ComponentName("com.android.settings",
+                ConfirmLockPassword.class.getName()), startedIntent.getComponent());
+        assertThat(startedIntent.getBooleanExtra(
+                ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, false)
+        ).isTrue();
+        assertThat(startedIntent.getParcelableExtra(
+                KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
+                StartLockscreenValidationRequest.class)
+        ).isEqualTo(request);
+        assertThat(startedIntent.getParcelableExtra(
+                Intent.EXTRA_COMPONENT_NAME, ComponentName.class)
+        ).isEqualTo(COMPONENT_NAME);
+    }
+
+    @Test
+    public void launchConfirmPassword_remoteValidation_pinLockType() throws Exception {
+        Activity activity = Robolectric.setupActivity(Activity.class);
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        StartLockscreenValidationRequest request = createStartLockscreenValidationRequest(
+                KeyguardManager.PIN, VALID_REMAINING_ATTEMPTS);
+
+        ChooseLockSettingsHelper chooseLockSettingsHelper = getChooseLockSettingsHelper(
+                new ChooseLockSettingsHelper.Builder(activity)
+                        .setRemoteLockscreenValidation(true)
+                        .setStartLockscreenValidationRequest(request)
+                        .setRemoteLockscreenValidationServiceComponent(COMPONENT_NAME));
+        chooseLockSettingsHelper.launch();
+
+        Intent startedIntent = shadowActivity.getNextStartedActivity();
+        assertEquals(new ComponentName("com.android.settings",
+                ConfirmLockPassword.class.getName()), startedIntent.getComponent());
+        assertThat(startedIntent.getBooleanExtra(
+                ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, false)
+        ).isTrue();
+        assertThat(startedIntent.getParcelableExtra(
+                KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
+                StartLockscreenValidationRequest.class)
+        ).isEqualTo(request);
+        assertThat(startedIntent.getParcelableExtra(
+                Intent.EXTRA_COMPONENT_NAME, ComponentName.class)
+        ).isEqualTo(COMPONENT_NAME);
+    }
+
+    @Test
+    public void launchConfirmPattern_remoteValidation_patternLockType() throws Exception {
+        Activity activity = Robolectric.setupActivity(Activity.class);
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        StartLockscreenValidationRequest request = createStartLockscreenValidationRequest(
+                KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS);
+
+        ChooseLockSettingsHelper chooseLockSettingsHelper = getChooseLockSettingsHelper(
+                new ChooseLockSettingsHelper.Builder(activity)
+                        .setRemoteLockscreenValidation(true)
+                        .setStartLockscreenValidationRequest(request)
+                        .setRemoteLockscreenValidationServiceComponent(COMPONENT_NAME));
+        chooseLockSettingsHelper.launch();
+
+        Intent startedIntent = shadowActivity.getNextStartedActivity();
+        assertEquals(new ComponentName("com.android.settings",
+                ConfirmLockPattern.class.getName()), startedIntent.getComponent());
+        assertThat(startedIntent.getBooleanExtra(
+                ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, false)
+        ).isTrue();
+        assertThat(startedIntent.getParcelableExtra(
+                KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
+                StartLockscreenValidationRequest.class)
+        ).isEqualTo(request);
+        assertThat(startedIntent.getParcelableExtra(
+                Intent.EXTRA_COMPONENT_NAME, ComponentName.class)
+        ).isEqualTo(COMPONENT_NAME);
+    }
+
     private ChooseLockSettingsHelper getChooseLockSettingsHelper(
             ChooseLockSettingsHelper.Builder builder) {
         LockPatternUtils mockLockPatternUtils = mock(LockPatternUtils.class);
diff --git a/tests/robotests/src/com/android/settings/password/ConfirmCredentialTest.java b/tests/robotests/src/com/android/settings/password/ConfirmCredentialTest.java
index e210c5f..40b359e 100644
--- a/tests/robotests/src/com/android/settings/password/ConfirmCredentialTest.java
+++ b/tests/robotests/src/com/android/settings/password/ConfirmCredentialTest.java
@@ -16,25 +16,162 @@
 
 package com.android.settings.password;
 
+import static com.android.settings.password.TestUtils.NO_MORE_REMAINING_ATTEMPTS;
+import static com.android.settings.password.TestUtils.PACKAGE_NAME;
+import static com.android.settings.password.TestUtils.SERVICE_NAME;
+import static com.android.settings.password.TestUtils.VALID_REMAINING_ATTEMPTS;
+import static com.android.settings.password.TestUtils.buildConfirmDeviceCredentialBaseActivity;
+import static com.android.settings.password.TestUtils.createPackageInfoWithService;
+import static com.android.settings.password.TestUtils.createRemoteLockscreenValidationIntent;
+import static com.android.settings.password.TestUtils.createStartLockscreenValidationRequest;
+import static com.android.settings.password.TestUtils.getConfirmDeviceCredentialBaseFragment;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import android.Manifest;
+import android.app.KeyguardManager;
+import android.app.admin.ManagedSubscriptionsPolicy;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.util.FeatureFlagUtils;
 
 import androidx.fragment.app.FragmentActivity;
 import androidx.fragment.app.FragmentManager;
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.password.ConfirmDeviceCredentialBaseFragment.LastTryDialog;
+import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settings.testutils.shadow.ShadowUtils;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+        ShadowLockPatternUtils.class,
+        ShadowUtils.class,
+        ShadowDevicePolicyManager.class,
+        ShadowUserManager.class,
+        ShadowApplicationPackageManager.class
+})
 public class ConfirmCredentialTest {
 
-    private Context mContext = RuntimeEnvironment.application;
+    private Context mContext;
+    private ShadowApplicationPackageManager mShadowApplicationPackageManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = ApplicationProvider.getApplicationContext();
+
+        mShadowApplicationPackageManager =
+                (ShadowApplicationPackageManager) Shadows.shadowOf(mContext.getPackageManager());
+        mShadowApplicationPackageManager.addPackageNoDefaults(
+                TestUtils.createPackageInfoWithService(
+                        PACKAGE_NAME, SERVICE_NAME,
+                        Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE));
+
+        final ShadowDevicePolicyManager shadowDpm = ShadowDevicePolicyManager.getShadow();
+        shadowDpm.setManagedSubscriptionsPolicy(
+                new ManagedSubscriptionsPolicy(
+                        ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS));
+
+        FeatureFlagUtils.setEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, true);
+    }
+
+    @Test
+    public void onCreate_successfullyStart() {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(ConfirmLockPassword.class, new Intent());
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+
+        assertThat(activity.isFinishing()).isFalse();
+        assertThat(fragment.mRemoteValidation).isFalse();
+    }
+
+    @Test
+    public void onCreate_remoteValidation_successfullyStart() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
+                ConfirmLockPassword.class, createRemoteLockscreenValidationIntent(
+                        KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+
+        assertThat(activity.isFinishing()).isFalse();
+        assertThat(fragment.mRemoteValidation).isTrue();
+    }
+
+    @Test
+    public void onCreate_remoteValidation_flagDisabled_finishActivity() throws Exception {
+        FeatureFlagUtils.setEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, false);
+
+        ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
+                ConfirmLockPassword.class,
+                createRemoteLockscreenValidationIntent(
+                        KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+
+        assertThat(activity.isFinishing()).isTrue();
+    }
+
+    @Test
+    public void onCreate_remoteValidation_invalidServiceComponentName_finishActivity()
+            throws Exception {
+        Intent intentWithInvalidComponentName = new Intent()
+                .putExtra(ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, true)
+                .putExtra(KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
+                        createStartLockscreenValidationRequest(
+                                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS))
+                .putExtra(Intent.EXTRA_COMPONENT_NAME, new ComponentName("pkg", "cls"));
+
+        ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
+                ConfirmLockPassword.class, intentWithInvalidComponentName);
+
+        assertThat(activity.isFinishing()).isTrue();
+    }
+
+    @Test
+    public void onCreate_remoteValidation_serviceDoesNotRequestCorrectPermission_finishActivity()
+            throws Exception {
+        // Remove package with valid ServiceInfo
+        mShadowApplicationPackageManager.removePackage(PACKAGE_NAME);
+        // Add a service that does not request the BIND_REMOTE_LOCKSCREEN_SERVICE permission
+        mShadowApplicationPackageManager.addPackageNoDefaults(
+                createPackageInfoWithService(
+                        PACKAGE_NAME,
+                        SERVICE_NAME,
+                        Manifest.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE));
+
+        ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
+                ConfirmLockPassword.class,
+                createRemoteLockscreenValidationIntent(
+                        KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+
+        assertThat(activity.isFinishing()).isTrue();
+    }
+
+    @Test
+    public void onCreate_remoteValidation_noMoreAttempts_finishActivity() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity = buildConfirmDeviceCredentialBaseActivity(
+                ConfirmLockPassword.class,
+                createRemoteLockscreenValidationIntent(
+                        KeyguardManager.PASSWORD, NO_MORE_REMAINING_ATTEMPTS));
+
+        assertThat(activity.isFinishing()).isTrue();
+    }
 
     @Test
     public void testLastTryDialogShownExactlyOnce() {
diff --git a/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java
new file mode 100644
index 0000000..77a013d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java
@@ -0,0 +1,308 @@
+/*
+ * 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 static com.android.settings.password.ConfirmLockPassword.ConfirmLockPasswordFragment;
+import static com.android.settings.password.TestUtils.GUESS_INVALID_RESULT;
+import static com.android.settings.password.TestUtils.GUESS_VALID_RESULT;
+import static com.android.settings.password.TestUtils.LOCKOUT_RESULT;
+import static com.android.settings.password.TestUtils.NO_REMAINING_ATTEMPTS_RESULT;
+import static com.android.settings.password.TestUtils.PACKAGE_NAME;
+import static com.android.settings.password.TestUtils.SERVICE_NAME;
+import static com.android.settings.password.TestUtils.TIMEOUT_MS;
+import static com.android.settings.password.TestUtils.VALID_REMAINING_ATTEMPTS;
+import static com.android.settings.password.TestUtils.buildConfirmDeviceCredentialBaseActivity;
+import static com.android.settings.password.TestUtils.createRemoteLockscreenValidationIntent;
+import static com.android.settings.password.TestUtils.getConfirmDeviceCredentialBaseFragment;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+import android.Manifest;
+import android.app.KeyguardManager;
+import android.app.admin.ManagedSubscriptionsPolicy;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
+import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
+import android.text.InputType;
+import android.util.FeatureFlagUtils;
+import android.widget.ImeAwareEditText;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settings.testutils.shadow.ShadowUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+        ShadowLockPatternUtils.class,
+        ShadowUtils.class,
+        ShadowDevicePolicyManager.class,
+        ShadowUserManager.class,
+        ShadowApplicationPackageManager.class
+})
+public class ConfirmLockPasswordTest {
+
+    @Mock
+    CredentialCheckResultTracker mCredentialCheckResultTracker;
+    @Mock
+    RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
+    @Captor
+    ArgumentCaptor<IRemoteLockscreenValidationCallback> mCallbackCaptor;
+
+    private Context mContext;
+    private LockPatternUtils mLockPatternUtils;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = ApplicationProvider.getApplicationContext();
+        mLockPatternUtils = new LockPatternUtils(mContext);
+
+        ShadowApplicationPackageManager shadowApplicationPackageManager =
+                (ShadowApplicationPackageManager) Shadows.shadowOf(mContext.getPackageManager());
+        shadowApplicationPackageManager.addPackageNoDefaults(
+                TestUtils.createPackageInfoWithService(
+                        PACKAGE_NAME,
+                        SERVICE_NAME,
+                        Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE));
+
+        final ShadowDevicePolicyManager shadowDpm = ShadowDevicePolicyManager.getShadow();
+        shadowDpm.setManagedSubscriptionsPolicy(
+                new ManagedSubscriptionsPolicy(
+                        ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS));
+
+        // Set false by default so we can check if lock was set when remote validation succeeds.
+        ShadowLockPatternUtils.setIsSecure(UserHandle.myUserId(), false);
+
+        FeatureFlagUtils.setEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, true);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowLockPatternUtils.reset();
+    }
+
+    @Test
+    public void onCreate_remoteValidation_password_successfullyStart() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPassword.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+        ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+
+        assertThat(activity.isFinishing()).isFalse();
+        assertThat(fragment.mRemoteValidation).isTrue();
+        ImeAwareEditText editText = (ImeAwareEditText) activity.findViewById(R.id.password_entry);
+        assertThat(editText.getInputType()).isEqualTo(
+                InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+    }
+
+    @Test
+    public void onCreate_remoteValidation_pin_successfullyStart() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPassword.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PIN, VALID_REMAINING_ATTEMPTS));
+        ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+
+        assertThat(activity.isFinishing()).isFalse();
+        assertThat(fragment.mRemoteValidation).isTrue();
+        ImeAwareEditText editText = (ImeAwareEditText) activity.findViewById(R.id.password_entry);
+        assertThat(editText.getInputType()).isEqualTo(
+                InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+    }
+
+    @Test
+    public void handleNext_normalFlow_doesNotAttemptRemoteLockscreenValidation() {
+        ConfirmLockPassword activity = Robolectric.buildActivity(
+                ConfirmLockPassword.class, new Intent()).setup().get();
+        ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+        ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerHandleNext(fragment, passwordEntry);
+
+        verifyNoInteractions(mRemoteLockscreenValidationClient);
+    }
+
+    @Test
+    public void handleNext_remoteValidation_correctGuess_checkboxChecked() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPassword.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+        ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerHandleNext(fragment, passwordEntry);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(GUESS_VALID_RESULT);
+
+        verify(mCredentialCheckResultTracker).setResult(
+                eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isTrue();
+        assertThat(fragment.mDeviceCredentialGuess).isNotNull();
+    }
+
+    @Test
+    public void handleNext_remoteValidation_correctGuess_checkboxUnchecked() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPassword.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+        ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        fragment.mCheckBox.setChecked(false);
+        ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerHandleNext(fragment, passwordEntry);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(GUESS_VALID_RESULT);
+
+        verify(mCredentialCheckResultTracker).setResult(
+                eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
+        assertThat(fragment.mDeviceCredentialGuess).isNull();
+    }
+
+    @Test
+    public void handleNext_remoteValidation_guessInvalid() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPassword.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+        ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerHandleNext(fragment, passwordEntry);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(GUESS_INVALID_RESULT);
+
+        verify(mCredentialCheckResultTracker).setResult(
+                eq(false), any(), eq(0), eq(fragment.mEffectiveUserId));
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
+    }
+
+    @Test
+    public void handleNext_remoteValidation_lockout() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPassword.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+        ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerHandleNext(fragment, passwordEntry);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(LOCKOUT_RESULT);
+
+        verify(mCredentialCheckResultTracker).setResult(
+                eq(false), any(), eq(TIMEOUT_MS), eq(fragment.mEffectiveUserId));
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
+    }
+
+    @Test
+    public void handleNext_remoteValidation_noRemainingAttempts_finishActivity() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPassword.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+        ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        ImeAwareEditText passwordEntry = activity.findViewById(R.id.password_entry);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerHandleNext(fragment, passwordEntry);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(NO_REMAINING_ATTEMPTS_RESULT);
+
+        assertThat(activity.isFinishing()).isTrue();
+        verify(mCredentialCheckResultTracker, never())
+                .setResult(anyBoolean(), any(), anyInt(), anyInt());
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
+    }
+
+    private void triggerHandleNext(
+            ConfirmLockPasswordFragment fragment, ImeAwareEditText passwordEntry) {
+        passwordEntry.setText("Password");
+        ReflectionHelpers.callInstanceMethod(fragment, "handleNext");
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java
new file mode 100644
index 0000000..4374e38
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java
@@ -0,0 +1,286 @@
+/*
+ * 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 static com.android.settings.password.TestUtils.GUESS_INVALID_RESULT;
+import static com.android.settings.password.TestUtils.GUESS_VALID_RESULT;
+import static com.android.settings.password.TestUtils.LOCKOUT_RESULT;
+import static com.android.settings.password.TestUtils.NO_REMAINING_ATTEMPTS_RESULT;
+import static com.android.settings.password.TestUtils.PACKAGE_NAME;
+import static com.android.settings.password.TestUtils.SERVICE_NAME;
+import static com.android.settings.password.TestUtils.TIMEOUT_MS;
+import static com.android.settings.password.TestUtils.VALID_REMAINING_ATTEMPTS;
+import static com.android.settings.password.TestUtils.buildConfirmDeviceCredentialBaseActivity;
+import static com.android.settings.password.TestUtils.createPackageInfoWithService;
+import static com.android.settings.password.TestUtils.createRemoteLockscreenValidationIntent;
+import static com.android.settings.password.TestUtils.getConfirmDeviceCredentialBaseFragment;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+import android.Manifest;
+import android.app.KeyguardManager;
+import android.app.admin.ManagedSubscriptionsPolicy;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
+import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
+import android.util.FeatureFlagUtils;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settings.testutils.shadow.ShadowUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+        ShadowLockPatternUtils.class,
+        ShadowUtils.class,
+        ShadowDevicePolicyManager.class,
+        ShadowUserManager.class,
+        ShadowApplicationPackageManager.class
+})
+public class ConfirmLockPatternTest {
+
+    @Mock
+    CredentialCheckResultTracker mCredentialCheckResultTracker;
+    @Mock
+    RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
+    @Captor
+    ArgumentCaptor<IRemoteLockscreenValidationCallback> mCallbackCaptor;
+
+    private Context mContext;
+    private LockPatternUtils mLockPatternUtils;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = ApplicationProvider.getApplicationContext();
+        mLockPatternUtils = new LockPatternUtils(mContext);
+
+        ShadowApplicationPackageManager shadowApplicationPackageManager =
+                (ShadowApplicationPackageManager) Shadows.shadowOf(mContext.getPackageManager());
+        shadowApplicationPackageManager.addPackageNoDefaults(
+                createPackageInfoWithService(
+                        PACKAGE_NAME,
+                        SERVICE_NAME,
+                        Manifest.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE));
+
+        final ShadowDevicePolicyManager shadowDpm = ShadowDevicePolicyManager.getShadow();
+        shadowDpm.setManagedSubscriptionsPolicy(
+                new ManagedSubscriptionsPolicy(
+                        ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS));
+
+        // Set false by default so we can check if lock was set when remote validation succeeds.
+        ShadowLockPatternUtils.setIsSecure(UserHandle.myUserId(), false);
+
+        FeatureFlagUtils.setEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, true);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowLockPatternUtils.reset();
+    }
+
+    @Test
+    public void onCreate_remoteValidation_successfullyStart() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPattern.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+
+        assertThat(activity.isFinishing()).isFalse();
+        assertThat(fragment.mRemoteValidation).isTrue();
+        LockPatternView lockPatternView = (LockPatternView) activity.findViewById(R.id.lockPattern);
+        assertThat(lockPatternView.isInStealthMode()).isFalse();
+    }
+
+    @Test
+    public void onPatternDetected_normalFlow_doesNotAttemptRemoteLockscreenValidation() {
+        ConfirmLockPattern activity = Robolectric.buildActivity(
+                ConfirmLockPattern.class, new Intent()).setup().get();
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+        LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerOnPatternDetected(lockPatternView);
+
+        verifyNoInteractions(mRemoteLockscreenValidationClient);
+    }
+
+    @Test
+    public void onPatternDetected_remoteValidation_guessValid_checkboxChecked() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPattern.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+        LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerOnPatternDetected(lockPatternView);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(GUESS_VALID_RESULT);
+
+        verify(mCredentialCheckResultTracker).setResult(
+                eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isTrue();
+        assertThat(fragment.mDeviceCredentialGuess).isNotNull();
+    }
+
+    @Test
+    public void onPatternDetected_remoteValidation_guessValid_checkboxUnchecked() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPattern.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+        LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        fragment.mCheckBox.setChecked(false);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerOnPatternDetected(lockPatternView);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(GUESS_VALID_RESULT);
+
+        verify(mCredentialCheckResultTracker).setResult(
+                eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
+        assertThat(fragment.mDeviceCredentialGuess).isNull();
+    }
+
+    @Test
+    public void onPatternDetected_remoteValidation_guessInvalid() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPattern.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+        LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerOnPatternDetected(lockPatternView);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(GUESS_INVALID_RESULT);
+
+        verify(mCredentialCheckResultTracker).setResult(
+                eq(false), any(), eq(0), eq(fragment.mEffectiveUserId));
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
+    }
+
+    @Test
+    public void onPatternDetected_remoteValidation_lockout() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPattern.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+        LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerOnPatternDetected(lockPatternView);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(LOCKOUT_RESULT);
+
+        verify(mCredentialCheckResultTracker).setResult(
+                eq(false), any(), eq(TIMEOUT_MS), eq(fragment.mEffectiveUserId));
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
+    }
+
+    @Test
+    public void onPatternDetected_noRemainingAttempts_finishActivity() throws Exception {
+        ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPattern.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
+        ConfirmDeviceCredentialBaseFragment fragment =
+                getConfirmDeviceCredentialBaseFragment(activity);
+        LockPatternView lockPatternView = activity.findViewById(R.id.lockPattern);
+        ReflectionHelpers.setField(fragment,
+                "mCredentialCheckResultTracker", mCredentialCheckResultTracker);
+        fragment.mRemoteLockscreenValidationClient = mRemoteLockscreenValidationClient;
+
+        triggerOnPatternDetected(lockPatternView);
+        verify(mRemoteLockscreenValidationClient)
+                .validateLockscreenGuess(any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onSuccess(NO_REMAINING_ATTEMPTS_RESULT);
+
+        assertThat(activity.isFinishing()).isTrue();
+        verifyNoInteractions(mCredentialCheckResultTracker);
+        assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
+    }
+
+    private void triggerOnPatternDetected(LockPatternView lockPatternView) {
+        List<LockPatternView.Cell> pattern = List.of(LockPatternView.Cell.of(0, 0));
+        lockPatternView.setPattern(LockPatternView.DisplayMode.Correct, pattern);
+        ReflectionHelpers.callInstanceMethod(lockPatternView, "notifyPatternDetected");
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/password/TestUtils.java b/tests/robotests/src/com/android/settings/password/TestUtils.java
new file mode 100644
index 0000000..246d926
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/TestUtils.java
@@ -0,0 +1,107 @@
+/*
+ * 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.KeyguardManager;
+import android.app.RemoteLockscreenValidationResult;
+import android.app.StartLockscreenValidationRequest;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.ServiceInfo;
+
+import com.android.security.SecureBox;
+import com.android.settings.R;
+
+import org.robolectric.Robolectric;
+
+import java.security.NoSuchAlgorithmException;
+
+public final class TestUtils {
+
+    public static final String SERVICE_NAME = "SERVICE_NAME";
+    public static final String PACKAGE_NAME = "PACKAGE_NAME";
+    public static final ComponentName COMPONENT_NAME =
+            new ComponentName(PACKAGE_NAME, SERVICE_NAME);
+    public static final int VALID_REMAINING_ATTEMPTS = 5;
+    public static final int NO_MORE_REMAINING_ATTEMPTS = 0;
+    public static final int TIMEOUT_MS = 10000;
+    public static final RemoteLockscreenValidationResult GUESS_VALID_RESULT =
+            new RemoteLockscreenValidationResult.Builder()
+                    .setResultCode(RemoteLockscreenValidationResult.RESULT_GUESS_VALID)
+                    .build();
+    public static final RemoteLockscreenValidationResult GUESS_INVALID_RESULT =
+            new RemoteLockscreenValidationResult.Builder()
+                    .setResultCode(RemoteLockscreenValidationResult.RESULT_GUESS_INVALID)
+                    .build();
+    public static final RemoteLockscreenValidationResult LOCKOUT_RESULT =
+            new RemoteLockscreenValidationResult.Builder()
+                    .setResultCode(RemoteLockscreenValidationResult.RESULT_LOCKOUT)
+                    .setTimeoutMillis(TIMEOUT_MS)
+                    .build();
+    public static final RemoteLockscreenValidationResult NO_REMAINING_ATTEMPTS_RESULT =
+            new RemoteLockscreenValidationResult.Builder()
+                    .setResultCode(RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS)
+                    .build();
+
+    private TestUtils() {
+    }
+
+    public static PackageInfo createPackageInfoWithService(
+            String packageName, String serviceName, String requiredServicePermission) {
+        ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.name = serviceName;
+        serviceInfo.applicationInfo = new ApplicationInfo();
+        serviceInfo.permission = requiredServicePermission;
+
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = packageName;
+        packageInfo.services = new ServiceInfo[]{serviceInfo};
+        return packageInfo;
+    }
+
+    public static Intent createRemoteLockscreenValidationIntent(
+            int lockscreenType, int remainingAttempts) throws Exception {
+        return new Intent()
+                .putExtra(ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION, true)
+                .putExtra(KeyguardManager.EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST,
+                        createStartLockscreenValidationRequest(lockscreenType, remainingAttempts))
+                .putExtra(Intent.EXTRA_COMPONENT_NAME, COMPONENT_NAME);
+    }
+
+    public static StartLockscreenValidationRequest createStartLockscreenValidationRequest(
+            int lockscreenType, int remainingAttempts) throws NoSuchAlgorithmException {
+        return new StartLockscreenValidationRequest.Builder()
+                .setLockscreenUiType(lockscreenType)
+                .setRemainingAttempts(remainingAttempts)
+                .setSourcePublicKey(SecureBox.genKeyPair().getPublic().getEncoded())
+                .build();
+    }
+
+    public static ConfirmDeviceCredentialBaseActivity buildConfirmDeviceCredentialBaseActivity(
+            Class<? extends ConfirmDeviceCredentialBaseActivity> impl, Intent intent) {
+        return Robolectric.buildActivity(impl, intent).setup().get();
+    }
+
+    public static ConfirmDeviceCredentialBaseFragment getConfirmDeviceCredentialBaseFragment(
+            ConfirmDeviceCredentialBaseActivity activity) {
+        return (ConfirmDeviceCredentialBaseFragment)
+                activity.getSupportFragmentManager().findFragmentById(R.id.main_content);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
index a2e4430..0396503 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
@@ -8,6 +8,7 @@
 import android.annotation.UserIdInt;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManager.DeviceOwnerType;
+import android.app.admin.ManagedSubscriptionsPolicy;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.PasswordPolicy;
 import android.content.ComponentName;
@@ -29,6 +30,7 @@
     private Map<Integer, CharSequence> mSupportMessagesMap = new HashMap<>();
     private boolean mIsAdminActiveAsUser = false;
     private ComponentName mDeviceOwnerComponentName;
+    private ManagedSubscriptionsPolicy mManagedSubscriptionsPolicy;
     private int mDeviceOwnerUserId = -1;
     private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
     private int mPasswordMinLength = 0;
@@ -85,6 +87,10 @@
         mDeviceOwnerTypes.put(admin.getPackageName(), deviceOwnerType);
     }
 
+    public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
+        mManagedSubscriptionsPolicy = policy;
+    }
+
     @DeviceOwnerType
     public int getDeviceOwnerType(@NonNull ComponentName admin) {
         return mDeviceOwnerTypes.getOrDefault(admin.getPackageName(), DEVICE_OWNER_TYPE_DEFAULT);
@@ -99,6 +105,11 @@
         return policy.getMinMetrics();
     }
 
+    @Implementation
+    public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
+        return mManagedSubscriptionsPolicy;
+    }
+
     public void setPasswordQuality(int quality) {
         mPasswordMinQuality = quality;
     }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
index 157c218..c6c6c55 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.testutils.shadow;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.PasswordMetrics;
 import android.content.ComponentName;
@@ -43,6 +45,10 @@
     private static Map<Integer, PasswordMetrics> sUserToMetricsMap = new HashMap<>();
     private static Map<Integer, PasswordMetrics> sUserToProfileMetricsMap = new HashMap<>();
     private static Map<Integer, Boolean> sUserToIsSecureMap = new HashMap<>();
+    private static Map<Integer, Boolean> sUserToPatternEverChosenMap = new HashMap<>();
+    private static Map<Integer, Boolean> sUserToVisiblePatternEnabledMap = new HashMap<>();
+    private static Map<Integer, Boolean> sUserToBiometricAllowedMap = new HashMap<>();
+    private static Map<Integer, Boolean> sUserToLockPatternEnabledMap = new HashMap<>();
 
     private static boolean sIsUserOwnsFrpCredential;
 
@@ -53,6 +59,10 @@
         sUserToMetricsMap.clear();
         sUserToProfileMetricsMap.clear();
         sUserToIsSecureMap.clear();
+        sUserToPatternEverChosenMap.clear();
+        sUserToVisiblePatternEnabledMap.clear();
+        sUserToBiometricAllowedMap.clear();
+        sUserToLockPatternEnabledMap.clear();
         sDeviceEncryptionEnabled = false;
         sIsUserOwnsFrpCredential = false;
     }
@@ -136,6 +146,56 @@
         sIsUserOwnsFrpCredential = isUserOwnsFrpCredential;
     }
 
+    @Implementation
+    public boolean isVisiblePatternEnabled(int userId) {
+        return sUserToVisiblePatternEnabledMap.getOrDefault(userId, false);
+    }
+
+    public static void setIsVisiblePatternEnabled(int userId, boolean isVisiblePatternEnabled) {
+        sUserToVisiblePatternEnabledMap.put(userId, isVisiblePatternEnabled);
+    }
+
+    @Implementation
+    public boolean isPatternEverChosen(int userId) {
+        return sUserToPatternEverChosenMap.getOrDefault(userId, true);
+    }
+
+    public static void setIsPatternEverChosen(int userId, boolean isPatternEverChosen) {
+        sUserToPatternEverChosenMap.put(userId, isPatternEverChosen);
+    }
+
+    @Implementation
+    public boolean isBiometricAllowedForUser(int userId) {
+        return sUserToBiometricAllowedMap.getOrDefault(userId, false);
+    }
+
+    public static void setIsBiometricAllowedForUser(int userId, boolean isBiometricAllowed) {
+        sUserToBiometricAllowedMap.put(userId, isBiometricAllowed);
+    }
+
+    @Implementation
+    public boolean isLockPatternEnabled(int userId) {
+        return sUserToBiometricAllowedMap.getOrDefault(userId, false);
+    }
+
+    public static void setIsLockPatternEnabled(int userId, boolean isLockPatternEnabled) {
+        sUserToLockPatternEnabledMap.put(userId, isLockPatternEnabled);
+    }
+
+    @Implementation
+    public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
+            @NonNull LockscreenCredential savedCredential, int userHandle) {
+        setIsSecure(userHandle, true);
+        return true;
+    }
+
+    @Implementation
+    public boolean checkCredential(@NonNull LockscreenCredential credential, int userId,
+            @Nullable LockPatternUtils.CheckCredentialProgressCallback progressCallback)
+            throws LockPatternUtils.RequestThrottledException {
+        return true;
+    }
+
     public static void setRequiredPasswordComplexity(int userHandle, int complexity) {
         sUserToComplexityMap.put(userHandle, complexity);
     }