Merge changes from topic "aae-camera-rvc-qpr-dev" into rvc-qpr-dev

* changes:
  Added a setView call before 3d stream started.
  Replace for-loop copying with memcpy.
  Fix SEGV_MAPERR in SurroundViewService destructor
  Update the core_lib from g3 cl/324068941 updated.
  Fixes setViews to be set from sv_app
  Fixes overlays in Surround View 3D
  Improve test coverage for SurroundViewSession
  Align the order of buffers to the listed cameras
  Remove CoreLibSetupHelper class.
  Sets processingEvsFrames earlier to fix conflicts
  Refine the stopping logic.
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index 657bad9..de75761 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -86,6 +86,7 @@
 PRODUCT_PACKAGES += \
     CarFrameworkPackageStubs \
     CarService \
+    CarShell \
     CarDialerApp \
     CarRadioApp \
     OverviewApp \
diff --git a/car_product/build/preinstalled-packages-product-car-base.xml b/car_product/build/preinstalled-packages-product-car-base.xml
index 0ed7b7f..14b16ec 100644
--- a/car_product/build/preinstalled-packages-product-car-base.xml
+++ b/car_product/build/preinstalled-packages-product-car-base.xml
@@ -42,6 +42,10 @@
         <install-in user-type="FULL" />
         <install-in user-type="SYSTEM" />
     </install-in-user-type>
+        <install-in-user-type package="com.android.car.shell">
+        <install-in user-type="FULL" />
+        <install-in user-type="SYSTEM" />
+    </install-in-user-type>
     <install-in-user-type package="com.android.car.frameworkpackagestubs">
         <install-in user-type="FULL" />
         <install-in user-type="SYSTEM" />
diff --git a/evs/sampleDriver/EvsV4lCamera.cpp b/evs/sampleDriver/EvsV4lCamera.cpp
index cd2a3a4..a46a80b 100644
--- a/evs/sampleDriver/EvsV4lCamera.cpp
+++ b/evs/sampleDriver/EvsV4lCamera.cpp
@@ -728,7 +728,7 @@
 
     if (!readyForFrame) {
         // We need to return the video buffer so it can capture a new frame
-        mVideo.markFrameConsumed();
+        mVideo.markFrameConsumed(pV4lBuff->index);
     } else {
         // Assemble the buffer description we'll transmit below
         BufferDesc_1_1 bufDesc_1_1 = {};
@@ -779,7 +779,7 @@
         // Give the video frame back to the underlying device for reuse
         // Note that we do this before making the client callback to give the
         // underlying camera more time to capture the next frame
-        mVideo.markFrameConsumed();
+        mVideo.markFrameConsumed(pV4lBuff->index);
 
         // Issue the (asynchronous) callback to the client -- can't be holding
         // the lock
diff --git a/evs/sampleDriver/VideoCapture.cpp b/evs/sampleDriver/VideoCapture.cpp
index 9663bf6..d83ae94 100644
--- a/evs/sampleDriver/VideoCapture.cpp
+++ b/evs/sampleDriver/VideoCapture.cpp
@@ -91,7 +91,7 @@
     // Set our desired output format
     v4l2_format format;
     format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+    format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
     format.fmt.pix.width = width;
     format.fmt.pix.height = height;
     LOG(INFO) << "Requesting format: "
@@ -125,7 +125,7 @@
 
     // Make sure we're initialized to the STOPPED state
     mRunMode = STOPPED;
-    mFrameReady = false;
+    mFrames.clear();
 
     // Ready to go!
     return true;
@@ -164,45 +164,54 @@
         return false;
     }
 
-    // Get the information on the buffer that was created for us
-    memset(&mBufferInfo, 0, sizeof(mBufferInfo));
-    mBufferInfo.type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    mBufferInfo.memory   = V4L2_MEMORY_MMAP;
-    mBufferInfo.index    = 0;
-    if (ioctl(mDeviceFd, VIDIOC_QUERYBUF, &mBufferInfo) < 0) {
-        PLOG(ERROR) << "VIDIOC_QUERYBUF failed";
-        return false;
-    }
+    mNumBuffers = bufrequest.count;
+    mBufferInfos = std::make_unique<v4l2_buffer[]>(mNumBuffers);
+    mPixelBuffers = std::make_unique<void *[]>(mNumBuffers);
 
