Merge "Fix b/265632782: Battery usage app list changed when entering the app details page and back"
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index 5befa1f..424d784 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -15,8 +15,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.perftests.multiuser">
-
+    package="com.android.perftests.multiuser">
 
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
@@ -27,6 +26,7 @@
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.SET_WALLPAPER" />
 
     <application>
         <uses-library android:name="android.test.runner" />
@@ -38,5 +38,4 @@
     <queries>
         <package android:name="perftests.multiuser.apps.dummyapp" />
     </queries>
-
 </manifest>
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index b24076a..64ba6d1 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -15,7 +15,11 @@
  */
 package android.multiuser;
 
+import static android.app.WallpaperManager.FLAG_LOCK;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
 import android.annotation.NonNull;
@@ -25,6 +29,7 @@
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
 import android.app.WaitResult;
+import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.IIntentReceiver;
@@ -34,6 +39,7 @@
 import android.content.pm.IPackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IProgressListener;
@@ -59,6 +65,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -114,6 +121,7 @@
     private ActivityManager mAm;
     private IActivityManager mIam;
     private PackageManager mPm;
+    private WallpaperManager mWm;
     private ArrayList<Integer> mUsersToRemove;
     private boolean mHasManagedUserFeature;
     private BroadcastWaiter mBroadcastWaiter;
@@ -132,6 +140,7 @@
         mIam = ActivityManager.getService();
         mUsersToRemove = new ArrayList<>();
         mPm = context.getPackageManager();
+        mWm = WallpaperManager.getInstance(context);
         mHasManagedUserFeature = mPm.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS);
         mBroadcastWaiter = new BroadcastWaiter(context, TAG, TIMEOUT_IN_SECOND,
                 Intent.ACTION_USER_STARTED,
@@ -241,24 +250,27 @@
 
     /**
      * Tests starting an uninitialized user, with wait times in between iterations.
-     * Measures the time until ACTION_USER_STARTED is received.
+     * Measures the time until the ProgressListener callback.
      */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void startUser_realistic() throws RemoteException {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             final int userId = createUserNoFlags();
+            final ProgressWaiter waiter = new ProgressWaiter();
 
             waitForBroadcastIdle();
-            runThenWaitForBroadcasts(userId, () -> {
-                mRunner.resumeTiming();
-                Log.i(TAG, "Starting timer");
+            mRunner.resumeTiming();
+            Log.i(TAG, "Starting timer");
 
-                mIam.startUserInBackground(userId);
-            }, Intent.ACTION_USER_STARTED);
+            final boolean success = mIam.startUserInBackgroundWithListener(userId, waiter)
+                    && waiter.waitForFinish(TIMEOUT_IN_SECOND * 1000);
 
             mRunner.pauseTiming();
             Log.i(TAG, "Stopping timer");
+
+            assertTrue("Error: could not start user " + userId, success);
+
             removeUser(userId);
             waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
@@ -372,6 +384,32 @@
         removeUser(testUser);
     }
 
+    /** Tests switching to a previously-started, but no-longer-running, user with wait
+     * times between iterations and using a static wallpaper */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void switchUser_stopped_staticWallpaper() throws RemoteException {
+        assumeTrue(mWm.isWallpaperSupported() && mWm.isSetWallpaperAllowed());
+        final int startUser = ActivityManager.getCurrentUser();
+        final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true,
+                /* useStaticWallpaper */true);
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            waitCoolDownPeriod();
+            Log.d(TAG, "Starting timer");
+            mRunner.resumeTiming();
+
+            switchUser(testUser);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            switchUserNoCheck(startUser);
+            stopUserAfterWaitingForBroadcastIdle(testUser, true);
+            attestFalse("Failed to stop user " + testUser, mAm.isUserRunning(testUser));
+            mRunner.resumeTimingForNextIteration();
+        }
+        removeUser(testUser);
+    }
+
     /** Tests switching to an already-created already-running non-owner background user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void switchUser_running() throws RemoteException {
@@ -415,6 +453,31 @@
         removeUser(testUser);
     }
 
+    /** Tests switching to an already-created already-running non-owner background user, with wait
+     * times between iterations and using a default static wallpaper */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void switchUser_running_staticWallpaper() throws RemoteException {
+        assumeTrue(mWm.isWallpaperSupported() && mWm.isSetWallpaperAllowed());
+        final int startUser = ActivityManager.getCurrentUser();
+        final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false,
+                /* useStaticWallpaper */ true);
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            waitCoolDownPeriod();
+            Log.d(TAG, "Starting timer");
+            mRunner.resumeTiming();
+
+            switchUser(testUser);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            waitForBroadcastIdle();
+            switchUserNoCheck(startUser);
+            mRunner.resumeTimingForNextIteration();
+        }
+        removeUser(testUser);
+    }
+
     /** Tests stopping a background user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void stopUser() throws RemoteException {
@@ -843,14 +906,20 @@
         waitForLatch("Failed to properly stop user " + userId, latch);
     }
 
+    private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws RemoteException {
+        return initializeNewUserAndSwitchBack(stopNewUser, /* useStaticWallpaper */ false);
+    }
+
     /**
      * Creates a user and waits for its ACTION_USER_UNLOCKED.
      * Then switches to back to the original user and waits for its switchUser() to finish.
      *
      * @param stopNewUser whether to stop the new user after switching to otherUser.
+     * @param useStaticWallpaper whether to switch the wallpaper of the default user to a static.
      * @return userId of the newly created user.
      */
-    private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws RemoteException {
+    private int initializeNewUserAndSwitchBack(boolean stopNewUser, boolean useStaticWallpaper)
+            throws RemoteException {
         final int origUser = mAm.getCurrentUser();
         // First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED
         final int testUser = createUserNoFlags();
@@ -858,6 +927,17 @@
             mAm.switchUser(testUser);
         }, Intent.ACTION_USER_UNLOCKED, Intent.ACTION_MEDIA_MOUNTED);
 
+        if (useStaticWallpaper) {
+            assertTrue(mWm.isWallpaperSupported() && mWm.isSetWallpaperAllowed());
+            try {
+                Bitmap blank = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+                mWm.setBitmap(blank, /* visibleCropHint */ null, /* allowBackup */ true,
+                        /* which */ FLAG_SYSTEM | FLAG_LOCK, testUser);
+            } catch (IOException exception) {
+                fail("Unable to set static wallpaper.");
+            }
+        }
+
         // Second, switch back to origUser, waiting merely for switchUser() to finish
         switchUser(origUser);
         attestTrue("Didn't switch back to user, " + origUser, origUser == mAm.getCurrentUser());
diff --git a/core/api/current.txt b/core/api/current.txt
index bec2559..4b1335e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40530,11 +40530,12 @@
     method public void onLaunchVoiceAssistFromKeyguard();
     method public void onPrepareToShowSession(@NonNull android.os.Bundle, int);
     method public void onReady();
-    method public void onShowSessionFailed();
+    method public void onShowSessionFailed(@NonNull android.os.Bundle);
     method public void onShutdown();
     method public void setDisabledShowContext(int);
     method public final void setUiHints(@NonNull android.os.Bundle);
     method public void showSession(android.os.Bundle, int);
+    field public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
     field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
     field public static final String SERVICE_META_DATA = "android.voice_interaction";
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 684b271..0fce784 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10403,6 +10403,7 @@
     method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public boolean isUserVisible();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int removeUserWhenPossible(@NonNull android.os.UserHandle, boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public void setBootUser(@NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 07750a5..8c64e40 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -528,6 +528,7 @@
     method @NonNull public static String operationSafetyReasonToString(int);
     method @NonNull public static String operationToString(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void resetShouldAllowBypassingDevicePolicyManagementRoleQualificationState();
     method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, int);
@@ -897,6 +898,7 @@
     method public boolean isDemo();
     method public boolean isEnabled();
     method public boolean isEphemeral();
+    method public boolean isForTesting();
     method public boolean isFull();
     method public boolean isGuest();
     method public boolean isInitialized();
@@ -2025,6 +2027,7 @@
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getBootUser();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index dbdee07..821a23c 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,6 +16,8 @@
 
 package android.accounts;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
 import android.annotation.BroadcastBehavior;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -891,15 +893,24 @@
      * @return An {@link AccountManagerFuture} which resolves to a Boolean, true if the account
      *         exists and has all of the specified features.
      */
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+            requiresPermissionIfNotCaller = INTERACT_ACROSS_USERS_FULL)
     public AccountManagerFuture<Boolean> hasFeatures(final Account account,
             final String[] features,
             AccountManagerCallback<Boolean> callback, Handler handler) {
+        return hasFeaturesAsUser(account, features, callback, handler, mContext.getUserId());
+    }
+
+    private AccountManagerFuture<Boolean> hasFeaturesAsUser(
+            final Account account, final String[] features,
+            AccountManagerCallback<Boolean> callback, Handler handler, int userId) {
         if (account == null) throw new IllegalArgumentException("account is null");
         if (features == null) throw new IllegalArgumentException("features is null");
         return new Future2Task<Boolean>(handler, callback) {
             @Override
             public void doWork() throws RemoteException {
-                mService.hasFeatures(mResponse, account, features, mContext.getOpPackageName());
+                mService.hasFeatures(
+                        mResponse, account, features, userId, mContext.getOpPackageName());
             }
             @Override
             public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
@@ -3319,7 +3330,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
     public AccountManagerFuture<Bundle> finishSessionAsUser(
             final Bundle sessionBundle,
             final Activity activity,
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index a3a7b0c..08fb308 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -38,7 +38,7 @@
     Account[] getAccountsByTypeForPackage(String type, String packageName, String opPackageName);
     Account[] getAccountsAsUser(String accountType, int userId, String opPackageName);
     void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features,
-        String opPackageName);
+        int userId, String opPackageName);
     void getAccountByTypeAndFeatures(in IAccountManagerResponse response, String accountType,
         in String[] features, String opPackageName);
     void getAccountsByFeatures(in IAccountManagerResponse response, String accountType,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1633073..33721a0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.QUERY_ADMIN_POLICY;
 import static android.Manifest.permission.SET_TIME;
 import static android.Manifest.permission.SET_TIME_ZONE;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
 import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
 
@@ -4186,7 +4187,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
     public boolean packageHasActiveAdmins(String packageName) {
         return packageHasActiveAdmins(packageName, myUserId());
     }
@@ -8743,7 +8744,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @RequiresPermission(allOf = {
             android.Manifest.permission.MANAGE_DEVICE_ADMINS,
-            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+            INTERACT_ACROSS_USERS_FULL
     })
     public void setActiveAdmin(@NonNull ComponentName policyReceiver, boolean refreshing,
             int userHandle) {
@@ -10654,7 +10655,7 @@
      */
     @UserHandleAware
     @RequiresPermission(allOf = {
-            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            INTERACT_ACROSS_USERS_FULL,
             android.Manifest.permission.MANAGE_USERS
             }, conditional = true)
     public @Nullable List<String> getPermittedInputMethods() {
@@ -14845,7 +14846,7 @@
      * @hide
      */
     @RequiresPermission(anyOf = {
-            permission.INTERACT_ACROSS_USERS_FULL,
+            INTERACT_ACROSS_USERS_FULL,
             permission.INTERACT_ACROSS_USERS
     }, conditional = true)
     public boolean isPackageAllowedToAccessCalendar(@NonNull  String packageName) {
@@ -14877,7 +14878,7 @@
      * @hide
      */
     @RequiresPermission(anyOf = {
-            permission.INTERACT_ACROSS_USERS_FULL,
+            INTERACT_ACROSS_USERS_FULL,
             permission.INTERACT_ACROSS_USERS
     })
     public @Nullable Set<String> getCrossProfileCalendarPackages() {
@@ -14970,7 +14971,7 @@
      * @hide
      */
     @RequiresPermission(anyOf = {
-            permission.INTERACT_ACROSS_USERS_FULL,
+            INTERACT_ACROSS_USERS_FULL,
             permission.INTERACT_ACROSS_USERS,
             permission.INTERACT_ACROSS_PROFILES
     })
@@ -16154,6 +16155,23 @@
     }
 
     /**
+     * Reset cache for {@link #shouldAllowBypassingDevicePolicyManagementRoleQualification}.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+    public void resetShouldAllowBypassingDevicePolicyManagementRoleQualificationState() {
+        if (mService != null) {
+            try {
+                mService.resetShouldAllowBypassingDevicePolicyManagementRoleQualificationState();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * @return {@code true} if bypassing the device policy management role qualification is allowed
      * with the current state of the device.
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 20695ca..aebeaf0 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -575,6 +575,7 @@
     void resetStrings(in List<String> stringIds);
     ParcelableResource getString(String stringId);
 
+    void resetShouldAllowBypassingDevicePolicyManagementRoleQualificationState();
     boolean shouldAllowBypassingDevicePolicyManagementRoleQualification();
 
     List<UserHandle> getPolicyManagedProfiles(in UserHandle userHandle);
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 2be0323..44747fa 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -171,6 +171,16 @@
     public static final int FLAG_MAIN = 0x00004000;
 
     /**
+     * Indicates that this user was created for the purposes of testing.
+     *
+     * <p>These users are subject to removal during tests and should not be used on actual devices
+     * used by humans.
+     *
+     * @hide
+     */
+    public static final int FLAG_FOR_TESTING = 0x00008000;
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = "FLAG_", value = {
@@ -188,7 +198,8 @@
             FLAG_SYSTEM,
             FLAG_PROFILE,
             FLAG_EPHEMERAL_ON_CREATE,
-            FLAG_MAIN
+            FLAG_MAIN,
+            FLAG_FOR_TESTING
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserInfoFlag {
@@ -369,6 +380,12 @@
         return (flags & FLAG_EPHEMERAL) == FLAG_EPHEMERAL;
     }
 
+    /** @hide */
+    @TestApi
+    public boolean isForTesting() {
+        return (flags & FLAG_FOR_TESTING) == FLAG_FOR_TESTING;
+    }
+
     public boolean isInitialized() {
         return (flags & FLAG_INITIALIZED) == FLAG_INITIALIZED;
     }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 3b4e8cd..d1d3315 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -142,4 +142,8 @@
     long getUserStartRealtime();
     long getUserUnlockRealtime();
     boolean setUserEphemeral(int userId, boolean enableEphemeral);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS})")
+    void setBootUser(int userId);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS})")
+    int getBootUser();
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b016c781..9db1233 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5671,6 +5671,40 @@
         }
     }
 
+    /**
+     * Sets the user who should be in the foreground when boot completes. This should be called
+     * during boot, and the provided user must be a full user (i.e. not a profile).
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public void setBootUser(@NonNull UserHandle bootUser) {
+        try {
+            mService.setBootUser(bootUser.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the user who should be in the foreground when boot completes.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    @SuppressWarnings("[AndroidFrameworkContextUserId]")
+    public @NonNull UserHandle getBootUser() {
+        try {
+            return UserHandle.of(mService.getBootUser());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
     /* Cache key for anything that assumes that userIds cannot be re-used without rebooting. */
     private static final String CACHE_KEY_STATIC_USER_PROPERTIES = "cache_key.static_user_props";
 
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index efae5c1..6a54606 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -31,5 +31,5 @@
     void getActiveServiceSupportedActions(in List<String> voiceActions,
      in IVoiceActionCheckCallback callback);
     void prepareToShowSession(in Bundle args, int flags);
-    void showSessionFailed();
+    void showSessionFailed(in Bundle args);
 }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index a5156ef..df739e3 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -95,6 +95,20 @@
     public static final String SERVICE_META_DATA = "android.voice_interaction";
 
     /**
+     * Bundle key used to specify the id when the system prepares to show session. It increases for
+     * each request.
+     * <p>
+     * Type: int
+     * </p>
+     * @see #showSession(Bundle, int)
+     * @see #onPrepareToShowSession(Bundle, int)
+     * @see #onShowSessionFailed(Bundle)
+     * @see VoiceInteractionSession#onShow(Bundle, int)
+     * @see VoiceInteractionSession#show(Bundle, int)
+     */
+    public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
+
+    /**
      * For apps targeting Build.VERSION_CODES.TRAMISU and above, implementors of this
      * service can create multiple AlwaysOnHotwordDetector instances in parallel. They will
      * also e ale to create a single SoftwareHotwordDetector in parallel with any other
@@ -170,10 +184,10 @@
         }
 
         @Override
-        public void showSessionFailed() {
+        public void showSessionFailed(@NonNull Bundle args) {
             Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
                     VoiceInteractionService::onShowSessionFailed,
-                    VoiceInteractionService.this));
+                    VoiceInteractionService.this, args));
         }
     };
 
@@ -205,9 +219,10 @@
      * bind the session service.
      *
      * @param args  The arguments that were supplied to {@link #showSession(Bundle, int)}.
+     *              It always includes {@link #KEY_SHOW_SESSION_ID}.
      * @param flags The show flags originally provided to {@link #showSession(Bundle, int)}.
      * @see #showSession(Bundle, int)
-     * @see #onShowSessionFailed()
+     * @see #onShowSessionFailed(Bundle)
      * @see VoiceInteractionSession#onShow(Bundle, int)
      * @see VoiceInteractionSession#show(Bundle, int)
      */
@@ -217,12 +232,14 @@
     /**
      * Called when the show session failed. E.g. When the system bound the session service failed.
      *
+     * @param args Additional info about the show session attempt that failed. For now, includes
+     *             {@link #KEY_SHOW_SESSION_ID}.
      * @see #showSession(Bundle, int)
      * @see #onPrepareToShowSession(Bundle, int)
      * @see VoiceInteractionSession#onShow(Bundle, int)
      * @see VoiceInteractionSession#show(Bundle, int)
      */
-    public void onShowSessionFailed() {
+    public void onShowSessionFailed(@NonNull Bundle args) {
     }
 
     /**
@@ -719,8 +736,8 @@
 
     private void onHotwordDetectorDestroyed(@NonNull HotwordDetector detector) {
         synchronized (mLock) {
-            if (mActiveVisualQueryDetector!= null &&
-                    detector == mActiveVisualQueryDetector.getInitializationDelegate()) {
+            if (mActiveVisualQueryDetector != null
+                    && detector == mActiveVisualQueryDetector.getInitializationDelegate()) {
                 mActiveVisualQueryDetector = null;
             }
             mActiveDetectors.remove(detector);
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 2c70229..d55fede 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1763,7 +1763,8 @@
      * @param args The arguments that were supplied to
      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
      * Some example keys include : "invocation_type", "invocation_phone_state",
-     * "invocation_time_ms", Intent.EXTRA_TIME ("android.intent.extra.TIME") indicating timing
+     * {@link VoiceInteractionService#KEY_SHOW_SESSION_ID}, "invocation_time_ms",
+     * Intent.EXTRA_TIME ("android.intent.extra.TIME") indicating timing
      * in milliseconds of the KeyEvent that triggered Assistant and
      * Intent.EXTRA_ASSIST_INPUT_DEVICE_ID (android.intent.extra.ASSIST_INPUT_DEVICE_ID)
      *  referring to the device that sent the request.
diff --git a/core/java/com/android/internal/config/appcloning/AppCloningDeviceConfigHelper.java b/core/java/com/android/internal/config/appcloning/AppCloningDeviceConfigHelper.java
new file mode 100644
index 0000000..ddd3d2c
--- /dev/null
+++ b/core/java/com/android/internal/config/appcloning/AppCloningDeviceConfigHelper.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.internal.config.appcloning;
+
+import android.content.Context;
+import android.provider.DeviceConfig;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Helper class that holds the flags related to the app_cloning namespace in {@link DeviceConfig}.
+ *
+ * @hide
+ */
+public class AppCloningDeviceConfigHelper {
+
+    @GuardedBy("sLock")
+    private static AppCloningDeviceConfigHelper sInstance;
+
+    private static final Object sLock = new Object();
+
+    private DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangeListener;
+
+    /**
+     * This flag is defined inside {@link DeviceConfig#NAMESPACE_APP_CLONING}. Please check
+     * {@link #mEnableAppCloningBuildingBlocks} for details.
+     */
+    public static final String ENABLE_APP_CLONING_BUILDING_BLOCKS =
+            "enable_app_cloning_building_blocks";
+
+    /**
+     * Checks whether the support for app-cloning building blocks (like contacts
+     * sharing/intent redirection), which are available starting from the U release, is turned on.
+     * The default value is true to ensure the features are always enabled going forward.
+     *
+     * TODO:(b/253449368) Add information about the app-cloning config and mention that the devices
+     * that do not support app-cloning should use the app-cloning config to disable all app-cloning
+     * features.
+     */
+    private volatile boolean mEnableAppCloningBuildingBlocks = true;
+
+    private AppCloningDeviceConfigHelper() {}
+
+    /**
+     * @hide
+     */
+    public static AppCloningDeviceConfigHelper getInstance(Context context) {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new AppCloningDeviceConfigHelper();
+                sInstance.init(context);
+            }
+            return sInstance;
+        }
+    }
+
+    private void init(Context context) {
+        initializeDeviceConfigChangeListener();
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_CLONING,
+                context.getMainExecutor(),
+                mDeviceConfigChangeListener);
+    }
+
+    private void initializeDeviceConfigChangeListener() {
+        mDeviceConfigChangeListener = properties -> {
+            if (!DeviceConfig.NAMESPACE_APP_CLONING.equals(properties.getNamespace())) {
+                return;
+            }
+            for (String name : properties.getKeyset()) {
+                if (name == null) {
+                    return;
+                }
+                if (ENABLE_APP_CLONING_BUILDING_BLOCKS.equals(name)) {
+                    updateEnableAppCloningBuildingBlocks();
+                }
+            }
+        };
+    }
+
+    private void updateEnableAppCloningBuildingBlocks() {
+        mEnableAppCloningBuildingBlocks = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_APP_CLONING, ENABLE_APP_CLONING_BUILDING_BLOCKS, true);
+    }
+
+    /**
+     * Fetch the feature flag to check whether the support for the app-cloning building blocks
+     * (like contacts sharing/intent redirection) is enabled on the device.
+     * @hide
+     */
+    public boolean getEnableAppCloningBuildingBlocks() {
+        return mEnableAppCloningBuildingBlocks;
+    }
+}
diff --git a/core/java/com/android/internal/config/appcloning/OWNERS b/core/java/com/android/internal/config/appcloning/OWNERS
new file mode 100644
index 0000000..0645a8c5
--- /dev/null
+++ b/core/java/com/android/internal/config/appcloning/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1207885
+jigarthakkar@google.com
+saumyap@google.com
\ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 07d0158..c5d932e 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -79,6 +79,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+import androidx.window.extensions.core.util.function.Function;
 import androidx.window.extensions.layout.WindowLayoutComponentImpl;
 import androidx.window.extensions.layout.WindowLayoutInfo;
 
@@ -563,10 +564,10 @@
                                 SplitAttributes.SplitType.RatioSplitType.splitEqually()
                         )
                 ).build();
+        final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
+                params -> splitAttributes;
 
-        mController.setSplitAttributesCalculator(params -> {
-            return splitAttributes;
-        });
+        mController.setSplitAttributesCalculator(calculator);
 
         assertEquals(splitAttributes, mPresenter.computeSplitAttributes(taskProperties,
                 splitPairRule, null /* minDimensionsPair */));
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 5de5365..cddbf469 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 8121b20..e85b3c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -232,10 +232,13 @@
 
                     if (mBubble.isAppBubble()) {
                         PendingIntent pi = PendingIntent.getActivity(mContext, 0,
-                                mBubble.getAppBubbleIntent(),
-                                PendingIntent.FLAG_MUTABLE,
+                                mBubble.getAppBubbleIntent()
+                                        .addFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+                                        .addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
+                                PendingIntent.FLAG_IMMUTABLE,
                                 null);
-                        mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
+                        mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
+                                launchBounds);
                     } else if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
                         options.setApplyActivityFlagsForBubbles(true);
                         mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index fcbf9e0..fa3a6ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -78,6 +78,7 @@
     private final Rect mResizingBounds = new Rect();
     private final Rect mTempRect = new Rect();
     private ValueAnimator mFadeAnimator;
+    private ValueAnimator mScreenshotAnimator;
 
     private int mIconSize;
     private int mOffsetX;
@@ -135,8 +136,17 @@
 
     /** Releases the surfaces for split decor. */
     public void release(SurfaceControl.Transaction t) {
-        if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
-            mFadeAnimator.cancel();
+        if (mFadeAnimator != null) {
+            if (mFadeAnimator.isRunning()) {
+                mFadeAnimator.cancel();
+            }
+            mFadeAnimator = null;
+        }
+        if (mScreenshotAnimator != null) {
+            if (mScreenshotAnimator.isRunning()) {
+                mScreenshotAnimator.cancel();
+            }
+            mScreenshotAnimator = null;
         }
         if (mViewHost != null) {
             mViewHost.release();
@@ -238,16 +248,20 @@
     /** Stops showing resizing hint. */
     public void onResized(SurfaceControl.Transaction t, Runnable animFinishedCallback) {
         if (mScreenshot != null) {
+            if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
+                mScreenshotAnimator.cancel();
+            }
+
             t.setPosition(mScreenshot, mOffsetX, mOffsetY);
 
             final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
-            final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
-            va.addUpdateListener(valueAnimator -> {
+            mScreenshotAnimator = ValueAnimator.ofFloat(1, 0);
+            mScreenshotAnimator.addUpdateListener(valueAnimator -> {
                 final float progress = (float) valueAnimator.getAnimatedValue();
                 animT.setAlpha(mScreenshot, progress);
                 animT.apply();
             });
-            va.addListener(new AnimatorListenerAdapter() {
+            mScreenshotAnimator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationStart(Animator animation) {
                     mRunningAnimationCount++;
@@ -266,7 +280,7 @@
                     }
                 }
             });
-            va.start();
+            mScreenshotAnimator.start();
         }
 
         if (mResizingIconView == null) {
@@ -292,9 +306,6 @@
                 });
                 return;
             }
-
-            // If fade-in animation is running, cancel it and re-run fade-out one.
-            mFadeAnimator.cancel();
         }
         if (mShown) {
             fadeOutDecor(animFinishedCallback);
@@ -332,6 +343,11 @@
      * directly. */
     public void fadeOutDecor(Runnable finishedCallback) {
         if (mShown) {
+            // If previous animation is running, just cancel it.
+            if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
+                mFadeAnimator.cancel();
+            }
+
             startFadeAnimation(false /* show */, true, finishedCallback);
             mShown = false;
         } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index b189163..8d4a384 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -125,7 +125,9 @@
             cancelScheduledPlacement();
             applyPlacement(placement, shouldStash, animationDuration);
         } else {
-            applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
+            if (mCurrentPlacementBounds != null) {
+                applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
+            }
             schedulePinnedStackPlacement(placement, animationDuration);
         }
     }
@@ -188,7 +190,7 @@
         applyPlacementBounds(bounds, animationDuration);
     }
 
-    void onPipDismissed() {
+    void reset() {
         mCurrentPlacementBounds = null;
         mPipTargetBounds = null;
         cancelScheduledPlacement();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index fd4fcff..4426423 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -450,18 +450,6 @@
         mPipMediaController.registerSessionListenerForCurrentUser();
     }
 
-    private void checkIfPinnedTaskAppeared() {
-        final TaskInfo pinnedTask = getPinnedTaskInfo();
-        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "%s: checkIfPinnedTaskAppeared(), task=%s", TAG, pinnedTask);
-        if (pinnedTask == null || pinnedTask.topActivity == null) return;
-        mPinnedTaskId = pinnedTask.taskId;
-
-        mPipMediaController.onActivityPinned();
-        mActionBroadcastReceiver.register();
-        mPipNotificationController.show(pinnedTask.topActivity.getPackageName());
-    }
-
     private void checkIfPinnedTaskIsGone() {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "%s: onTaskStackChanged()", TAG);
@@ -482,7 +470,7 @@
 
         mTvPipMenuController.closeMenu();
         mTvPipBoundsState.resetTvPipState();
-        mTvPipBoundsController.onPipDismissed();
+        mTvPipBoundsController.reset();
         setState(STATE_NO_PIP);
         mPinnedTaskId = NONEXISTENT_TASK_ID;
     }