-    LOG(INFO) << "Buffer description:";
-    LOG(INFO) << "  offset: " << mBufferInfo.m.offset;
-    LOG(INFO) << "  length: " << mBufferInfo.length;
-    LOG(INFO) << "  flags : " << std::hex << mBufferInfo.flags;
+    for (int i = 0; i < mNumBuffers; ++i) {
+      // Get the information on the buffer that was created for us
+        memset(&mBufferInfos[i], 0, sizeof(v4l2_buffer));
+        mBufferInfos[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+        mBufferInfos[i].memory = V4L2_MEMORY_MMAP;
+        mBufferInfos[i].index = i;
 
-    // Get a pointer to the buffer contents by mapping into our address space
-    mPixelBuffer = mmap(
-            NULL,
-            mBufferInfo.length,
-            PROT_READ | PROT_WRITE,
-            MAP_SHARED,
-            mDeviceFd,
-            mBufferInfo.m.offset
-    );
-    if( mPixelBuffer == MAP_FAILED) {
-        PLOG(ERROR) << "mmap() failed";
-        return false;
-    }
-    memset(mPixelBuffer, 0, mBufferInfo.length);
-    LOG(INFO) << "Buffer mapped at " << mPixelBuffer;
+        if (ioctl(mDeviceFd, VIDIOC_QUERYBUF, &mBufferInfos[i]) < 0) {
+            PLOG(ERROR) << "VIDIOC_QUERYBUF failed";
+            return false;
+        }
 
-    // Queue the first capture buffer
-    if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
-        PLOG(ERROR) << "VIDIOC_QBUF failed";
-        return false;
+        LOG(INFO) << "Buffer description:";
+        LOG(INFO) << "  offset: " << mBufferInfos[i].m.offset;
+        LOG(INFO) << "  length: " << mBufferInfos[i].length;
+        LOG(INFO) << "  flags : " << std::hex << mBufferInfos[i].flags;
+
+        // Get a pointer to the buffer contents by mapping into our address space
+        mPixelBuffers[i] = mmap(
+                NULL,
+                mBufferInfos[i].length,
+                PROT_READ | PROT_WRITE,
+                MAP_SHARED,
+                mDeviceFd,
+                mBufferInfos[i].m.offset
+        );
+
+        if(mPixelBuffers[i] == MAP_FAILED) {
+            PLOG(ERROR) << "mmap() failed";
+            return false;
+        }
+
+        memset(mPixelBuffers[i], 0, mBufferInfos[i].length);
+        LOG(INFO) << "Buffer mapped at " << mPixelBuffers[i];
+
+        // Queue the first capture buffer
+        if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfos[i]) < 0) {
+            PLOG(ERROR) << "VIDIOC_QBUF failed";
+            return false;
+        }
     }
 
     // Start the video stream
-    int type = mBufferInfo.type;
+    const int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     if (ioctl(mDeviceFd, VIDIOC_STREAMON, &type) < 0) {
         PLOG(ERROR) << "VIDIOC_STREAMON failed";
         return false;
@@ -236,7 +245,7 @@
         }
 
         // Stop the underlying video stream (automatically empties the buffer queue)
-        int type = mBufferInfo.type;
+        const int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         if (ioctl(mDeviceFd, VIDIOC_STREAMOFF, &type) < 0) {
             PLOG(ERROR) << "VIDIOC_STREAMOFF failed";
         }
@@ -244,8 +253,10 @@
         LOG(DEBUG) << "Capture thread stopped.";
     }
 
-    // Unmap the buffers we allocated
-    munmap(mPixelBuffer, mBufferInfo.length);
+    for (int i = 0; i < mNumBuffers; ++i) {
+        // Unmap the buffers we allocated
+        munmap(mPixelBuffers[i], mBufferInfos[i].length);
+    }
 
     // Tell the L4V2 driver to release our streaming buffers
     v4l2_requestbuffers bufrequest;
@@ -256,24 +267,29 @@
 
     // Drop our reference to the frame delivery callback interface
     mCallback = nullptr;
+
+    // Release capture buffers
+    mNumBuffers = 0;
+    mBufferInfos = nullptr;
+    mPixelBuffers = nullptr;
 }
 
 
-void VideoCapture::markFrameReady() {
-    mFrameReady = true;
-};
-
-
-bool VideoCapture::returnFrame() {
-    // We're giving the frame back to the system, so clear the "ready" flag
-    mFrameReady = false;
+bool VideoCapture::returnFrame(int id) {
+    if (mFrames.find(id) == mFrames.end()) {
+        LOG(WARNING) << "Invalid request to return a buffer " << id << " is ignored.";
+        return false;
+    }
 
     // Requeue the buffer to capture the next available frame
-    if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
+    if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfos[id]) < 0) {
         PLOG(ERROR) << "VIDIOC_QBUF failed";
         return false;
     }
 