@@ -537,7 +525,16 @@
         taskStackListener.addListener(new TaskStackListenerCallback() {
             @Override
             public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
-                checkIfPinnedTaskAppeared();
+                final TaskInfo pinnedTask = getPinnedTaskInfo();
+                ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                        "%s: onActivityPinned(), task=%s", TAG, pinnedTask);
+                if (pinnedTask == null || pinnedTask.topActivity == null) return;
+                mPinnedTaskId = pinnedTask.taskId;
+
+                mPipMediaController.onActivityPinned();
+                mActionBroadcastReceiver.register();
+                mPipNotificationController.show(pinnedTask.topActivity.getPackageName());
+                mTvPipBoundsController.reset();
                 mAppOpsListener.onActivityPinned(packageName);
             }
 
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 98819a3..0198419 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -28,11 +28,11 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * @hide
@@ -140,7 +140,7 @@
      */
     public static int getLegacyStreamTypeForStrategyWithAudioAttributes(
             @NonNull AudioAttributes audioAttributes) {
-        Preconditions.checkNotNull(audioAttributes, "AudioAttributes must not be null");
+        Objects.requireNonNull(audioAttributes, "AudioAttributes must not be null");
         for (final AudioProductStrategy productStrategy :
                 AudioProductStrategy.getAudioProductStrategies()) {
             if (productStrategy.supportsAudioAttributes(audioAttributes)) {
@@ -160,6 +160,30 @@
         return AudioSystem.STREAM_MUSIC;
     }
 
+    /**
+     * @hide
+     * @param attributes the {@link AudioAttributes} to identify VolumeGroupId with
+     * @param fallbackOnDefault if set, allows to fallback on the default group (e.g. the group
+     *                          associated to {@link AudioManager#STREAM_MUSIC}).
+     * @return volume group id associated with the given {@link AudioAttributes} if found,
+     *     default volume group id if fallbackOnDefault is set
+     * <p>By convention, the product strategy with default attributes will be associated to the
+     * default volume group (e.g. associated to {@link AudioManager#STREAM_MUSIC})
+     * or {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} if not found.
+     */
+    public static int getVolumeGroupIdForAudioAttributes(
+            @NonNull AudioAttributes attributes, boolean fallbackOnDefault) {
+        Objects.requireNonNull(attributes, "attributes must not be null");
+        int volumeGroupId = getVolumeGroupIdForAudioAttributesInt(attributes);
+        if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+            return volumeGroupId;
+        }
+        if (fallbackOnDefault) {
+            return getVolumeGroupIdForAudioAttributesInt(getDefaultAttributes());
+        }
+        return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
+    }
+
     private static List<AudioProductStrategy> initializeAudioProductStrategies() {
         ArrayList<AudioProductStrategy> apsList = new ArrayList<AudioProductStrategy>();
         int status = native_list_audio_product_strategies(apsList);
@@ -190,8 +214,8 @@
      */
     private AudioProductStrategy(@NonNull String name, int id,
             @NonNull AudioAttributesGroup[] aag) {
-        Preconditions.checkNotNull(name, "name must not be null");
-        Preconditions.checkNotNull(aag, "AudioAttributesGroups must not be null");
+        Objects.requireNonNull(name, "name must not be null");
+        Objects.requireNonNull(aag, "AudioAttributesGroups must not be null");
         mName = name;
         mId = id;
         mAudioAttributesGroups = aag;
@@ -241,7 +265,7 @@
      */
     @TestApi
     public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
-        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        Objects.requireNonNull(aa, "AudioAttributes must not be null");
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
             if (aag.supportsAttributes(aa)) {
                 return aag.getStreamType();
@@ -258,7 +282,7 @@
      */
     @SystemApi
     public boolean supportsAudioAttributes(@NonNull AudioAttributes aa) {
-        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        Objects.requireNonNull(aa, "AudioAttributes must not be null");
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
             if (aag.supportsAttributes(aa)) {
                 return true;
@@ -291,7 +315,7 @@
      */
     @TestApi
     public int getVolumeGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
-        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        Objects.requireNonNull(aa, "AudioAttributes must not be null");
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
             if (aag.supportsAttributes(aa)) {
                 return aag.getVolumeGroupId();
@@ -300,6 +324,17 @@
         return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
     }
 
+    private static int getVolumeGroupIdForAudioAttributesInt(@NonNull AudioAttributes attributes) {
+        Objects.requireNonNull(attributes, "attributes must not be null");
+        for (AudioProductStrategy productStrategy : getAudioProductStrategies()) {
+            int volumeGroupId = productStrategy.getVolumeGroupIdForAudioAttributes(attributes);
+            if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+                return volumeGroupId;
+            }
+        }
+        return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -374,8 +409,8 @@
      */
     private static boolean attributesMatches(@NonNull AudioAttributes refAttr,
             @NonNull AudioAttributes attr) {
-        Preconditions.checkNotNull(refAttr, "refAttr must not be null");
-        Preconditions.checkNotNull(attr, "attr must not be null");
+        Objects.requireNonNull(refAttr, "reference AudioAttributes must not be null");
+        Objects.requireNonNull(attr, "requester's AudioAttributes must not be null");
         String refFormattedTags = TextUtils.join(";", refAttr.getTags());
         String cliFormattedTags = TextUtils.join(";", attr.getTags());
         if (refAttr.equals(DEFAULT_ATTRIBUTES)) {
diff --git a/media/java/android/media/projection/TEST_MAPPING b/media/java/android/media/projection/TEST_MAPPING
new file mode 100644
index 0000000..a792498
--- /dev/null
+++ b/media/java/android/media/projection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "MediaProjectionTests",
+      "options": [
+        {
+          "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/media/tests/projection/TEST_MAPPING b/media/tests/projection/TEST_MAPPING
deleted file mode 100644
index ddb68af..0000000
--- a/media/tests/projection/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-    "presubmit": [
-        {
-            "name": "FrameworksServicesTests",
-            "options": [
-                {"include-filter": "android.media.projection.mediaprojectiontests"},
-                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
-                {"exclude-annotation": "androidx.test.filters.FlakyTest"},
-                {"exclude-annotation": "org.junit.Ignore"}
-            ]
-        }
-    ]
-}
diff --git a/packages/SettingsLib/HelpUtils/Android.bp b/packages/SettingsLib/HelpUtils/Android.bp
index aea51b1..3ec4366a 100644
--- a/packages/SettingsLib/HelpUtils/Android.bp
+++ b/packages/SettingsLib/HelpUtils/Android.bp
@@ -22,5 +22,6 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.permission",
+        "com.android.healthconnect",
     ],
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 90d3488..115cf792 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -343,6 +343,7 @@
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
+    <protected-broadcast android:name="com.android.systemui.STARTED" />
 
     <application
         android:name=".SystemUIApplication"
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 259f0ed..4e96dda 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -41,6 +41,7 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCompositionContext
@@ -73,6 +74,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.ViewTreeLifecycleOwner
 import androidx.lifecycle.ViewTreeViewModelStoreOwner
+import com.android.compose.runtime.movableContentOf
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.LaunchAnimator
 import kotlin.math.max
@@ -170,25 +172,25 @@
     val contentColor = controller.contentColor
     val shape = controller.shape
 
-    // TODO(b/230830644): Use movableContentOf to preserve the content state instead once the
-    // Compose libraries have been updated and include aosp/2163631.
     val wrappedContent =
-        @Composable { controller: ExpandableController ->
-            CompositionLocalProvider(
-                LocalContentColor provides contentColor,
-            ) {
-                // We make sure that the content itself (wrapped by the background) is at least
-                // 40.dp, which is the same as the M3 buttons. This applies even if onClick is
-                // null, to make it easier to write expandables that are sometimes clickable and
-                // sometimes not. There shouldn't be any Expandable smaller than 40dp because if
-                // the expandable is not clickable directly, then something in its content should
-                // be (and with a size >= 40dp).
-                val minSize = 40.dp
-                Box(
-                    Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
-                    contentAlignment = Alignment.Center,
+        remember(content) {
+            movableContentOf { expandable: Expandable ->
+                CompositionLocalProvider(
+                    LocalContentColor provides contentColor,
                 ) {
-                    content(controller.expandable)
+                    // We make sure that the content itself (wrapped by the background) is at least
+                    // 40.dp, which is the same as the M3 buttons. This applies even if onClick is
+                    // null, to make it easier to write expandables that are sometimes clickable and
+                    // sometimes not. There shouldn't be any Expandable smaller than 40dp because if
+                    // the expandable is not clickable directly, then something in its content
+                    // should be (and with a size >= 40dp).
+                    val minSize = 40.dp
+                    Box(
+                        Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+                        contentAlignment = Alignment.Center,
+                    ) {
+                        content(expandable)
+                    }
                 }
             }
         }
@@ -270,7 +272,7 @@
                     .onGloballyPositioned {
                         controller.boundsInComposeViewRoot.value = it.boundsInRoot()
                     }
-            ) { wrappedContent(controller) }
+            ) { wrappedContent(controller.expandable) }
         }
         else -> {
             val clickModifier =
@@ -301,7 +303,7 @@
                         controller.boundsInComposeViewRoot.value = it.boundsInRoot()
                     },
             ) {
-                wrappedContent(controller)
+                wrappedContent(controller.expandable)
             }
         }
     }
@@ -315,7 +317,7 @@
     animatorState: State<LaunchAnimator.State?>,
     overlay: ViewGroupOverlay,
     controller: ExpandableControllerImpl,
-    content: @Composable (ExpandableController) -> Unit,
+    content: @Composable (Expandable) -> Unit,
     composeViewRoot: View,
     onOverlayComposeViewChanged: (View?) -> Unit,
     density: Density,
@@ -370,7 +372,7 @@
                             // We center the content in the expanding container.
                             contentAlignment = Alignment.Center,
                         ) {
-                            Box(contentModifier) { content(controller) }
+                            Box(contentModifier) { content(controller.expandable) }
                         }
                     }
                 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
index 24ae42a..fe607e1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
@@ -19,7 +19,7 @@
 import com.android.systemui.dagger.qualifiers.UiBackground
 import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldBackground
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.updates.FoldProvider
 import com.android.systemui.unfold.util.CurrentActivityTypeProvider
@@ -56,6 +56,6 @@
     abstract fun mainHandler(@Main handler: Handler): Handler
 
     @Binds
-    @UnfoldBackground
+    @UnfoldSingleThreadBg
     abstract fun backgroundExecutor(@UiBackground executor: Executor): Executor
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f60f9a1..a5d9fee 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -411,6 +411,17 @@
     val WM_DESKTOP_WINDOWING_2 =
         sysPropBooleanFlag(1112, "persist.wm.debug.desktop_mode_2", default = false)
 
+    // TODO(b/254513207): Tracking Bug to delete
+    @Keep
+    @JvmField
+    val WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES =
+        unreleasedFlag(
+            1113,
+            name = "screen_record_enterprise_policies",
+            namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+            teamfood = false
+        )
+
     // 1200 - predictive back
     @Keep
     @JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index ceb4845..a692ad7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -18,6 +18,7 @@
 import android.app.ActivityOptions
 import android.content.Intent
 import android.content.res.Configuration
+import android.content.res.Resources
 import android.media.projection.IMediaProjection
 import android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION
 import android.os.Binder
@@ -27,6 +28,7 @@
 import android.os.UserHandle
 import android.view.ViewGroup
 import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider
 import com.android.internal.app.ChooserActivity
 import com.android.internal.app.ResolverListController
 import com.android.internal.app.chooser.NotSelectableTargetInfo
@@ -59,16 +61,12 @@
     private lateinit var configurationController: ConfigurationController
     private lateinit var controller: MediaProjectionAppSelectorController
     private lateinit var recentsViewController: MediaProjectionRecentsViewController
+    private lateinit var component: MediaProjectionAppSelectorComponent
 
     override fun getLayoutResource() = R.layout.media_projection_app_selector
 
     public override fun onCreate(bundle: Bundle?) {
-        val component =
-            componentFactory.create(
-                activity = this,
-                view = this,
-                resultHandler = this
-            )
+        component = componentFactory.create(activity = this, view = this, resultHandler = this)
 
         // Create a separate configuration controller for this activity as the configuration
         // might be different from the global one
@@ -76,11 +74,12 @@
         controller = component.controller
         recentsViewController = component.recentsViewController
 
-        val queryIntent = Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
-        intent.putExtra(Intent.EXTRA_INTENT, queryIntent)
+        intent.configureChooserIntent(
+            resources,
+            component.hostUserHandle,
+            component.personalProfileUserHandle
+        )
 
-        val title = getString(R.string.media_projection_permission_app_selector_title)
-        intent.putExtra(Intent.EXTRA_TITLE, title)
         super.onCreate(bundle)
         controller.init()
     }
@@ -183,6 +182,13 @@
 
     override fun shouldShowContentPreview() = true
 
+    override fun shouldShowContentPreviewWhenEmpty(): Boolean = true
+
+    override fun createMyUserIdProvider(): MyUserIdProvider =
+        object : MyUserIdProvider() {
+            override fun getMyUserId(): Int = component.hostUserHandle.identifier
+        }
+
     override fun createContentPreviewView(parent: ViewGroup): ViewGroup =
         recentsViewController.createView(parent)
 
@@ -193,6 +199,34 @@
          * instance through activity result.
          */
         const val EXTRA_CAPTURE_REGION_RESULT_RECEIVER = "capture_region_result_receiver"
+
+        /** UID of the app that originally launched the media projection flow (host app user) */
+        const val EXTRA_HOST_APP_USER_HANDLE = "launched_from_user_handle"
         const val KEY_CAPTURE_TARGET = "capture_region"
+
+        /** Set up intent for the [ChooserActivity] */
+        private fun Intent.configureChooserIntent(
+            resources: Resources,
+            hostUserHandle: UserHandle,
+            personalProfileUserHandle: UserHandle
+        ) {
+            // Specify the query intent to show icons for all apps on the chooser screen
+            val queryIntent =
+                Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
+            putExtra(Intent.EXTRA_INTENT, queryIntent)
+
+            // Update the title of the chooser
+            val title = resources.getString(R.string.media_projection_permission_app_selector_title)
+            putExtra(Intent.EXTRA_TITLE, title)
+
+            // Select host app's profile tab by default
+            val selectedProfile =
+                if (hostUserHandle == personalProfileUserHandle) {
+                    PROFILE_PERSONAL
+                } else {
+                    PROFILE_WORK
+                }
+            putExtra(EXTRA_SELECTED_PROFILE, selectedProfile)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index bfa67a8..d830fc4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -22,6 +22,7 @@
 import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -35,6 +36,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.text.BidiFormatter;
 import android.text.SpannableString;
 import android.text.TextPaint;
@@ -208,8 +210,14 @@
                 final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class);
                 intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
                         projection.asBinder());
+                intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
+                        UserHandle.getUserHandleForUid(getLaunchedFromUid()));
                 intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
-                startActivity(intent);
+
+                // Start activity from the current foreground user to avoid creating a separate
+                // SystemUI process without access to recent tasks because it won't have
+                // WM Shell running inside.
+                startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error granting projection permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 6c41caa..1d86343 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -19,9 +19,11 @@
 import android.app.Activity
 import android.content.ComponentName
 import android.content.Context
+import android.os.UserHandle
 import com.android.launcher3.icons.IconFactory
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
 import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
 import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader
@@ -30,6 +32,8 @@
 import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
 import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
 import com.android.systemui.statusbar.policy.ConfigurationController
 import dagger.Binds
@@ -39,6 +43,7 @@
 import dagger.Subcomponent
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import java.lang.IllegalArgumentException
 import javax.inject.Qualifier
 import javax.inject.Scope
 import kotlinx.coroutines.CoroutineScope
@@ -46,6 +51,12 @@
 
 @Qualifier @Retention(AnnotationRetention.BINARY) annotation class MediaProjectionAppSelector
 
+@Qualifier @Retention(AnnotationRetention.BINARY) annotation class HostUserHandle
+
+@Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile
+
+@Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile
+
 @Retention(AnnotationRetention.RUNTIME) @Scope annotation class MediaProjectionAppSelectorScope
 
 @Module(subcomponents = [MediaProjectionAppSelectorComponent::class])
@@ -83,7 +94,7 @@
         @MediaProjectionAppSelector
         @MediaProjectionAppSelectorScope
         fun provideAppSelectorComponentName(context: Context): ComponentName =
-                ComponentName(context, MediaProjectionAppSelectorActivity::class.java)
+            ComponentName(context, MediaProjectionAppSelectorActivity::class.java)
 
         @Provides
         @MediaProjectionAppSelector
@@ -93,9 +104,32 @@
         ): ConfigurationController = ConfigurationControllerImpl(activity)
 
         @Provides
-        fun bindIconFactory(
-            context: Context
-        ): IconFactory = IconFactory.obtain(context)
+        @PersonalProfile
+        @MediaProjectionAppSelectorScope
+        fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle {
+            // Current foreground user is the 'personal' profile
+            return UserHandle.of(activityManagerWrapper.currentUserId)
+        }
+
+        @Provides
+        @WorkProfile
+        @MediaProjectionAppSelectorScope
+        fun workProfileUserHandle(userTracker: UserTracker): UserHandle? =
+            userTracker.userProfiles.find { it.isManagedProfile }?.userHandle
+
+        @Provides
+        @HostUserHandle
+        @MediaProjectionAppSelectorScope
+        fun hostUserHandle(activity: MediaProjectionAppSelectorActivity): UserHandle {
+            val extras =
+                activity.intent.extras
+                    ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
+            return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
+                ?: error("MediaProjectionAppSelectorActivity should be provided with " +
+                        "$EXTRA_HOST_APP_USER_HANDLE extra")
+        }
+
+        @Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
 
         @Provides
         @MediaProjectionAppSelector
@@ -124,6 +158,8 @@
 
     val controller: MediaProjectionAppSelectorController
     val recentsViewController: MediaProjectionRecentsViewController
+    @get:HostUserHandle val hostUserHandle: UserHandle
+    @get:PersonalProfile val personalProfileUserHandle: UserHandle
 
     @MediaProjectionAppSelector val configurationController: ConfigurationController
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index d744a40b..52c7ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -17,24 +17,36 @@
 package com.android.systemui.mediaprojection.appselector
 
 import android.content.ComponentName
+import android.os.UserHandle
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.launch
-import javax.inject.Inject
 
 @MediaProjectionAppSelectorScope