+    // Remove ID of returned buffer from the set
+    mFrames.erase(id);
+
     return true;
 }
 
@@ -282,17 +298,22 @@
 void VideoCapture::collectFrames() {
     // Run until our atomic signal is cleared
     while (mRunMode == RUN) {
+        struct v4l2_buffer buf = {
+            .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+            .memory = V4L2_MEMORY_MMAP
+        };
+
         // Wait for a buffer to be ready
-        if (ioctl(mDeviceFd, VIDIOC_DQBUF, &mBufferInfo) < 0) {
+        if (ioctl(mDeviceFd, VIDIOC_DQBUF, &buf) < 0) {
             PLOG(ERROR) << "VIDIOC_DQBUF failed";
             break;
         }
 
-        markFrameReady();
+        mFrames.insert(buf.index);
 
         // If a callback was requested per frame, do that now
         if (mCallback) {
-            mCallback(this, &mBufferInfo, mPixelBuffer);
+            mCallback(this, &mBufferInfos[buf.index], mPixelBuffers[buf.index]);
         }
     }
 
diff --git a/evs/sampleDriver/VideoCapture.h b/evs/sampleDriver/VideoCapture.h
index 42a5ef4..d298d41 100644
--- a/evs/sampleDriver/VideoCapture.h
+++ b/evs/sampleDriver/VideoCapture.h
@@ -41,12 +41,21 @@
     __u32   getV4LFormat()      { return mFormat; };
 
     // NULL until stream is started
-    void* getLatestData()       { return mPixelBuffer; };
+    void* getLatestData() {
+        if (mFrames.empty()) {
+            // No frame is available
+            return nullptr;
+        }
 
-    bool isFrameReady()         { return mFrameReady; };
-    void markFrameConsumed()    { returnFrame(); };
+        // Return a pointer to the buffer captured most recently
+        const int latestBufferId = *mFrames.end();
+        return mPixelBuffers[latestBufferId];
+    }
 
-    bool isOpen()               { return mDeviceFd >= 0; };
+    bool isFrameReady()             { return !mFrames.empty(); }
+    void markFrameConsumed(int id)  { returnFrame(id); }
+
+    bool isOpen()                   { return mDeviceFd >= 0; }
 
     int setParameter(struct v4l2_control& control);
     int getParameter(struct v4l2_control& control);
@@ -54,13 +63,13 @@
 
 private:
     void collectFrames();
-    void markFrameReady();
-    bool returnFrame();
+    bool returnFrame(int id);
 
     int mDeviceFd = -1;
 
-    v4l2_buffer mBufferInfo = {};
-    void* mPixelBuffer = nullptr;
+    int mNumBuffers = 0;
+    std::unique_ptr<v4l2_buffer[]> mBufferInfos = nullptr;
+    std::unique_ptr<void*[]>       mPixelBuffers = nullptr;
 
     __u32   mFormat = 0;
     __u32   mWidth  = 0;
@@ -71,7 +80,7 @@
 
     std::thread mCaptureThread;             // The thread we'll use to dispatch frames
     std::atomic<int> mRunMode;              // Used to signal the frame loop (see RunModes below)
-    std::atomic<bool> mFrameReady;          // Set when a frame has been delivered
+    std::set<int> mFrames;                  // Set of available frame buffers
 
     // Careful changing these -- we're using bit-wise ops to manipulate these
     enum RunModes {
diff --git a/packages/CarShell/Android.bp b/packages/CarShell/Android.bp
new file mode 100644
index 0000000..545a2c5
--- /dev/null
+++ b/packages/CarShell/Android.bp
@@ -0,0 +1,7 @@
+android_app {
+    name: "CarShell",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/packages/CarShell/AndroidManifest.xml b/packages/CarShell/AndroidManifest.xml
new file mode 100644
index 0000000..d9bdace
--- /dev/null
+++ b/packages/CarShell/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.car.shell"
+        coreApp="true"
+        android:sharedUserId="android.uid.shell"
+        >
+    <!-- Permission required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
+    <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" />
+    <!-- Permission required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
+    <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
+</manifest>
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index f6f9aaa..837a0ad 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -480,16 +480,10 @@
         int currentUserId = ActivityManager.getCurrentUser();
         UserInfo currentUser = mUserManager.getUserInfo(currentUserId);
 
-        UserInfo newUser = mInitialUserSetter.replaceGuestIfNeeded(currentUser);
-        if (newUser == currentUser) return; // Not a guest
+        if (!mInitialUserSetter.canReplaceGuestUser(currentUser)) return; // Not a guest
 
-        boolean replaceGuest = !mSwitchGuestUserBeforeSleep;
-        if (newUser == null) {
-            Slog.w(TAG, "Failed to replace guest; falling back to default behavior");
-            executeDefaultInitialUserBehavior(replaceGuest);
-            return;
-        }
-        switchUser(newUser.id, replaceGuest);
+        mInitialUserSetter.set(newInitialUserInfoBuilder(InitialUserSetter.TYPE_REPLACE_GUEST)
+                .build());
     }
 
     private void switchUser(@UserIdInt int userId, boolean replaceGuest) {
diff --git a/tests/carservice_unit_test/src/android/car/userlib/InitialUserSetterTest.java b/tests/carservice_unit_test/src/android/car/userlib/InitialUserSetterTest.java
index 785cbc4..9fbe5b3 100644
--- a/tests/carservice_unit_test/src/android/car/userlib/InitialUserSetterTest.java
+++ b/tests/carservice_unit_test/src/android/car/userlib/InitialUserSetterTest.java
@@ -338,6 +338,28 @@
     }
 
     @Test
+    public void testCanReplaceGuestUser_NotGuest() {
+        UserInfo user = expectUserExists(USER_ID);
+
+        assertThat(mSetter.canReplaceGuestUser(user)).isFalse();
+    }
+
+    @Test
+    public void testCanReplaceGuestUser_Guest() {
+        UserInfo user = expectGuestExists(USER_ID, /* isEphemeral */true);
+
+        assertThat(mSetter.canReplaceGuestUser(user)).isTrue();
+    }
+
+    @Test
+    public void testCanReplaceGuestUser_GuestLocked() {
+        UserInfo user = expectGuestExists(USER_ID, /* isEphemeral */true);
+        expectUserIsSecure(USER_ID);
+
+        assertThat(mSetter.canReplaceGuestUser(user)).isFalse();
+    }
+
+    @Test
     public void testCreateUser_ok_noflags() throws Exception {
         UserInfo newUser = expectCreateFullUser(USER_ID, "TheDude", NO_FLAGS);
         expectSwitchUser(USER_ID);
@@ -486,6 +508,51 @@
     }
 
     @Test
+    public void testReplaceUser_ok() throws Exception {
+        mockGetCurrentUser(CURRENT_USER_ID);
+        expectGuestExists(CURRENT_USER_ID, /* ephemeral */ true); // ephemeral doesn't matter
+        UserInfo newGuest = expectGuestExists(NEW_USER_ID, /* ephemeral */ true);
+        expectGuestReplaced(CURRENT_USER_ID, newGuest);
+        expectSwitchUser(NEW_USER_ID);
+
+        mSetter.set(new Builder(InitialUserSetter.TYPE_REPLACE_GUEST)
+                .build());
+
+        verifyUserSwitched(NEW_USER_ID);
+        verifyFallbackDefaultBehaviorNeverCalled();
+        verifySystemUserUnlocked();
+        verifyUserDeleted(CURRENT_USER_ID);
+        assertInitialUserSet(newGuest);
+    }
+
+    @Test
+    public void testReplaceUser_fail_cantCreate() throws Exception {
+        mockGetCurrentUser(CURRENT_USER_ID);
+        expectGuestExists(CURRENT_USER_ID, /* ephemeral */ true); // ephemeral doesn't matter
+        expectGuestReplaced(CURRENT_USER_ID, null);
+
+        mSetter.set(new Builder(InitialUserSetter.TYPE_REPLACE_GUEST)
+                .build());
+
+        verifyFallbackDefaultBehaviorCalledFromReaplceUser();
+    }
+
+    @Test
+    public void testReplaceUser_ok_sameUser() throws Exception {
+        mockGetCurrentUser(CURRENT_USER_ID);
+        UserInfo userInfo = expectGuestExists(CURRENT_USER_ID, /* ephemeral */ true);
+        expectGuestReplaced(CURRENT_USER_ID, userInfo);
+
+        mSetter.set(new Builder(InitialUserSetter.TYPE_REPLACE_GUEST)
+                .build());
+
+        verifyUserNeverSwitched();
+        verifyFallbackDefaultBehaviorNeverCalled();
+        verifySystemUserUnlocked();
+        assertInitialUserSet(userInfo);
+    }
+
+    @Test
     public void testDefaultBehavior_firstBoot_ok() throws Exception {
         // no need to mock hasInitialUser(), it will return false by default
         UserInfo newUser = expectCreateFullUser(USER_ID, OWNER_NAME, UserInfo.FLAG_ADMIN);
@@ -858,6 +925,11 @@
         assertInitialUserSet(null);
     }
 
+    private void verifyFallbackDefaultBehaviorCalledFromReaplceUser() {
+        verify(mSetter).fallbackDefaultBehavior(isInitialInfo(false), eq(true), anyString());
+        assertInitialUserSet(null);
+    }
+
     private void verifyFallbackDefaultBehaviorNeverCalled() {
         verifyFallbackDefaultBehaviorNeverCalled(/* supportsOverrideUserIdProperty= */ false);
     }
diff --git a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
index 3cbfffc..df2d9bf 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
@@ -16,9 +16,6 @@
 
 package com.android.car;
 
-import static android.car.test.mocks.CarArgumentMatchers.isUserInfo;
-import static android.car.test.util.UserTestingHelper.newGuestUser;
-
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -352,33 +349,21 @@
     @Test
     public void testUserSwitchingOnResume_disabledByOEM_nonGuest() throws Exception {
         UserInfo currentUser = setCurrentUser(CURRENT_USER_ID, /* isGuest= */ false);
-        expectNewGuestCreated(CURRENT_USER_ID, currentUser);
+        expectCurrentGuestCanBeReplaced(false);
 
         suspendAndResumeForUserSwitchingTestsWhileDisabledByOem();
 
-        verifyUserNotSwitched();
+        verifyNoSetCall();
     }
 
     @Test
     public void testUserSwitchingOnResume_disabledByOEM_guest() throws Exception {
         setCurrentUser(CURRENT_GUEST_ID, /* isGuest= */ true);
-        UserInfo newGuest = newGuestUser(NEW_GUEST_ID, /* ephemeral= */ true);
-        expectNewGuestCreated(CURRENT_GUEST_ID, newGuest);
+        expectCurrentGuestCanBeReplaced(true);
 
         suspendAndResumeForUserSwitchingTestsWhileDisabledByOem();
 
-        verifyUserSwitched(NEW_GUEST_ID);
-    }
-
-    @Test
-    public void testUserSwitchingOnResume_disabledByOEM_guestReplacementFails() throws Exception {
-        setCurrentUser(CURRENT_GUEST_ID, /* isGuest= */ true);
-        expectNewGuestCreated(CURRENT_GUEST_ID, /* newGuest= */ null);
-
-        suspendAndResumeForUserSwitchingTestsWhileDisabledByOem();
-
-        verifyUserNotSwitched();
-        verifyDefaultInitialUserBehaviorCalled();
+        verifyUserReplaced();
     }
 
     @Test
@@ -622,9 +607,8 @@
         }));
     }
 