-class MediaProjectionAppSelectorController @Inject constructor(
+class MediaProjectionAppSelectorController
+@Inject
+constructor(
     private val recentTaskListProvider: RecentTaskListProvider,
     private val view: MediaProjectionAppSelectorView,
+    private val flags: FeatureFlags,
+    @HostUserHandle private val hostUserHandle: UserHandle,
     @MediaProjectionAppSelector private val scope: CoroutineScope,
     @MediaProjectionAppSelector private val appSelectorComponentName: ComponentName
 ) {
 
     fun init() {
         scope.launch {
-            val tasks = recentTaskListProvider.loadRecentTasks().sortTasks()
+            val recentTasks = recentTaskListProvider.loadRecentTasks()
+
+            val tasks = recentTasks
+                .filterDevicePolicyRestrictedTasks()
+                .sortedTasks()
+
             view.bind(tasks)
         }
     }
@@ -43,9 +55,20 @@
         scope.cancel()
     }
 
-    private fun List<RecentTask>.sortTasks(): List<RecentTask> =
-        sortedBy {
-            // Show normal tasks first and only then tasks with opened app selector
-            it.topActivityComponent == appSelectorComponentName
+    /**
+     * Removes all recent tasks that are different from the profile of the host app to avoid any
+     * cross-profile sharing
+     */
+    private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> =
+        if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) {
+            // TODO(b/263950746): filter tasks based on the enterprise policies
+            this
+        } else {
+            filter { UserHandle.of(it.userId) == hostUserHandle }
         }
+
+    private fun List<RecentTask>.sortedTasks(): List<RecentTask> = sortedBy {
+        // Show normal tasks first and only then tasks with opened app selector
+        it.topActivityComponent == appSelectorComponentName
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index cd994b8..41e2286 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -17,11 +17,12 @@
 package com.android.systemui.mediaprojection.appselector.data
 
 import android.annotation.ColorInt
+import android.annotation.UserIdInt
 import android.content.ComponentName
 
 data class RecentTask(
     val taskId: Int,
-    val userId: Int,
+    @UserIdInt val userId: Int,
     val topActivityComponent: ComponentName?,
     val baseIntentComponent: ComponentName?,
     @ColorInt val colorBackground: Int?
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 44b18ec..68e3dcd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -23,6 +23,7 @@
 import android.os.Handler
 import android.os.Looper
 import android.os.ResultReceiver
+import android.os.UserHandle
 import android.view.View
 import android.view.View.GONE
 import android.view.View.VISIBLE
@@ -77,6 +78,14 @@
                     MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
                     CaptureTargetResultReceiver()
                 )
+
+                // Send SystemUI's user handle as the host app user handle because SystemUI
+                // is the 'host app' (the app that receives screen capture data)
+                intent.putExtra(
+                    MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
+                    UserHandle.of(UserHandle.myUserId())
+                )
+
                 val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
                 if (animationController == null) {
                     dismiss()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 964d0b2..fd31e49 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -715,7 +715,7 @@
         updatePanelExpansionAndVisibility();
     };
     private final Runnable mMaybeHideExpandedRunnable = () -> {
-        if (getExpansionFraction() == 0.0f) {
+        if (getExpandedFraction() == 0.0f) {
             postToView(mHideExpandedRunnable);
         }
     };
@@ -5451,10 +5451,6 @@
                 InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
     }
 
-    private float getExpansionFraction() {
-        return mExpandedFraction;
-    }
-
     private ShadeExpansionStateManager getShadeExpansionStateManager() {
         return mShadeExpansionStateManager;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 19d2d33..1042ea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -1,12 +1,16 @@
 package com.android.systemui.mediaprojection.appselector
 
 import android.content.ComponentName
+import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import org.junit.Test
@@ -21,11 +25,17 @@
     private val scope = CoroutineScope(Dispatchers.Unconfined)
     private val appSelectorComponentName = ComponentName("com.test", "AppSelector")
 
+    private val hostUserHandle = UserHandle.of(123)
+    private val otherUserHandle = UserHandle.of(456)
+
     private val view: MediaProjectionAppSelectorView = mock()
+    private val featureFlags: FeatureFlags = mock()
 
     private val controller = MediaProjectionAppSelectorController(
         taskListProvider,
         view,
+        featureFlags,
+        hostUserHandle,
         scope,
         appSelectorComponentName
     )
@@ -98,15 +108,72 @@
         )
     }
 
+    @Test
+    fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesDisabled_bindsOnlyTasksWithHostProfile() {
+        givenEnterprisePoliciesFeatureFlag(enabled = false)
+
+        val tasks = listOf(
+            createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+            createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
+            createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+            createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
+            createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+        )
+        taskListProvider.tasks = tasks
+
+        controller.init()
+
+        verify(view).bind(
+            listOf(
+                createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+                createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+                createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+            )
+        )
+    }
+
+    @Test
+    fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesEnabled_bindsAllTasks() {
+        givenEnterprisePoliciesFeatureFlag(enabled = true)
+
+        val tasks = listOf(
+            createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+            createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
+            createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+            createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
+            createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+        )
+        taskListProvider.tasks = tasks
+
+        controller.init()
+
+        // TODO(b/233348916) should filter depending on the policies
+        verify(view).bind(
+            listOf(
+                createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+                createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
+                createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+                createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
+                createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+            )
+        )
+    }
+
+    private fun givenEnterprisePoliciesFeatureFlag(enabled: Boolean) {
+        whenever(featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+            .thenReturn(enabled)
+    }
+
     private fun createRecentTask(
         taskId: Int,
-        topActivityComponent: ComponentName? = null
+        topActivityComponent: ComponentName? = null,
+        userId: Int = hostUserHandle.identifier
     ): RecentTask {
         return RecentTask(
             taskId = taskId,
             topActivityComponent = topActivityComponent,
             baseIntentComponent = ComponentName("com", "Test"),
-            userId = 0,
+            userId = userId,
             colorBackground = 0
         )
     }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index 5a868a4..cfb959e 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -22,8 +22,8 @@
 import android.os.Handler
 import android.view.IWindowManager
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldBackground
 import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
 import com.android.systemui.unfold.updates.FoldProvider
 import com.android.systemui.unfold.updates.RotationChangeProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
@@ -58,7 +58,7 @@
             @BindsInstance sensorManager: SensorManager,
             @BindsInstance @UnfoldMain handler: Handler,
             @BindsInstance @UnfoldMain executor: Executor,
-            @BindsInstance @UnfoldBackground backgroundExecutor: Executor,
+            @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
             @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
             @BindsInstance windowManager: IWindowManager,
             @BindsInstance contentResolver: ContentResolver = context.contentResolver
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index 3fa5469..31616fa 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -16,9 +16,7 @@
 
 package com.android.systemui.unfold
 
-import android.hardware.SensorManager
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldBackground
 import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
 import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
 import com.android.systemui.unfold.updates.DeviceFoldStateProvider
@@ -34,55 +32,18 @@
 import dagger.Module
 import dagger.Provides
 import java.util.Optional
-import java.util.concurrent.Executor
+import javax.inject.Provider
 import javax.inject.Singleton
 
-@Module
+@Module(includes = [UnfoldSharedInternalModule::class])
 class UnfoldSharedModule {
     @Provides
     @Singleton
-    fun unfoldTransitionProgressProvider(
-        config: UnfoldTransitionConfig,
-        scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
-        tracingListener: ATraceLoggerTransitionProgressListener,
-        foldStateProvider: FoldStateProvider
-    ): Optional<UnfoldTransitionProgressProvider> =
-        if (!config.isEnabled) {
-            Optional.empty()
-        } else {
-            val baseProgressProvider =
-                if (config.isHingeAngleEnabled) {
-                    PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
-                } else {
-                    FixedTimingTransitionProgressProvider(foldStateProvider)
-                }
-            Optional.of(
-                scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
-                    // Always present callback that logs animation beginning and end.
-                    addCallback(tracingListener)
-                }
-            )
-        }
-
-    @Provides
-    @Singleton
     fun provideFoldStateProvider(
         deviceFoldStateProvider: DeviceFoldStateProvider
     ): FoldStateProvider = deviceFoldStateProvider
 
     @Provides
-    fun hingeAngleProvider(
-        config: UnfoldTransitionConfig,
-        sensorManager: SensorManager,
-        @UnfoldBackground executor: Executor
-    ): HingeAngleProvider =
-        if (config.isHingeAngleEnabled) {
-            HingeSensorAngleProvider(sensorManager, executor)
-        } else {
-            EmptyHingeAngleProvider
-        }
-
-    @Provides
     @Singleton
     fun unfoldKeyguardVisibilityProvider(
         impl: UnfoldKeyguardVisibilityManagerImpl
@@ -94,3 +55,51 @@
         impl: UnfoldKeyguardVisibilityManagerImpl
     ): UnfoldKeyguardVisibilityManager = impl
 }
+
+/**
+ * Needed as methods inside must be public, but their parameters can be internal (and, a public
+ * method can't have internal parameters). Making the module internal and included in a public one
+ * fixes the issue.
+ */
+@Module
+internal class UnfoldSharedInternalModule {
+    @Provides
+    @Singleton
+    fun unfoldTransitionProgressProvider(
+        config: UnfoldTransitionConfig,
+        scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+        tracingListener: ATraceLoggerTransitionProgressListener,
+        physicsBasedUnfoldTransitionProgressProvider:
+            Provider<PhysicsBasedUnfoldTransitionProgressProvider>,
+        fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
+    ): Optional<UnfoldTransitionProgressProvider> {
+        if (!config.isEnabled) {
+            return Optional.empty()
+        }
+        val baseProgressProvider =
+            if (config.isHingeAngleEnabled) {
+                physicsBasedUnfoldTransitionProgressProvider.get()
+            } else {
+                fixedTimingTransitionProgressProvider.get()
+            }
+
+        return Optional.of(
+            scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
+                // Always present callback that logs animation beginning and end.
+                addCallback(tracingListener)
+            }
+        )
+    }
+
+    @Provides
+    fun hingeAngleProvider(
+        config: UnfoldTransitionConfig,
+        hingeAngleSensorProvider: Provider<HingeSensorAngleProvider>
+    ): HingeAngleProvider {
+        return if (config.isHingeAngleEnabled) {
+            hingeAngleSensorProvider.get()
+        } else {
+            EmptyHingeAngleProvider
+        }
+    }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index a1ed178..aa93c629 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -37,29 +37,29 @@
  * This should **never** be called from sysui, as the object is already provided in that process.
  */
 fun createUnfoldSharedComponent(
-    context: Context,
-    config: UnfoldTransitionConfig,
-    screenStatusProvider: ScreenStatusProvider,
-    foldProvider: FoldProvider,
-    activityTypeProvider: CurrentActivityTypeProvider,
-    sensorManager: SensorManager,
-    mainHandler: Handler,
-    mainExecutor: Executor,
-    backgroundExecutor: Executor,
-    tracingTagPrefix: String,
-    windowManager: IWindowManager,
+        context: Context,
+        config: UnfoldTransitionConfig,
+        screenStatusProvider: ScreenStatusProvider,
+        foldProvider: FoldProvider,
+        activityTypeProvider: CurrentActivityTypeProvider,
+        sensorManager: SensorManager,
+        mainHandler: Handler,
+        mainExecutor: Executor,
+        singleThreadBgExecutor: Executor,
+        tracingTagPrefix: String,
+        windowManager: IWindowManager,
 ): UnfoldSharedComponent =
-    DaggerUnfoldSharedComponent.factory()
-        .create(
-            context,
-            config,
-            screenStatusProvider,
-            foldProvider,
-            activityTypeProvider,
-            sensorManager,
-            mainHandler,
-            mainExecutor,
-            backgroundExecutor,
-            tracingTagPrefix,
-            windowManager,
-        )
+        DaggerUnfoldSharedComponent.factory()
+                .create(
+                        context,
+                        config,
+                        screenStatusProvider,
+                        foldProvider,
+                        activityTypeProvider,
+                        sensorManager,
+                        mainHandler,
+                        mainExecutor,
+                        singleThreadBgExecutor,
+                        tracingTagPrefix,
+                        windowManager,
+                )
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldSingleThreadBg.kt
similarity index 89%
rename from packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt
rename to packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldSingleThreadBg.kt
index 6074795..dcac531 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldSingleThreadBg.kt
@@ -18,8 +18,7 @@
 
 /**
  * Alternative to [UiBackground] qualifier annotation in unfold module.
+ *
  * It is needed as we can't depend on SystemUI code in this module.
  */
-@Qualifier
-@Retention(AnnotationRetention.RUNTIME)
-annotation class UnfoldBackground
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldSingleThreadBg
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
index fa59cb4..4622464 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
@@ -24,11 +24,13 @@
 import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
 import com.android.systemui.unfold.updates.FoldStateProvider
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import javax.inject.Inject
 
 /** Emits animation progress with fixed timing after unfolding */
-internal class FixedTimingTransitionProgressProvider(
-    private val foldStateProvider: FoldStateProvider
-) : UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener {
+internal class FixedTimingTransitionProgressProvider
+@Inject
+constructor(private val foldStateProvider: FoldStateProvider) :
+    UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener {
 
     private val animatorListener = AnimatorListener()
     private val animator =
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index 074b1e1..6ffbe5a 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -33,9 +33,10 @@
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
 import com.android.systemui.unfold.updates.name
+import javax.inject.Inject
 
 /** Maps fold updates to unfold transition progress using DynamicAnimation. */
-class PhysicsBasedUnfoldTransitionProgressProvider(
+class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
     private val foldStateProvider: FoldStateProvider
 ) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
index 577137c..89fb12e 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -20,35 +20,43 @@
 import android.hardware.SensorManager
 import android.os.Trace
 import androidx.core.util.Consumer
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
 import java.util.concurrent.Executor
+import javax.inject.Inject
 
-internal class HingeSensorAngleProvider(
+internal class HingeSensorAngleProvider
+@Inject
+constructor(
     private val sensorManager: SensorManager,
-    private val executor: Executor
-) :
-    HingeAngleProvider {
+    @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor
+) : HingeAngleProvider {
 
     private val sensorListener = HingeAngleSensorListener()
     private val listeners: MutableList<Consumer<Float>> = arrayListOf()
     var started = false
 
-    override fun start() = executor.execute {
-        if (started) return@execute
-        Trace.beginSection("HingeSensorAngleProvider#start")
-        val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
-        sensorManager.registerListener(
-            sensorListener,
-            sensor,
-            SensorManager.SENSOR_DELAY_FASTEST
-        )
-        Trace.endSection()
-        started = true
+    override fun start() {
+        singleThreadBgExecutor.execute {
+            if (started) return@execute
+            Trace.beginSection("HingeSensorAngleProvider#start")
+            val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
+            sensorManager.registerListener(
+                sensorListener,
+                sensor,
+                SensorManager.SENSOR_DELAY_FASTEST
+            )
+            Trace.endSection()
+
+            started = true
+        }
     }
 
-    override fun stop() = executor.execute {
-        if (!started) return@execute
-        sensorManager.unregisterListener(sensorListener)
-        started = false
+    override fun stop() {
+        singleThreadBgExecutor.execute {
+            if (!started) return@execute
+            sensorManager.unregisterListener(sensorListener)
+            started = false
+        }
     }
 
     override fun removeCallback(listener: Consumer<Float>) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 88492ed..35b5f1b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2010,7 +2010,7 @@
 
     @Override
     public void hasFeatures(IAccountManagerResponse response,
-            Account account, String[] features, String opPackageName) {
+            Account account, String[] features, int userId, String opPackageName) {
         int callingUid = Binder.getCallingUid();
         mAppOpsManager.checkPackage(callingUid, opPackageName);
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2018,12 +2018,22 @@
                     + ", response " + response
                     + ", features " + Arrays.toString(features)
                     + ", caller's uid " + callingUid
+                    + ", userId " + userId
                     + ", pid " + Binder.getCallingPid());
         }
         Preconditions.checkArgument(account != null, "account cannot be null");
         Preconditions.checkArgument(response != null, "response cannot be null");
         Preconditions.checkArgument(features != null, "features cannot be null");
-        int userId = UserHandle.getCallingUserId();
+
+        if (userId != UserHandle.getCallingUserId()
+                && callingUid != Process.SYSTEM_UID
+                && mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("User " + UserHandle.getCallingUserId()
+                    + " trying to check account features for " + userId);
+        }
+
         checkReadAccountsPermitted(callingUid, account.type, userId,
                 opPackageName);
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 58ddd9c..39c7f30 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3785,12 +3785,13 @@
         super.setVolumeIndexForAttributes_enforcePermission();
 
         Objects.requireNonNull(attr, "attr must not be null");
-        final int volumeGroup = getVolumeGroupIdForAttributes(attr);
+        int volumeGroup = AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
+                attr, /* fallbackOnDefault= */false);
         if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
             Log.e(TAG, ": no volume group found for attributes " + attr.toString());
             return;
         }
-        final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+        VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
 
         sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
                 index/*val1*/, flags/*val2*/, callingPackage));
@@ -3798,7 +3799,7 @@
         vgs.setVolumeIndex(index, flags);
 
         // For legacy reason, propagate to all streams associated to this volume group
-        for (final int groupedStream : vgs.getLegacyStreamTypes()) {
+        for (int groupedStream : vgs.getLegacyStreamTypes()) {
             try {
                 ensureValidStreamType(groupedStream);
             } catch (IllegalArgumentException e) {
@@ -3830,7 +3831,9 @@
         super.getVolumeIndexForAttributes_enforcePermission();
 
         Objects.requireNonNull(attr, "attr must not be null");
-        final int volumeGroup = getVolumeGroupIdForAttributes(attr);
+        final int volumeGroup =
+                AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
+                        attr, /* fallbackOnDefault= */false);
         if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
             throw new IllegalArgumentException("No volume group for attributes " + attr);
         }
@@ -4380,31 +4383,6 @@
         sendVolumeUpdate(streamType, oldIndex, index, flags, device);
     }
 
-
-
-    private int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
-        Objects.requireNonNull(attributes, "attributes must not be null");
-        int volumeGroupId = getVolumeGroupIdForAttributesInt(attributes);
-        if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
-            return volumeGroupId;
-        }
-        // The default volume group is the one hosted by default product strategy, i.e.
-        // supporting Default Attributes
-        return getVolumeGroupIdForAttributesInt(AudioProductStrategy.getDefaultAttributes());
-    }
-
-    private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
-        Objects.requireNonNull(attributes, "attributes must not be null");
-        for (final AudioProductStrategy productStrategy :
-                AudioProductStrategy.getAudioProductStrategies()) {
-            int volumeGroupId = productStrategy.getVolumeGroupIdForAudioAttributes(attributes);
-            if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
-                return volumeGroupId;
-            }
-        }
-        return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
-    }
-
     private void dispatchAbsoluteVolumeChanged(int streamType, AbsoluteVolumeDeviceInfo deviceInfo,
             int index) {
         VolumeInfo volumeInfo = deviceInfo.getMatchingVolumeInfoForStream(streamType);
@@ -4437,7 +4415,6 @@
         }
     }
 
-
     // No ringer or zen muted stream volumes can be changed unless it'll exit dnd
     private boolean volumeAdjustmentAllowedByDnd(int streamTypeAlias, int flags) {
         switch (mNm.getZenMode()) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index dcc98e1..5b696c2 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -104,6 +104,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
+import com.android.internal.config.appcloning.AppCloningDeviceConfigHelper;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BackgroundThread;
@@ -264,6 +265,8 @@
 
     private final SyncLogger mLogger;
 
+    private final AppCloningDeviceConfigHelper mAppCloningDeviceConfigHelper;
+
     private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) {
         for (int i = 0, size = pendingJobs.size(); i < size; i++) {
             JobInfo job = pendingJobs.get(i);
@@ -627,6 +630,7 @@
         }, mSyncHandler);
 
         mConstants = new SyncManagerConstants(context);
+        mAppCloningDeviceConfigHelper = AppCloningDeviceConfigHelper.getInstance(context);
 
         IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
         context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
@@ -828,6 +832,18 @@
     }
 
     /**
+     * Check whether the feature flag controlling contacts sharing for clone profile is set. If
+     * true, the contact syncs for clone profile should be disabled.
+     *
+     * @return true/false if contact sharing is enabled/disabled
+     */
+    protected boolean isContactSharingAllowedForCloneProfile() {
+        // TODO(b/253449368): This method should also check for the config controlling
+        // all app-cloning features.
+        return mAppCloningDeviceConfigHelper.getEnableAppCloningBuildingBlocks();
+    }
+
+    /**
      * Check if account sync should be disabled for the given user and provider.
      * @param userInfo
      * @param providerName
@@ -836,7 +852,9 @@
      */
     @VisibleForTesting
     protected boolean shouldDisableSyncForUser(UserInfo userInfo, String providerName) {
-        if (userInfo == null || providerName == null) return false;
+        if (userInfo == null || providerName == null || !isContactSharingAllowedForCloneProfile()) {
+            return false;
+        }
         return providerName.equals(ContactsContract.AUTHORITY)
                 && !areContactWritesEnabledForUser(userInfo);
     }
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index f3d8803..10c16b6 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -380,7 +380,12 @@
                 }
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
-                // Do nothing.
+                // Do nothing but preserving the last IME requested visibility state.
+                final ImeTargetWindowState lastState =
+                        getWindowStateOrNull(mService.mLastImeTargetWindow);
+                if (lastState != null) {
+                    state.setRequestedImeVisible(lastState.mRequestedImeVisible);
+                }
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
                 if (isForwardNavigation) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 187de930..ba9e280 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -469,11 +469,11 @@
 
     @GuardedBy("ImfLock.class")
     private boolean bindCurrentInputMethodService(ServiceConnection conn, int flags) {
-        if (getCurIntent() == null || conn == null) {
+        if (mCurIntent == null || conn == null) {
             Slog.e(TAG, "--- bind failed: service = " + mCurIntent + ", conn = " + conn);
             return false;
         }
-        return mContext.bindServiceAsUser(getCurIntent(), conn, flags,
+        return mContext.bindServiceAsUser(mCurIntent, conn, flags,
                 new UserHandle(mSettings.getCurrentUserId()));
     }
 
diff --git a/services/core/java/com/android/server/media/projection/TEST_MAPPING b/services/core/java/com/android/server/media/projection/TEST_MAPPING
new file mode 100644
index 0000000..a792498
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "MediaProjectionTests",
+      "options": [
+        {
+          "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 5ab9f38..69ea559 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -108,7 +108,7 @@
     @VisibleForTesting
     static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
     @VisibleForTesting
-    static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 50000;
+    static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 6000;
 
     private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
     private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 93a119c..7f7a234 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -291,8 +291,8 @@
                     rollbackTimeoutIntent.putExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
                             sessionId);
-                    rollbackTimeoutIntent.addFlags(
-                            Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                    rollbackTimeoutIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                            | Intent.FLAG_RECEIVER_FOREGROUND);
                     mPm.mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
                             android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
                 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 12841a4..0685435 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2875,6 +2875,8 @@
                 newUserType = UserManager.USER_TYPE_FULL_DEMO;
             } else if ("--ephemeral".equals(opt)) {
                 flags |= UserInfo.FLAG_EPHEMERAL;
+            } else if ("--for-testing".equals(opt)) {
+                flags |= UserInfo.FLAG_FOR_TESTING;
             } else if ("--pre-create-only".equals(opt)) {
                 preCreateOnly = true;
             } else if ("--user-type".equals(opt)) {
@@ -4269,8 +4271,8 @@
         pw.println("  list users");
         pw.println("    Lists the current users.");
         pw.println("");
-        pw.println("  create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
-        pw.println("      [--guest] [--pre-create-only] [--user-type USER_TYPE] USER_NAME");
+        pw.println("  create-user [--profileOf USER_ID] [--managed] [--restricted] [--guest]");
+        pw.println("       [--user-type USER_TYPE] [--ephemeral] [--for-testing] [--pre-create-only]   USER_NAME");
         pw.println("    Create a new user with the given USER_NAME, printing the new user identifier");
         pw.println("    of the user.");
         // TODO(b/142482943): Consider fetching the list of user types from UMS.
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 7b15e76..1787116 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -513,4 +513,20 @@
      * @see UserManager#isMainUser()
      */
     public abstract @UserIdInt int getMainUserId();
+
+    /**
+     * Returns the id of the user which should be in the foreground after boot completes.
+     *
+     * <p>If a boot user has been provided by calling {@link UserManager#setBootUser}, the
+     * returned value will be whatever was specified, as long as that user exists and can be
+     * switched to.
+     *
+     * <p>Otherwise, in {@link UserManager#isHeadlessSystemUserMode() headless system user mode},
+     * this will be the user who was last in the foreground on this device. If there is no
+     * switchable user on the device, a new user will be created and its id will be returned.
+     *
+     * <p>In non-headless system user mode, the return value will be {@link UserHandle#USER_SYSTEM}.
+     */
+    public abstract @UserIdInt int getBootUser()
+            throws UserManager.CheckedUserOperationException;
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a8cf8cb..175c11d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -21,6 +21,7 @@
 import static android.os.UserManager.DEV_CREATE_OVERRIDE_PROPERTY;
 import static android.os.UserManager.DISALLOW_USER_SWITCH;
 import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
+import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
 
 import android.Manifest;
 import android.accounts.Account;
@@ -31,7 +32,6 @@
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.UserIdInt;
-import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
@@ -44,6 +44,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
@@ -127,11 +128,8 @@
 import com.android.server.LockGuard;
 import com.android.server.SystemService;
 import com.android.server.am.UserState;
-import com.android.server.pm.UserManagerInternal.UserAssignmentResult;
 import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
 import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
-import com.android.server.pm.UserManagerInternal.UserStartMode;
-import com.android.server.pm.UserManagerInternal.UserVisibilityListener;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.utils.Slogf;
 import com.android.server.utils.TimingsTraceAndSlog;
@@ -254,7 +252,8 @@
             | UserInfo.FLAG_RESTRICTED
             | UserInfo.FLAG_GUEST
             | UserInfo.FLAG_DEMO
-            | UserInfo.FLAG_FULL;
+            | UserInfo.FLAG_FULL
+            | UserInfo.FLAG_FOR_TESTING;
 
     @VisibleForTesting
     static final int MIN_USER_ID = UserHandle.MIN_SECONDARY_USER_ID;
@@ -637,6 +636,9 @@
 
     private final UserVisibilityMediator mUserVisibilityMediator;
 
+    @GuardedBy("mUsersLock")
+    private @UserIdInt int mBootUser = UserHandle.USER_NULL;
+
     private static UserManagerService sInstance;
 
     public static UserManagerService getInstance() {
@@ -936,6 +938,26 @@
     }
 
     @Override
+    public void setBootUser(@UserIdInt int userId) {
+        checkCreateUsersPermission("Set boot user");
+        synchronized (mUsersLock) {
+            // TODO(b/263381643): Change to EventLog.
+            Slogf.i(LOG_TAG, "setBootUser %d", userId);
+            mBootUser = userId;
+        }
+    }
+
+    @Override
+    public @UserIdInt int getBootUser() {
+        checkCreateUsersPermission("Get boot user");
+        try {
+            return mLocalService.getBootUser();
+        } catch (UserManager.CheckedUserOperationException e) {
+            throw e.toServiceSpecificException();
+        }
+    }
+
+    @Override
     public int getPreviousFullUserToEnterForeground() {
         checkQueryOrCreateUsersPermission("get previous user");
         int previousUser = UserHandle.USER_NULL;
@@ -1569,6 +1591,8 @@
                     Slog.w(LOG_TAG, "System user instantiated at least " + number + " times");
                 }
                 name = getOwnerName();
+            } else if (orig.isMain()) {
+                name = getOwnerName();
             } else if (orig.isGuest()) {
                 name = getGuestName();
             }
@@ -4535,7 +4559,7 @@
                     UserHandle.USER_NULL, null);
 
             if (userInfo == null) {
-                throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN);
+                throw new ServiceSpecificException(USER_OPERATION_ERROR_UNKNOWN);
             }
         } catch (UserManager.CheckedUserOperationException e) {
             throw e.toServiceSpecificException();
@@ -4664,7 +4688,7 @@
                     if (parent == null) {
                         throwCheckedUserOperationException(
                                 "Cannot find user data for parent user " + parentId,
-                                UserManager.USER_OPERATION_ERROR_UNKNOWN);
+                                USER_OPERATION_ERROR_UNKNOWN);
                     }
                 }
                 if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) {
@@ -4692,7 +4716,7 @@
                         && !isCreationOverrideEnabled()) {
                     throwCheckedUserOperationException(
                             "Cannot add restricted profile - parent user must be system",
-                            UserManager.USER_OPERATION_ERROR_UNKNOWN);
+                            USER_OPERATION_ERROR_UNKNOWN);
                 }
 
                 userId = getNextAvailableId();
@@ -5598,29 +5622,24 @@
             // Also, add the UserHandle for mainline modules which can't use the @hide
             // EXTRA_USER_HANDLE.
             removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
-            mContext.sendOrderedBroadcastAsUser(removedIntent, UserHandle.ALL,
-                    android.Manifest.permission.MANAGE_USERS,
-
-                    new BroadcastReceiver() {
+            getActivityManagerInternal().broadcastIntentWithCallback(removedIntent,
+                    new IIntentReceiver.Stub() {
                         @Override
-                        public void onReceive(Context context, Intent intent) {
+                        public void performReceive(Intent intent, int resultCode, String data,
+                                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                             if (DBG) {
                                 Slog.i(LOG_TAG,
                                         "USER_REMOVED broadcast sent, cleaning up user data "
-                                        + userId);
+                                                + userId);
                             }
-                            new Thread() {
-                                @Override
-                                public void run() {
-                                    LocalServices.getService(ActivityManagerInternal.class)
-                                            .onUserRemoved(userId);
-                                    removeUserState(userId);
-                                }
-                            }.start();
+                            new Thread(() -> {
+                                getActivityManagerInternal().onUserRemoved(userId);
+                                removeUserState(userId);
+                            }).start();
                         }
                     },
-
-                    null, Activity.RESULT_OK, null, null);
+                    new String[] {android.Manifest.permission.MANAGE_USERS},
+                    UserHandle.USER_ALL, null, null, null);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -6459,6 +6478,9 @@
         if (DBG_ALLOCATION) {
             pw.println("  System user allocations: " + mUser0Allocations.get());
         }
+        synchronized (mUsersLock) {
+            pw.println("  Boot user: " + mBootUser);
+        }
 
         pw.println();
         pw.println("Number of listeners for");
@@ -6651,6 +6673,18 @@
         return mLocalService.isUserInitialized(userId);
     }
 
+    /**
+     * Creates a new user, intended to be the initial user on a device in headless system user mode.
+     */
+    private UserInfo createInitialUserForHsum() throws UserManager.CheckedUserOperationException {
+        final int flags = UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN;
+
+        // Null name will be replaced with "Owner" on-demand to allow for localisation.
+        return createUserInternalUnchecked(/* name= */ null, UserManager.USER_TYPE_FULL_SECONDARY,
+                flags, UserHandle.USER_NULL, /* preCreate= */ false,
+                /* disallowedPackages= */ null, /* token= */ null);
+    }
+
     private class LocalService extends UserManagerInternal {
         @Override
         public void setDevicePolicyUserRestrictions(@UserIdInt int originatingUserId,
@@ -7094,6 +7128,56 @@
             return getMainUserIdUnchecked();
         }
 
+        @Override
+        public @UserIdInt int getBootUser() throws UserManager.CheckedUserOperationException {
+            synchronized (mUsersLock) {
+                // TODO(b/242195409): On Automotive, block if boot user not provided.
+                if (mBootUser != UserHandle.USER_NULL) {
+                    final UserData userData = mUsers.get(mBootUser);
+                    if (userData != null && userData.info.supportsSwitchToByUser()) {
+                        Slogf.i(LOG_TAG, "Using provided boot user: %d", mBootUser);
+                        return mBootUser;
+                    } else {
+                        Slogf.w(LOG_TAG,
+                                "Provided boot user cannot be switched to: %d", mBootUser);
+                    }
+                }
+            }
+
+            if (isHeadlessSystemUserMode()) {
+                // Return the previous foreground user, if there is one.
+                final int previousUser = getPreviousFullUserToEnterForeground();
+                if (previousUser != UserHandle.USER_NULL) {
+                    Slogf.i(LOG_TAG, "Boot user is previous user %d", previousUser);
+                    return previousUser;
+                }
+                // No previous user. Return the first switchable user if there is one.
+                synchronized (mUsersLock) {
+                    final int userSize = mUsers.size();
+                    for (int i = 0; i < userSize; i++) {
+                        final UserData userData = mUsers.valueAt(i);
+                        if (userData.info.supportsSwitchToByUser()) {
+                            int firstSwitchable = userData.info.id;
+                            Slogf.i(LOG_TAG,
+                                    "Boot user is first switchable user %d", firstSwitchable);
+                            return firstSwitchable;
+                        }
+                    }
+                }
+                // No switchable users. Create the initial user.
+                final UserInfo newInitialUser = createInitialUserForHsum();
+                if (newInitialUser == null) {
+                    throw new UserManager.CheckedUserOperationException(
+                            "Initial user creation failed", USER_OPERATION_ERROR_UNKNOWN);
+                }
+                Slogf.i(LOG_TAG,
+                        "No switchable users. Boot user is new user %d", newInitialUser.id);
+                return newInitialUser.id;
+            }
+            // Not HSUM, return system user.
+            return UserHandle.USER_SYSTEM;
+        }
+
     } // class LocalService
 
 
@@ -7113,7 +7197,7 @@
                     + restriction + " is enabled.";
             Slog.w(LOG_TAG, errorMessage);
             throw new UserManager.CheckedUserOperationException(errorMessage,
-                    UserManager.USER_OPERATION_ERROR_UNKNOWN);
+                    USER_OPERATION_ERROR_UNKNOWN);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index a54f526..8ec6241 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -233,7 +233,8 @@
                 PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
                 mSessionId);
         enableRollbackIntent.setType(PACKAGE_MIME_TYPE);