-    private void expectNewGuestCreated(int existingGuestId, UserInfo newGuest) {
-        when(mInitialUserSetter.replaceGuestIfNeeded(isUserInfo(existingGuestId)))
-                .thenReturn(newGuest);
+    private void expectCurrentGuestCanBeReplaced(boolean result) {
+        when(mInitialUserSetter.canReplaceGuestUser(notNull())).thenReturn(result);
     }
 
     private void verifyDefaultInitialUserBehaviorCalled() {
@@ -649,6 +633,16 @@
         }));
     }
 
+    private void verifyUserReplaced() {
+        verify(mInitialUserSetter).set(argThat((info) -> {
+            return info.type == InitialUserSetter.TYPE_REPLACE_GUEST;
+        }));
+    }
+
+    private void verifyNoSetCall() {
+        verify(mInitialUserSetter, never()).set(notNull());
+    }
+
     private static final class MockDisplayInterface implements DisplayInterface {
         private boolean mDisplayOn = true;
         private final Semaphore mDisplayStateWait = new Semaphore(0);
diff --git a/user/car-user-lib/src/android/car/userlib/InitialUserSetter.java b/user/car-user-lib/src/android/car/userlib/InitialUserSetter.java
index 10b642b..aea4f3d 100644
--- a/user/car-user-lib/src/android/car/userlib/InitialUserSetter.java
+++ b/user/car-user-lib/src/android/car/userlib/InitialUserSetter.java
@@ -89,10 +89,18 @@
      */
     public static final int TYPE_CREATE = 2;
 
+    /**
+     * Creates a new guest user and switches to it, if current user is unlocked guest user.
+     * Does not fallback if any of these steps fails. falling back to
+     * {@link #fallbackDefaultBehavior(String) if any of these steps fails
+     */
+    public static final int TYPE_REPLACE_GUEST = 3;
+
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_DEFAULT_BEHAVIOR,
             TYPE_SWITCH,
             TYPE_CREATE,
+            TYPE_REPLACE_GUEST
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InitialUserInfoType { }
@@ -153,12 +161,11 @@
          * Constructor for the given type.
          *
          * @param type {@link #TYPE_DEFAULT_BEHAVIOR}, {@link #TYPE_SWITCH},
-         * or {@link #TYPE_CREATE}.
+         * {@link #TYPE_CREATE} or {@link #TYPE_REPLACE_GUEST}.
          */
         public Builder(@InitialUserInfoType int type) {
-            Preconditions.checkArgument(
-                    type == TYPE_DEFAULT_BEHAVIOR || type == TYPE_SWITCH || type == TYPE_CREATE,
-                    "invalid builder type");
+            Preconditions.checkArgument(type == TYPE_DEFAULT_BEHAVIOR || type == TYPE_SWITCH
+                    || type == TYPE_CREATE || type == TYPE_REPLACE_GUEST, "invalid builder type");
             mType = type;
         }
 
@@ -289,11 +296,42 @@
                                     + e);
                 }
                 break;
+            case TYPE_REPLACE_GUEST:
+                try {
+                    replaceUser(info, /* fallback= */ true);
+                } catch (Exception e) {
+                    fallbackDefaultBehavior(info, /* fallback= */ true,
+                            "Exception replace guest user: " + e);
+                }
+                break;
             default:
                 throw new IllegalArgumentException("invalid InitialUserInfo type: " + info.type);
         }
     }
 