-        enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_RECEIVER_FOREGROUND);
 
         // Allow the broadcast to be sent before boot complete.
         // This is needed when committing the apk part of a staged
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
new file mode 100644
index 0000000..a380dea
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
@@ -0,0 +1,137 @@
+/*
+ * 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.server.wallpaper;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Binder;
+import android.os.Debug;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.WindowManagerInternal;
+
+import java.util.function.Consumer;
+/**
+ * Internal class used to store all the display data relevant to the wallpapers
+ */
+class WallpaperDisplayHelper {
+
+    @VisibleForTesting
+    static final class DisplayData {
+        int mWidth = -1;
+        int mHeight = -1;
+        final Rect mPadding = new Rect(0, 0, 0, 0);
+        final int mDisplayId;
+        DisplayData(int displayId) {
+            mDisplayId = displayId;
+        }
+    }
+
+    private static final String TAG = WallpaperDisplayHelper.class.getSimpleName();
+    private final SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
+    private final DisplayManager mDisplayManager;
+    private final WindowManagerInternal mWindowManagerInternal;
+
+    WallpaperDisplayHelper(
+            DisplayManager displayManager,
+            WindowManagerInternal windowManagerInternal) {
+        mDisplayManager = displayManager;
+        mWindowManagerInternal = windowManagerInternal;
+    }
+
+    DisplayData getDisplayDataOrCreate(int displayId) {
+        DisplayData wpdData = mDisplayDatas.get(displayId);
+        if (wpdData == null) {
+            wpdData = new DisplayData(displayId);
+            ensureSaneWallpaperDisplaySize(wpdData, displayId);
+            mDisplayDatas.append(displayId, wpdData);
+        }
+        return wpdData;
+    }
+
+    void removeDisplayData(int displayId) {
+        mDisplayDatas.remove(displayId);
+    }
+
+    void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) {
+        // We always want to have some reasonable width hint.
+        final int baseSize = getMaximumSizeDimension(displayId);
+        if (wpdData.mWidth < baseSize) {
+            wpdData.mWidth = baseSize;
+        }
+        if (wpdData.mHeight < baseSize) {
+            wpdData.mHeight = baseSize;
+        }
+    }
+
+    int getMaximumSizeDimension(int displayId) {
+        Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4));
+            display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+        }
+        return display.getMaximumSizeDimension();
+    }
+
+    void forEachDisplayData(Consumer<DisplayData> action) {
+        for (int i = mDisplayDatas.size() - 1; i >= 0; i--) {
+            final DisplayData wpdData = mDisplayDatas.valueAt(i);
+            action.accept(wpdData);
+        }
+    }
+
+    Display[] getDisplays() {
+        return mDisplayManager.getDisplays();
+    }
+
+    DisplayInfo getDisplayInfo(int displayId) {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        mDisplayManager.getDisplay(displayId).getDisplayInfo(displayInfo);
+        return displayInfo;
+    }
+
+    boolean isUsableDisplay(int displayId, int clientUid) {
+        return isUsableDisplay(mDisplayManager.getDisplay(displayId), clientUid);
+    }
+
+    boolean isUsableDisplay(Display display, int clientUid) {
+        if (display == null || !display.hasAccess(clientUid)) {
+            return false;
+        }
+        final int displayId = display.getDisplayId();
+        if (displayId == DEFAULT_DISPLAY) {
+            return true;
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    boolean isValidDisplay(int displayId) {
+        return mDisplayManager.getDisplay(displayId) != null;
+    }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6edfebf..075bac1 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -32,6 +32,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import static com.android.server.wallpaper.WallpaperDisplayHelper.DisplayData;
 import static com.android.server.wallpaper.WallpaperUtils.RECORD_FILE;
 import static com.android.server.wallpaper.WallpaperUtils.RECORD_LOCK_FILE;
 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
@@ -79,7 +80,6 @@
 import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Debug;
 import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -609,10 +609,9 @@
         boolean success = false;
 
         // Only generate crop for default display.
-        final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
+        final DisplayData wpData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
         final Rect cropHint = new Rect(wallpaper.cropHint);
-        final DisplayInfo displayInfo = new DisplayInfo();
-        mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo);
+        final DisplayInfo displayInfo = mWallpaperDisplayHelper.getDisplayInfo(DEFAULT_DISPLAY);
 
         if (DEBUG) {
             Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
@@ -829,7 +828,8 @@
     private final MyPackageMonitor mMonitor;
     private final AppOpsManager mAppOpsManager;
 
-    private final DisplayManager mDisplayManager;
+    // TODO("b/264637309") probably move this in WallpaperDisplayUtils,
+    //  after logic is changed for the lockscreen lwp project
     private final DisplayManager.DisplayListener mDisplayListener =
             new DisplayManager.DisplayListener() {
 
@@ -848,12 +848,12 @@
                         targetWallpaper = mFallbackWallpaper;
                     }
                     if (targetWallpaper == null) return;
-                    WallpaperConnection.DisplayConnector connector =
+                    DisplayConnector connector =
                             targetWallpaper.connection.getDisplayConnectorOrCreate(displayId);
                     if (connector == null) return;
                     connector.disconnectLocked();
                     targetWallpaper.connection.removeDisplayConnector(displayId);
-                    removeDisplayData(displayId);
+                    mWallpaperDisplayHelper.removeDisplayData(displayId);
                 }
                 for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
                     final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
@@ -904,8 +904,6 @@
     private final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
     private final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
 
-    private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
-
     protected WallpaperData mFallbackWallpaper;
 
     private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
@@ -914,57 +912,7 @@
     private LocalColorRepository mLocalColorRepo = new LocalColorRepository();
 
     @VisibleForTesting
-    static final class DisplayData {
-        int mWidth = -1;
-        int mHeight = -1;
-        final Rect mPadding = new Rect(0, 0, 0, 0);
-        final int mDisplayId;
-
-        DisplayData(int displayId) {
-            mDisplayId = displayId;
-        }
-    }
-
-    private void removeDisplayData(int displayId) {
-        mDisplayDatas.remove(displayId);
-    }
-
-    private DisplayData getDisplayDataOrCreate(int displayId) {
-        DisplayData wpdData = mDisplayDatas.get(displayId);
-        if (wpdData == null) {
-            wpdData = new DisplayData(displayId);
-            ensureSaneWallpaperDisplaySize(wpdData, displayId);
-            mDisplayDatas.append(displayId, wpdData);
-        }
-        return wpdData;
-    }
-
-    private void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) {
-        // We always want to have some reasonable width hint.
-        final int baseSize = getMaximumSizeDimension(displayId);
-        if (wpdData.mWidth < baseSize) {
-            wpdData.mWidth = baseSize;
-        }
-        if (wpdData.mHeight < baseSize) {
-            wpdData.mHeight = baseSize;
-        }
-    }
-
-    private int getMaximumSizeDimension(int displayId) {
-        Display display = mDisplayManager.getDisplay(displayId);
-        if (display == null) {
-            Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4));
-            display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-        }
-        return display.getMaximumSizeDimension();
-    }
-
-    void forEachDisplayData(Consumer<DisplayData> action) {
-        for (int i = mDisplayDatas.size() - 1; i >= 0; i--) {
-            final DisplayData wpdData = mDisplayDatas.valueAt(i);
-            action.accept(wpdData);
-        }
-    }
+    final WallpaperDisplayHelper mWallpaperDisplayHelper;
 
     private boolean supportsMultiDisplay(WallpaperConnection connection) {
         if (connection != null) {
@@ -993,7 +941,7 @@
             }
         } else {
             fallbackConnection.appendConnectorWithCondition(display ->
-                    fallbackConnection.isUsableDisplay(display)
+                    mWallpaperDisplayHelper.isUsableDisplay(display, fallbackConnection.mClientUid)
                             && display.getDisplayId() != DEFAULT_DISPLAY
                             && !fallbackConnection.containsDisplay(display.getDisplayId()));
             fallbackConnection.forEachDisplayConnector(connector -> {
@@ -1004,84 +952,87 @@
         }
     }
 
-    class WallpaperConnection extends IWallpaperConnection.Stub
-            implements ServiceConnection {
+    /**
+     * Collect needed info for a display.
+     */
+    @VisibleForTesting
+    final class DisplayConnector {
+        final int mDisplayId;
+        final Binder mToken = new Binder();
+        IWallpaperEngine mEngine;
+        boolean mDimensionsChanged;
+        boolean mPaddingChanged;
 
-        /**
-         * Collect needed info for a display.
-         */
-        @VisibleForTesting
-        final class DisplayConnector {
-            final int mDisplayId;
-            final Binder mToken = new Binder();
-            IWallpaperEngine mEngine;
-            boolean mDimensionsChanged;
-            boolean mPaddingChanged;
+        DisplayConnector(int displayId) {
+            mDisplayId = displayId;
+        }
 
-            DisplayConnector(int displayId) {
-                mDisplayId = displayId;
-            }
-
-            void ensureStatusHandled() {
-                final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
-                if (mDimensionsChanged) {
-                    try {
-                        mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to set wallpaper dimensions", e);
-                    }
-                    mDimensionsChanged = false;
-                }
-                if (mPaddingChanged) {
-                    try {
-                        mEngine.setDisplayPadding(wpdData.mPadding);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to set wallpaper padding", e);
-                    }
-                    mPaddingChanged = false;
-                }
-            }
-
-            void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
-                if (connection.mService == null) {
-                    Slog.w(TAG, "WallpaperService is not connected yet");
-                    return;
-                }
-                TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
-                t.traceBegin("WPMS.connectLocked-" + wallpaper.wallpaperComponent);
-                if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
-                mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
-                        null /* options */);
-                final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
+        void ensureStatusHandled() {
+            final DisplayData wpdData =
+                    mWallpaperDisplayHelper.getDisplayDataOrCreate(mDisplayId);
+            if (mDimensionsChanged) {
                 try {
-                    connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
-                            wpdData.mWidth, wpdData.mHeight,
-                            wpdData.mPadding, mDisplayId, mWallpaper.mWhich);
+                    mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed attaching wallpaper on display", e);
-                    if (wallpaper != null && !wallpaper.wallpaperUpdating
-                            && connection.getConnectedEngineSize() == 0) {
-                        bindWallpaperComponentLocked(null /* componentName */, false /* force */,
-                                false /* fromUser */, wallpaper, null /* reply */);
-                    }
+                    Slog.w(TAG, "Failed to set wallpaper dimensions", e);
                 }
-                t.traceEnd();
+                mDimensionsChanged = false;
             }
-
-            void disconnectLocked() {
-                if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
-                mWindowManagerInternal.removeWindowToken(mToken, false/* removeWindows */,
-                        mDisplayId);
+            if (mPaddingChanged) {
                 try {
-                    if (mEngine != null) {
-                        mEngine.destroy();
-                    }
+                    mEngine.setDisplayPadding(wpdData.mPadding);
                 } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to set wallpaper padding", e);
                 }
-                mEngine = null;
+                mPaddingChanged = false;
             }
         }
 
+        void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+            if (connection.mService == null) {
+                Slog.w(TAG, "WallpaperService is not connected yet");
+                return;
+            }
+            TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+            t.traceBegin("WPMS.connectLocked-" + wallpaper.wallpaperComponent);
+            if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
+            mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
+                    null /* options */);
+            final DisplayData wpdData =
+                    mWallpaperDisplayHelper.getDisplayDataOrCreate(mDisplayId);
+            try {
+                connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
+                        wpdData.mWidth, wpdData.mHeight,
+                        wpdData.mPadding, mDisplayId, wallpaper.mWhich);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed attaching wallpaper on display", e);
+                if (wallpaper != null && !wallpaper.wallpaperUpdating
+                        && connection.getConnectedEngineSize() == 0) {
+                    bindWallpaperComponentLocked(null /* componentName */, false /* force */,
+                            false /* fromUser */, wallpaper, null /* reply */);
+                }
+            }
+            t.traceEnd();
+        }
+
+        void disconnectLocked() {
+            if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
+            mWindowManagerInternal.removeWindowToken(mToken, false/* removeWindows */,
+                    mDisplayId);
+            try {
+                if (mEngine != null) {
+                    mEngine.destroy();
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Engine.destroy() threw a RemoteException");
+            }
+            mEngine = null;
+        }
+    }
+
+    class WallpaperConnection extends IWallpaperConnection.Stub
+            implements ServiceConnection {
+
         /**
          * A map for each display.
          * Use {@link #getDisplayConnectorOrCreate(int displayId)} to ensure the display is usable.
@@ -1132,7 +1083,8 @@
             if (!mWallpaper.equals(mFallbackWallpaper)) {
                 if (supportsMultiDisplay(this)) {
                     // The system wallpaper is image wallpaper or it can supports multiple displays.
-                    appendConnectorWithCondition(this::isUsableDisplay);
+                    appendConnectorWithCondition(display ->
+                            mWallpaperDisplayHelper.isUsableDisplay(display, mClientUid));
                 } else {
                     // The system wallpaper does not support multiple displays, so just attach it on
                     // default display.
@@ -1143,37 +1095,18 @@
         }
 
         private void appendConnectorWithCondition(Predicate<Display> tester) {
-            final Display[] displays = mDisplayManager.getDisplays();
+            final Display[] displays = mWallpaperDisplayHelper.getDisplays();
             for (Display display : displays) {
                 if (tester.test(display)) {
                     final int displayId = display.getDisplayId();
                     final DisplayConnector connector = mDisplayConnector.get(displayId);
                     if (connector == null) {
-                        mDisplayConnector.append(displayId,
-                                new DisplayConnector(displayId));
+                        mDisplayConnector.append(displayId, new DisplayConnector(displayId));
                     }
                 }
             }
         }
 
-        @VisibleForTesting
-        boolean isUsableDisplay(Display display) {
-            if (display == null || !display.hasAccess(mClientUid)) {
-                return false;
-            }
-            final int displayId = display.getDisplayId();
-            if (displayId == DEFAULT_DISPLAY) {
-                return true;
-            }
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
         void forEachDisplayConnector(Consumer<DisplayConnector> action) {
             for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
                 final DisplayConnector connector = mDisplayConnector.valueAt(i);
@@ -1193,8 +1126,7 @@
         DisplayConnector getDisplayConnectorOrCreate(int displayId) {
             DisplayConnector connector = mDisplayConnector.get(displayId);
             if (connector == null) {
-                final Display display = mDisplayManager.getDisplay(displayId);
-                if (isUsableDisplay(display)) {
+                if (mWallpaperDisplayHelper.isUsableDisplay(displayId, mClientUid)) {
                     connector = new DisplayConnector(displayId);
                     mDisplayConnector.append(displayId, connector);
                 }
@@ -1633,8 +1565,9 @@
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mIPackageManager = AppGlobals.getPackageManager();
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-        mDisplayManager = mContext.getSystemService(DisplayManager.class);
-        mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
+        DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+        dm.registerDisplayListener(mDisplayListener, null /* handler */);
+        mWallpaperDisplayHelper = new WallpaperDisplayHelper(dm, mWindowManagerInternal);
         mActivityManager = mContext.getSystemService(ActivityManager.class);
         mMonitor = new MyPackageMonitor();
         mColorsChangedListeners = new SparseArray<>();
@@ -2084,10 +2017,6 @@
         return false;
     }
 
-    private boolean isValidDisplay(int displayId) {
-        return mDisplayManager.getDisplay(displayId) != null;
-    }
-
     /**
      * Sets the dimension hint for the wallpaper. These hints indicate the desired
      * minimum width and height for the wallpaper in a particular display.
@@ -2110,18 +2039,18 @@
                 throw new IllegalArgumentException("width and height must be > 0");
             }
 
-            if (!isValidDisplay(displayId)) {
+            if (!mWallpaperDisplayHelper.isValidDisplay(displayId)) {
                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
             }
 
-            final DisplayData wpdData = getDisplayDataOrCreate(displayId);
+            final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(displayId);
             if (width != wpdData.mWidth || height != wpdData.mHeight) {
                 wpdData.mWidth = width;
                 wpdData.mHeight = height;
                 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+                    final DisplayConnector connector = wallpaper.connection
                             .getDisplayConnectorOrCreate(displayId);
                     final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
                     if (engine != null) {
@@ -2146,12 +2075,13 @@
      */
     public int getWidthHint(int displayId) throws RemoteException {
         synchronized (mLock) {
-            if (!isValidDisplay(displayId)) {
+            if (!mWallpaperDisplayHelper.isValidDisplay(displayId)) {
                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
             }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                final DisplayData wpdData = getDisplayDataOrCreate(displayId);
+                final DisplayData wpdData =
+                        mWallpaperDisplayHelper.getDisplayDataOrCreate(displayId);
                 return wpdData.mWidth;
             } else {
                 return 0;
@@ -2164,12 +2094,13 @@
      */
     public int getHeightHint(int displayId) throws RemoteException {
         synchronized (mLock) {
-            if (!isValidDisplay(displayId)) {
+            if (!mWallpaperDisplayHelper.isValidDisplay(displayId)) {
                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
             }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                final DisplayData wpdData = getDisplayDataOrCreate(displayId);
+                final DisplayData wpdData =
+                        mWallpaperDisplayHelper.getDisplayDataOrCreate(displayId);
                 return wpdData.mHeight;
             } else {
                 return 0;
@@ -2186,7 +2117,7 @@
             return;
         }
         synchronized (mLock) {
-            if (!isValidDisplay(displayId)) {
+            if (!mWallpaperDisplayHelper.isValidDisplay(displayId)) {
                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
             }
             int userId = UserHandle.getCallingUserId();
@@ -2195,7 +2126,7 @@
                 throw new IllegalArgumentException("padding must be positive: " + padding);
             }
 
-            int maxSize = getMaximumSizeDimension(displayId);
+            int maxSize = mWallpaperDisplayHelper.getMaximumSizeDimension(displayId);
 
             final int paddingWidth = padding.left + padding.right;
             final int paddingHeight = padding.top + padding.bottom;
@@ -2208,13 +2139,13 @@
                         + " exceeds max height " + maxSize);
             }
 
-            final DisplayData wpdData = getDisplayDataOrCreate(displayId);
+            final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(displayId);
             if (!padding.equals(wpdData.mPadding)) {
                 wpdData.mPadding.set(padding);
                 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+                    final DisplayConnector connector = wallpaper.connection
                             .getDisplayConnectorOrCreate(displayId);
                     final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
                     if (engine != null) {
@@ -2268,7 +2199,8 @@
                 return null;
             }
             // Only for default display.
-            final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
+            final DisplayData wpdData =
+                    mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
             try {
                 if (outParams != null) {
                     outParams.putInt("width", wpdData.mWidth);
@@ -3155,8 +3087,7 @@
                 Slog.w(TAG, "Failed detaching wallpaper service ", e);
             }
             mContext.unbindService(wallpaper.connection);
-            wallpaper.connection.forEachDisplayConnector(
-                    WallpaperConnection.DisplayConnector::disconnectLocked);
+            wallpaper.connection.forEachDisplayConnector(DisplayConnector::disconnectLocked);
             wallpaper.connection.mService = null;
             wallpaper.connection.mDisplayConnector.clear();
 
@@ -3286,7 +3217,7 @@
                 return;
             }
             if (supportsMultiDisplay(mLastWallpaper.connection)) {
-                final WallpaperConnection.DisplayConnector connector =
+                final DisplayConnector connector =
                         mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
                 if (connector == null) return;
                 connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
@@ -3295,7 +3226,7 @@
             // System wallpaper does not support multiple displays, attach this display to
             // the fallback wallpaper.
             if (mFallbackWallpaper != null) {
-                final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper
+                final DisplayConnector connector = mFallbackWallpaper
                         .connection.getDisplayConnectorOrCreate(displayId);
                 if (connector == null) return;
                 connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper);
@@ -3352,7 +3283,7 @@
         if (DEBUG) {
             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
         }
-        final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
+        final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
         out.startTag(null, tag);
         out.attributeInt(null, "id", wallpaper.wallpaperId);
         out.attributeInt(null, "width", wpdData.mWidth);
@@ -3536,7 +3467,7 @@
             initializeFallbackWallpaper();
         }
         boolean success = false;
-        final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
+        final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
         try {
             stream = new FileInputStream(file);
             TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -3613,7 +3544,7 @@
             }
         }
 
-        ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
+        mWallpaperDisplayHelper.ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
         ensureSaneWallpaperData(wallpaper);
         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
         if (lockWallpaper != null) {
@@ -3653,7 +3584,7 @@
             wallpaper.wallpaperId = makeWallpaperIdLocked();
         }
 
-        final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
+        final DisplayData wpData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
 
         if (!keepDimensionHints) {
             wpData.mWidth = parser.getAttributeInt(null, "width");
@@ -3866,7 +3797,7 @@
                 pw.print(" User "); pw.print(wallpaper.userId);
                 pw.print(": id="); pw.println(wallpaper.wallpaperId);
                 pw.println(" Display state:");
-                forEachDisplayData(wpSize -> {
+                mWallpaperDisplayHelper.forEachDisplayData(wpSize -> {
                     pw.print("  displayId=");
                     pw.println(wpSize.mDisplayId);
                     pw.print("  mWidth=");
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index af135b7..9e258cb 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -20,6 +20,7 @@
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
+import static android.view.ViewProtoEnums.DISPLAY_STATE_OFF;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONTENT_RECORDING;
 
@@ -317,6 +318,11 @@
         if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
             mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged(
                     mRecordedWindowContainer.asTask().isVisibleRequested());
+        } else {
+            int currentDisplayState =
+                    mRecordedWindowContainer.asDisplayContent().getDisplay().getState();
+            mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged(
+                    currentDisplayState != DISPLAY_STATE_OFF);
         }
 
         // No need to clean up. In SurfaceFlinger, parents hold references to their children. The
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f47c330..fa6fa53 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1092,13 +1092,13 @@
                 // (ACTION_DATE_CHANGED), or when manual clock adjustment is made
                 // (ACTION_TIME_CHANGED)
                 updateSystemUpdateFreezePeriodsRecord(/* saveIfChanged */ true);
-                final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
+                final int userId = getManagedUserId(mUserManager.getMainUser().getIdentifier());
                 if (userId >= 0) {
                     updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
                 }
             } else if (ACTION_PROFILE_OFF_DEADLINE.equals(action)) {
                 Slogf.i(LOG_TAG, "Profile off deadline alarm was triggered");
-                final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
+                final int userId = getManagedUserId(mUserManager.getMainUser().getIdentifier());
                 if (userId >= 0) {
                     updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
                 } else {
@@ -8598,9 +8598,20 @@
         Preconditions.checkArgument(admin != null);
 
         final CallerIdentity caller = getCallerIdentity();
-        // Cannot be called while holding the lock:
-        final boolean hasIncompatibleAccountsOrNonAdb =
-                hasIncompatibleAccountsOrNonAdbNoLock(caller, userId, admin);
+
+        boolean hasIncompatibleAccountsOrNonAdb =
+                !isAdb(caller) || hasIncompatibleAccountsOnAnyUser();
+
+        if (!hasIncompatibleAccountsOrNonAdb) {
+            synchronized (getLockObject()) {
+                if (!isAdminTestOnlyLocked(admin, userId) && hasAccountsOnAnyUser()) {
+                    Slogf.w(LOG_TAG,
+                            "Non test-only owner can't be installed with existing accounts.");
+                    return false;
+                }
+            }
+        }
+
         synchronized (getLockObject()) {
             enforceCanSetDeviceOwnerLocked(caller, admin, userId, hasIncompatibleAccountsOrNonAdb);
             Preconditions.checkArgument(isPackageInstalledForUser(admin.getPackageName(), userId),
@@ -14746,14 +14757,10 @@
         if (isAdb) {
             // If shell command runs after user setup completed check device status. Otherwise, OK.
             if (mIsWatch || hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
-                // In non-headless system user mode, DO can be setup only if
-                // there's no non-system user.
-                // In headless system user mode, DO can be setup only if there are
-                // two users: the headless system user and the foreground user.
-                // If there could be multiple foreground users, this constraint should be modified.
+                // DO can be setup only if there are no users which are neither created by default
+                // nor marked as FOR_TESTING
 
-                int maxNumberOfExistingUsers = isHeadlessSystemUserMode ? 2 : 1;
-                if (mUserManager.getUserCount() > maxNumberOfExistingUsers) {
+                if (nonTestNonPrecreatedUsersExist()) {
                     return STATUS_NONSYSTEM_USER_EXISTS;
                 }
 
@@ -14783,6 +14790,17 @@
         }
     }
 
+    /**
+     * True if there are any users on the device which were not setup by default (1 usually, 2 for
+     * devices with a headless system user) and also are not marked as FOR_TESTING.
+     */
+    private boolean nonTestNonPrecreatedUsersExist() {
+        int allowedUsers = UserManager.isHeadlessSystemUserMode() ? 2 : 1;
+        return mUserManagerInternal.getUsers(/* excludeDying= */ true).stream()
+                .filter(u -> !u.isForTesting())
+                .count() > allowedUsers;
+    }
+
     private int checkDeviceOwnerProvisioningPreCondition(@UserIdInt int callingUserId) {
         synchronized (getLockObject()) {
             final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
@@ -16054,8 +16072,10 @@
         wtfIfInLock();
 
         return mInjector.binderWithCleanCallingIdentity(() -> {
-            final AccountManager am = AccountManager.get(mContext);
-            final Account accounts[] = am.getAccountsAsUser(userId);
+            AccountManager am =
+                    mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0)
+                            .getSystemService(AccountManager.class);
+            Account[] accounts = am.getAccounts();
             if (accounts.length == 0) {
                 return false;
             }
@@ -18169,8 +18189,10 @@
                         .addAction(turnProfileOnButton)
                         .addExtras(extras)
                         .build();
-        mInjector.getNotificationManager().notify(
-                SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED, notification);
+
+        mInjector.getNotificationManager().notifyAsUser(
+                null, SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED, notification,
+                UserHandle.of(getProfileParentId(profileUserId)));
     }
 
     private String getPersonalAppSuspensionButtonText() {
@@ -19537,6 +19559,14 @@
     }
 
     @Override
+    public void resetShouldAllowBypassingDevicePolicyManagementRoleQualificationState() {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLE_HOLDERS));
+        setBypassDevicePolicyManagementRoleQualificationStateInternal(
+                /* currentRoleHolder= */ null, /* allowBypass= */ false);
+    }
+
+    @Override
     public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification() {
         Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
                 android.Manifest.permission.MANAGE_ROLE_HOLDERS));
@@ -19550,15 +19580,51 @@
     }
 
     private boolean shouldAllowBypassingDevicePolicyManagementRoleQualificationInternal() {
-        if (mUserManager.getUserCount() > 1) {
+        if (nonTestNonPrecreatedUsersExist()) {
             return false;
         }
-        AccountManager am = AccountManager.get(mContext);
-        Account[] accounts = am.getAccounts();
-        if (accounts.length == 0) {
-            return true;
+
+
+        return !hasIncompatibleAccountsOnAnyUser();
+    }
+
+    private boolean hasAccountsOnAnyUser() {
+        long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            for (UserInfo user : mUserManagerInternal.getUsers(/* excludeDying= */ true)) {
+                AccountManager am = mContext.createContextAsUser(
+                                UserHandle.of(user.id), /* flags= */ 0)
+                        .getSystemService(AccountManager.class);
+                Account[] accounts = am.getAccounts();
+                if (accounts.length != 0) {
+                    return true;
+                }
+            }
+
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
         }
-        return !hasIncompatibleAccounts(am, accounts);
+    }
+
+    private boolean hasIncompatibleAccountsOnAnyUser() {
+        long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            for (UserInfo user : mUserManagerInternal.getUsers(/* excludeDying= */ true)) {
+                AccountManager am = mContext.createContextAsUser(
+                        UserHandle.of(user.id), /* flags= */ 0)
+                        .getSystemService(AccountManager.class);
+                Account[] accounts = am.getAccounts();
+
+                if (hasIncompatibleAccounts(am, accounts)) {
+                    return true;
+                }
+            }
+
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
     }
 
     private void setBypassDevicePolicyManagementRoleQualificationStateInternal(
diff --git a/services/java/com/android/server/BootUserInitializer.java b/services/java/com/android/server/BootUserInitializer.java
index deebfc7..3d71739 100644
--- a/services/java/com/android/server/BootUserInitializer.java
+++ b/services/java/com/android/server/BootUserInitializer.java
@@ -17,7 +17,6 @@
 
 import android.annotation.UserIdInt;
 import android.content.ContentResolver;
-import android.content.pm.UserInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -27,8 +26,6 @@
 import com.android.server.utils.Slogf;
 import com.android.server.utils.TimingsTraceAndSlog;
 
-import java.util.List;
-
 /**
  * Class responsible for booting the device in the proper user on headless system user mode.
  *
@@ -56,50 +53,18 @@
         // this class or the setup wizard app
         provisionHeadlessSystemUser();
 
-        UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
-        t.traceBegin("get-existing-users");
-        List<UserInfo> existingUsers = um.getUsers(/* excludeDying= */ true);
-        t.traceEnd();
-
-        Slogf.d(TAG, "%d existing users", existingUsers.size());
-
-        int initialUserId = UserHandle.USER_NULL;
-
-        for (int i = 0; i < existingUsers.size(); i++) {
-            UserInfo user = existingUsers.get(i);
-            if (DEBUG) {
-                Slogf.d(TAG, "User at position %d: %s", i, user.toFullString());
-            }
-            if (user.id != UserHandle.USER_SYSTEM && user.isFull()) {
-                if (DEBUG) {
-                    Slogf.d(TAG, "Found initial user: %d", user.id);
-                }
-                initialUserId = user.id;
-                break;
-            }
-        }
-
-        if (initialUserId == UserHandle.USER_NULL) {
-            Slogf.d(TAG, "Creating initial user");
-            t.traceBegin("create-initial-user");
-            try {
-                int flags = UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN;
-                // TODO(b/204091126): proper name for user
-                UserInfo newUser = um.createUserEvenWhenDisallowed("Real User",
-                        UserManager.USER_TYPE_FULL_SECONDARY, flags,
-                        /* disallowedPackages= */ null, /* token= */ null);
-                Slogf.i(TAG, "Created initial user: %s", newUser.toFullString());
-                initialUserId = newUser.id;
-            } catch (Exception e) {
-                Slogf.wtf(TAG, "failed to created initial user", e);
-                return;
-            } finally {
-                t.traceEnd(); // create-initial-user
-            }
-        }
-
         unlockSystemUser(t);
-        switchToInitialUser(initialUserId);
+
+        try {
+            t.traceBegin("getBootUser");
+            int bootUser = LocalServices.getService(UserManagerInternal.class).getBootUser();
+            t.traceEnd();
+            t.traceBegin("switchToBootUser-" + bootUser);
+            switchToBootUser(bootUser);
+            t.traceEnd();
+        } catch (UserManager.CheckedUserOperationException e) {
+            Slogf.wtf(TAG, "Failed to created boot user", e);
+        }
     }
 
     /* TODO(b/261791491): STOPSHIP - SUW should be responsible for this. */
@@ -152,12 +117,12 @@
         }
     }
 
-    private void switchToInitialUser(@UserIdInt int initialUserId) {
-        Slogf.i(TAG, "Switching to initial user %d", initialUserId);
-        boolean started = mAms.startUserInForegroundWithListener(initialUserId,
+    private void switchToBootUser(@UserIdInt int bootUserId) {
+        Slogf.i(TAG, "Switching to boot user %d", bootUserId);
+        boolean started = mAms.startUserInForegroundWithListener(bootUserId,
                 /* unlockListener= */ null);
         if (!started) {
-            Slogf.wtf(TAG, "Failed to start user %d in foreground", initialUserId);
+            Slogf.wtf(TAG, "Failed to start user %d in foreground", bootUserId);
         }
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 1367af8..f13de12 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -20,25 +20,31 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.content.Context;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
 
 import androidx.test.annotation.UiThreadTest;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.internal.widget.LockSettingsInternal;
 import com.android.server.ExtendedMockitoTestCase;
 import com.android.server.LocalServices;
 import com.android.server.am.UserState;
 import com.android.server.pm.UserManagerService.UserData;
+import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -86,6 +92,10 @@
     private @Mock PackageManagerService mMockPms;
     private @Mock UserDataPreparer mMockUserDataPreparer;
     private @Mock ActivityManagerInternal mActivityManagerInternal;
+    private @Mock DeviceStorageMonitorInternal mDeviceStorageMonitorInternal;
+    private @Mock StorageManager mStorageManager;
+    private @Mock LockSettingsInternal mLockSettingsInternal;
+    private @Mock PackageManagerInternal mPackageManagerInternal;
 
     /**
      * Reference to the {@link UserManagerService} being tested.
@@ -101,7 +111,8 @@
     protected void initializeSession(StaticMockitoSessionBuilder builder) {
         builder
                 .spyStatic(UserManager.class)
-                .spyStatic(LocalServices.class);
+                .spyStatic(LocalServices.class)
+                .mockStatic(Settings.Global.class);
     }
 
     @Before
@@ -112,6 +123,14 @@
         // Called when WatchedUserStates is constructed
         doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache());
 
+        // Called when creating new users
+        when(mDeviceStorageMonitorInternal.isMemoryLow()).thenReturn(false);
+        mockGetLocalService(DeviceStorageMonitorInternal.class, mDeviceStorageMonitorInternal);
+        when(mSpiedContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
+        mockGetLocalService(LockSettingsInternal.class, mLockSettingsInternal);
+        mockGetLocalService(PackageManagerInternal.class, mPackageManagerInternal);
+        doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
+
         // Must construct UserManagerService in the UiThread
         mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
                 mPackagesLock, mRealContext.getDataDir(), mUsers);
@@ -223,6 +242,87 @@
                 .that(mUms.isUserRunning(PROFILE_USER_ID)).isFalse();
     }
 
+    @Test
+    public void testSetBootUser_SuppliedUserIsSwitchable() throws Exception {
+        addUser(USER_ID);
+        addUser(OTHER_USER_ID);
+
+        mUms.setBootUser(OTHER_USER_ID);
+
+        assertWithMessage("getBootUser")
+                .that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
+    }
+
+    @Test
+    public void testSetBootUser_NotHeadless_SuppliedUserIsNotSwitchable() throws Exception {
+        setSystemUserHeadless(false);
+        addUser(USER_ID);
+        addUser(OTHER_USER_ID);
+        addDefaultProfileAndParent();
+
+        mUms.setBootUser(PROFILE_USER_ID);
+
+        assertWithMessage("getBootUser")
+                .that(mUmi.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
+    }
+
+    @Test
+    public void testSetBootUser_Headless_SuppliedUserIsNotSwitchable() throws Exception {
+        setSystemUserHeadless(true);
+        addUser(USER_ID);
+        setLastForegroundTime(USER_ID, 1_000_000L);
+        addUser(OTHER_USER_ID);
+        setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+        addDefaultProfileAndParent();
+
+        mUms.setBootUser(PROFILE_USER_ID);
+
+        // Boot user not switchable so return most recently in foreground.
+        assertWithMessage("getBootUser")
+                .that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
+    }
+
+    @Test
+    public void testGetBootUser_NotHeadless_ReturnsSystemUser() throws Exception {
+        setSystemUserHeadless(false);
+        addUser(USER_ID);
+        addUser(OTHER_USER_ID);
+
+        assertWithMessage("getBootUser")
+                .that(mUmi.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
+    }
+
+    @Test
+    public void testGetBootUser_Headless_ReturnsMostRecentlyInForeground() throws Exception {
+        setSystemUserHeadless(true);
+        addUser(USER_ID);
+        setLastForegroundTime(USER_ID, 1_000_000L);
+
+        addUser(OTHER_USER_ID);
+        setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+        assertWithMessage("getBootUser")
+                .that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
+    }
+
+    @Test
+    public void testGetBootUser_Headless_UserCreatedIfOnlySystemUserExists() throws Exception {
+        setSystemUserHeadless(true);
+
+        int bootUser = mUmi.getBootUser();
+
+        assertWithMessage("getStartingUser")
+                .that(bootUser).isNotEqualTo(UserHandle.USER_SYSTEM);
+
+        UserData newUser = mUsers.get(bootUser);
+        assertWithMessage("New boot user is a full user")
+                .that(newUser.info.isFull()).isTrue();
+        assertWithMessage("New boot user is an admin user")
+                .that(newUser.info.isAdmin()).isTrue();
+        assertWithMessage("New boot user is the main user")
+                .that(newUser.info.isMain()).isTrue();
+    }
+
     private void mockCurrentUser(@UserIdInt int userId) {
         mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
 
@@ -248,7 +348,7 @@
 
     private void addUser(@UserIdInt int userId) {
         TestUserData userData = new TestUserData(userId);
-
+        userData.info.flags = UserInfo.FLAG_FULL;
         addUserData(userData);
     }
 
@@ -277,6 +377,23 @@
         mUsers.put(userData.info.id, userData);
     }
 
+    private void setSystemUserHeadless(boolean headless) {
+        UserData systemUser = mUsers.get(UserHandle.USER_SYSTEM);
+        if (headless) {
+            systemUser.info.flags &= ~UserInfo.FLAG_FULL;
+            systemUser.info.userType = UserManager.USER_TYPE_SYSTEM_HEADLESS;
+        } else {
+            systemUser.info.flags |= UserInfo.FLAG_FULL;
+            systemUser.info.userType = UserManager.USER_TYPE_FULL_SYSTEM;
+        }
+        doReturn(headless).when(() -> UserManager.isHeadlessSystemUserMode());
+    }
+
+    private void setLastForegroundTime(@UserIdInt int userId, long timeMillis) {
+        UserData userData = mUsers.get(userId);
+        userData.mLastEnteredForegroundTimeMillis = timeMillis;
+    }
+
     private static final class TestUserData extends UserData {
 
         @SuppressWarnings("deprecation")
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index c08f6bf..a924207 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -306,12 +306,13 @@
         verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
         verifyCurrentSystemData(testUserId);
 
-        spyOn(mService.mLastWallpaper.connection);
-        doReturn(true).when(mService.mLastWallpaper.connection).isUsableDisplay(any());
+        spyOn(mService.mWallpaperDisplayHelper);
+        doReturn(true).when(mService.mWallpaperDisplayHelper)
+                .isUsableDisplay(any(Display.class), mService.mLastWallpaper.connection.mClientUid);
         mService.mLastWallpaper.connection.attachEngine(mock(IWallpaperEngine.class),
                 DEFAULT_DISPLAY);
 
-        WallpaperManagerService.WallpaperConnection.DisplayConnector connector =
+        WallpaperManagerService.DisplayConnector connector =
                 mService.mLastWallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY);
         mService.setWallpaperComponent(sDefaultWallpaperComponent, FLAG_SYSTEM, testUserId);
 
@@ -521,7 +522,7 @@
     }
 
     private void verifyDisplayData() {
-        mService.forEachDisplayData(data -> {
+        mService.mWallpaperDisplayHelper.forEachDisplayData(data -> {
             assertTrue("Display width must larger than maximum screen size",
                     data.mWidth >= DISPLAY_SIZE_DIMENSION);
             assertTrue("Display height must larger than maximum screen size",
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index e54a48b..1298e7b 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -1292,10 +1292,11 @@
         unlockSystemUser();
         try {
             mAms.hasFeatures(
-                null, // response
-                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
-                new String[] {"feature1", "feature2"}, // features
-                "testPackage"); // opPackageName
+                    null, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                    new String[] {"feature1", "feature2"}, // features
+                    0, // userId
+                    "testPackage"); // opPackageName
             fail("IllegalArgumentException expected. But no exception was thrown.");
         } catch (IllegalArgumentException e) {
             // IllegalArgumentException is expected.
@@ -1307,10 +1308,11 @@
         unlockSystemUser();
         try {
             mAms.hasFeatures(
-                mMockAccountManagerResponse, // response
-                null, // account
-                new String[] {"feature1", "feature2"}, // features
-                "testPackage"); // opPackageName
+                    mMockAccountManagerResponse, // response
+                    null, // account
+                    new String[] {"feature1", "feature2"}, // features
+                    0, // userId
+                    "testPackage"); // opPackageName
             fail("IllegalArgumentException expected. But no exception was thrown.");
         } catch (IllegalArgumentException e) {
             // IllegalArgumentException is expected.
@@ -1325,6 +1327,7 @@
                     mMockAccountManagerResponse, // response
                     AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, // account
                     null, // features
+                    0, // userId
                     "testPackage"); // opPackageName
             fail("IllegalArgumentException expected. But no exception was thrown.");
         } catch (IllegalArgumentException e) {
@@ -1341,6 +1344,7 @@
                 response, // response
                 AccountManagerServiceTestFixtures.ACCOUNT_ERROR, // account
                 AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, // features
+                0, // userId
                 "testPackage"); // opPackageName
         waitForLatch(latch);
         verify(mMockAccountManagerResponse).onError(
@@ -1357,6 +1361,7 @@
                 response, // response
                 AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, // account
                 AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, // features
+                0, // userId
                 "testPackage"); // opPackageName
         waitForLatch(latch);
         verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
index d4e3d44..0dd60b8 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
@@ -24,6 +24,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -95,7 +96,7 @@
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
         doNothing().when(mAccountManagerInternal).addOnAppPermissionChangeListener(any());
         when(mJobSchedulerInternal.getSystemScheduledPendingJobs()).thenReturn(new ArrayList<>());
-        mSyncManager = new SyncManagerWithMockedServices(mContext, true);
+        mSyncManager = spy(new SyncManagerWithMockedServices(mContext, true));
     }
 
     public void testSyncExtrasEquals_WithNull() throws Exception {
@@ -233,6 +234,7 @@
     }
 
     public void testShouldDisableSync() {
+        doReturn(true).when(mSyncManager).isContactSharingAllowedForCloneProfile();
         UserInfo primaryUserInfo = createUserInfo("primary", 0 /* id */, 0 /* groupId */,
                 UserInfo.FLAG_PRIMARY | UserInfo.FLAG_ADMIN);
         UserInfo cloneUserInfo = createUserInfo("clone", 10 /* id */, 0 /* groupId */,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 61c3f13..210aeef 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -7396,7 +7396,7 @@
         verify(getServices().alarmManager, times(1)).set(anyInt(), eq(PROFILE_OFF_DEADLINE), any());
         // Now the user should see a warning notification.
         verify(getServices().notificationManager, times(1))
-                .notify(anyInt(), any());
+                .notifyAsUser(any(), anyInt(), any(), any());
         // Apps shouldn't be suspended yet.
         verifyZeroInteractions(getServices().ipackageManager);
         clearInvocations(getServices().alarmManager);
@@ -7410,7 +7410,7 @@
         verifyZeroInteractions(getServices().alarmManager);
         // Now the user should see a notification about suspended apps.
         verify(getServices().notificationManager, times(1))
-                .notify(anyInt(), any());
+                .notifyAsUser(any(), anyInt(), any(), any());
         // Verify that the apps are suspended.
         verify(getServices().ipackageManager, times(1)).setPackagesSuspendedAsUser(
                 any(), eq(true), any(), any(), any(), any(), anyInt());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 4ad8516..8cc362c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -280,7 +280,7 @@
     }
 
     @Test
-    public void testStartRecording_notifiesCallback() {
+    public void testStartRecording_notifiesCallback_taskSession() {
         // WHEN a recording is ongoing.
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mContentRecorder.updateRecording();
@@ -292,6 +292,18 @@
     }
 
     @Test
+    public void testStartRecording_notifiesCallback_displaySession() {
+        // WHEN a recording is ongoing.
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        // THEN the visibility change callback is notified.
+        verify(mMediaProjectionManagerWrapper)
+                .notifyActiveProjectionCapturedContentVisibilityChanged(true);
+    }
+
+    @Test
     public void testOnVisibleRequestedChanged_notifiesCallback() {
         // WHEN a recording is ongoing.
         mContentRecorder.setContentRecordingSession(mTaskSession);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index c373305..665d5e7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -635,6 +635,7 @@
                     return;
                 }
             }
+            //TODO(b265535257): report error to either service only.
             synchronized (HotwordDetectionConnection.this.mLock) {
                 runForEachDetectorSessionLocked((session) -> {
                     session.reportErrorLocked(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 19aa374..38bf9c2 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -331,6 +331,12 @@
         @GuardedBy("this")
         private boolean mTemporarilyDisabled;
 
+        /** The start value of showSessionId */
+        private static final int SHOW_SESSION_START_ID = 0;
+
+        @GuardedBy("this")
+        private int mShowSessionId = SHOW_SESSION_START_ID;
+
         private final boolean mEnableService;
         // TODO(b/226201975): remove reference once RoleService supports pre-created users
         private final RoleObserver mRoleObserver;
@@ -350,6 +356,24 @@
             }
         }
 
+        int getNextShowSessionId() {
+            synchronized (this) {
+                // Reset the showSessionId to SHOW_SESSION_START_ID to avoid the value exceeds
+                // Integer.MAX_VALUE
+                if (mShowSessionId == Integer.MAX_VALUE - 1) {
+                    mShowSessionId = SHOW_SESSION_START_ID;
+                }
+                mShowSessionId++;
+                return mShowSessionId;
+            }
+        }
+
+        int getShowSessionId() {
+            synchronized (this) {
+                return mShowSessionId;
+            }
+        }
+
         @Override
         public @NonNull IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
                 @NonNull Identity originatorIdentity, IBinder client) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 4ee3306..04c8f8f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityManager.START_VOICE_HIDDEN_SESSION;
 import static android.app.ActivityManager.START_VOICE_NOT_ACTIVE_SESSION;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.service.voice.VoiceInteractionService.KEY_SHOW_SESSION_ID;
 
 import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
 
@@ -255,13 +256,17 @@
                 /* direct= */ true);
     }
 
-    public boolean showSessionLocked(@NonNull Bundle args, int flags,
+    public boolean showSessionLocked(@Nullable Bundle args, int flags,
             @Nullable String attributionTag,
             @Nullable IVoiceInteractionSessionShowCallback showCallback,
             @Nullable IBinder activityToken) {
+        final int sessionId = mServiceStub.getNextShowSessionId();
+        final Bundle newArgs = args == null ? new Bundle() : args;
+        newArgs.putInt(KEY_SHOW_SESSION_ID, sessionId);
+
         try {
             if (mService != null) {
-                mService.prepareToShowSession(args, flags);
+                mService.prepareToShowSession(newArgs, flags);
             }
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException while calling prepareToShowSession", e);
@@ -275,7 +280,9 @@
         if (!mActiveSession.mBound) {
             try {
                 if (mService != null) {
-                    mService.showSessionFailed();
+                    Bundle failedArgs = new Bundle();
+                    failedArgs.putInt(KEY_SHOW_SESSION_ID, sessionId);
+                    mService.showSessionFailed(failedArgs);
                 }
             } catch (RemoteException e) {
                 Slog.w(TAG, "RemoteException while calling showSessionFailed", e);
@@ -300,7 +307,7 @@
         } else {
             visibleActivities = allVisibleActivities;
         }
-        return mActiveSession.showLocked(args, flags, attributionTag, mDisabledShowContext,
+        return mActiveSession.showLocked(newArgs, flags, attributionTag, mDisabledShowContext,
                 showCallback, visibleActivities);
     }
 
@@ -673,6 +680,21 @@
             Slog.w(TAG, "Visual query detection service not in isolated process");
             throw new IllegalStateException("Visual query detection not in isolated process");
         }
+        if (!Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE.equals(
+                visualQueryDetectionServiceInfo.permission)) {
+            Slog.w(TAG, "Visual query detection does not require permission "
+                    + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE);
+            throw new SecurityException("Visual query detection does not require permission "
+                    + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE);
+        }
+        if (mContext.getPackageManager().checkPermission(
+                Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE,
+                mInfo.getServiceInfo().packageName) == PackageManager.PERMISSION_GRANTED) {
+            Slog.w(TAG, "Voice interaction service should not hold permission "
+                    + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE);
+            throw new SecurityException("Voice interaction service should not hold permission "
+                    + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE);
+        }
         if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
             Slog.w(TAG, "Can't set sharedMemory to be read-only");
             throw new IllegalStateException("Can't set sharedMemory to be read-only");
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
new file mode 100644
index 0000000..41316d8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 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.server.wm.flicker.launch
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.KeyEvent
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
+import com.android.server.wm.flicker.helpers.CameraAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launching camera from launcher by double pressing power button
+ *
+ * To run this test: `atest FlickerTests:OpenCameraOnDoubleClickPowerButton`
+ *
+ * Actions:
+ * ```
+ *     Make sure no apps are running on the device
+ *     Launch an app [testApp] and wait animation to complete
+ * ```
+ * Notes:
+ * ```
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [OpenAppTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@FlickerServiceCompatible
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) :
+    OpenAppFromLauncherTransition(flicker) {
+    private val cameraApp = CameraAppHelper(instrumentation)
+
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            setup {
+                RemoveAllTasksButHomeRule.removeAllTasksButHome()
+                this.setRotation(flicker.scenario.startRotation)
+            }
+            transitions {
+                device.pressKeyCode(KeyEvent.KEYCODE_POWER)
+                device.pressKeyCode(KeyEvent.KEYCODE_POWER)
+                wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(cameraApp).waitForAndVerify()
+            }
+            teardown { RemoveAllTasksButHomeRule.removeAllTasksButHome() }
+        }
+
+    @Postsubmit @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+
+    @Postsubmit @Test override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
+
+    @Postsubmit @Test override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
+    @Postsubmit @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+    @Postsubmit @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+    @Postsubmit @Test override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
+
+    @Postsubmit
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() =
+        super.appWindowReplacesLauncherAsTopWindow()
+
+    @Postsubmit @Test override fun focusChanges() = super.focusChanges()
+
+    @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun statusBarLayerIsVisibleAtStartAndEnd() =
+        super.statusBarLayerIsVisibleAtStartAndEnd()
+
+    @Postsubmit
+    @Test
+    override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+    @Postsubmit
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+    @Postsubmit
+    @Test
+    override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Postsubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+         * navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<FlickerTest> {
+            return FlickerTestFactory.nonRotationTests()
+        }
+    }
+}
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index f2234fb..21007ef 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -74,6 +74,7 @@
     ],
     test_suites: ["general-tests"],
     test_config: "MultiUserRollbackTest.xml",
+    data : [":RollbackTest"],
 }
 
 java_library_host {