+    private void replaceUser(InitialUserInfo info, boolean fallback) {
+        int currentUserId = ActivityManager.getCurrentUser();
+        UserInfo currentUser = mUm.getUserInfo(currentUserId);
+
+        UserInfo newUser = replaceGuestIfNeeded(currentUser);
+        if (newUser == null) {
+            fallbackDefaultBehavior(info, fallback,
+                    "could not replace guest " + currentUser.toFullString());
+            return;
+        }
+
+        switchUser(new Builder(TYPE_SWITCH)
+                .setSwitchUserId(newUser.id)
+                .build(), fallback);
+
+        if (newUser.id != currentUser.id) {
+            Slog.i(TAG, "Removing old guest " + currentUser.id);
+            if (!mUm.removeUser(currentUser.id)) {
+                Slog.w(TAG, "Could not remove old guest " + currentUser.id);
+            }
+        }
+    }
+
     private void executeDefaultBehavior(@NonNull InitialUserInfo info, boolean fallback) {
         if (!mHelper.hasInitialUser()) {
             if (DBG) Log.d(TAG, "executeDefaultBehavior(): no initial user, creating it");
@@ -385,6 +423,23 @@
         }
     }
 
+    /**
+     * Check if the user is a guest and can be replaced.
+     */
+    public boolean canReplaceGuestUser(UserInfo user) {
+        if (!user.isGuest()) return false;
+
+        if (mLockPatternUtils.isSecure(user.id)) {
+            if (DBG) {
+                Log.d(TAG, "replaceGuestIfNeeded(), skipped, since user "
+                        + user.id + " has secure lock pattern");
+            }
+            return false;
+        }
+
+        return true;
+    }
+
     // TODO(b/151758646): move to CarUserManagerHelper
     /**
      * Replaces {@code user} by a new guest, if necessary.
@@ -394,17 +449,13 @@
      * <p>Otherwise, it marks the current guest for deletion, creates a new one, and returns the
      * new guest (or {@code null} if a new guest could not be created).
      */
+
+    @VisibleForTesting
     @Nullable
-    public UserInfo replaceGuestIfNeeded(@NonNull UserInfo user) {
+    UserInfo replaceGuestIfNeeded(@NonNull UserInfo user) {
         Preconditions.checkArgument(user != null, "user cannot be null");
 
-        if (!user.isGuest()) return user;
-
-        if (mLockPatternUtils.isSecure(user.id)) {
-            if (DBG) {
-                Log.d(TAG, "replaceGuestIfNeeded(), skipped, since user "
-                        + user.id + " has secure lock pattern");
-            }
+        if (!canReplaceGuestUser(user)) {
             return user;
         }