Merge "Create default implementation for getSyncTarget"
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 463d0c49..fccb3c0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1026,6 +1026,7 @@
   }
 
   public class Typeface {
+    method @NonNull public static android.util.Pair<java.util.List<android.graphics.Typeface>,java.util.List<android.graphics.Typeface>> changeDefaultFontForTest(@NonNull java.util.List<android.graphics.Typeface>, @NonNull java.util.List<android.graphics.Typeface>);
     method @NonNull public static long[] deserializeFontMap(@NonNull java.nio.ByteBuffer, @NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws java.io.IOException;
     method @Nullable public static android.os.SharedMemory getSystemFontMapSharedMemory();
     method @NonNull public static android.os.SharedMemory serializeFontMap(@NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws android.system.ErrnoException, java.io.IOException;
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 91d6a9b..40f7533 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -117,6 +117,7 @@
     /**
      * Shuts down the service
      */
+    @EnforcePermission("SHUTDOWN")
     void shutdown();
 
     /**
@@ -277,6 +278,7 @@
      */
     void setUidOnMeteredNetworkDenylist(int uid, boolean enable);
     void setUidOnMeteredNetworkAllowlist(int uid, boolean enable);
+    @EnforcePermission("NETWORK_SETTINGS")
     boolean setDataSaverModeEnabled(boolean enable);
 
     void setUidCleartextNetworkPolicy(int uid, int policy);
@@ -308,5 +310,6 @@
     void removeInterfaceFromLocalNetwork(String iface);
     int removeRoutesFromLocalNetwork(in List<RouteInfo> routes);
 
+    @EnforcePermission("OBSERVE_NETWORK_POLICY")
     boolean isNetworkRestricted(int uid);
 }
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
index 8dbaec4..60edf35 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
@@ -139,11 +139,13 @@
             @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
             @Nullable IRemoteInputConnection remoteInputConnection,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion, @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         try {
             return mTarget.startInputOrWindowGainedFocus(startInputReason, client, windowToken,
                     startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
-                    remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, imeDispatcher);
+                    remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
+                    imeDispatcher);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index aacd4ec..8ce134e7 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -285,9 +285,10 @@
     private static final String SUBTYPE_MODE_VOICE = "voice";
 
     /**
-     * Provide this to {@link IInputMethodManager#startInputOrWindowGainedFocus(
-     * int, IInputMethodClient, IBinder, int, int, int, EditorInfo,
-     * com.android.internal.inputmethod.IRemoteInputConnection, int)} to receive
+     * Provide this to {@link IInputMethodManagerInvoker#startInputOrWindowGainedFocus(int,
+     * IInputMethodClient, IBinder, int, int, int, EditorInfo,
+     * com.android.internal.inputmethod.IRemoteInputConnection, IRemoteAccessibilityInputConnection,
+     * int, int, ImeOnBackInvokedDispatcher)} to receive
      * {@link android.window.OnBackInvokedCallback} registrations from IME.
      */
     private final ImeOnBackInvokedDispatcher mImeDispatcher =
@@ -790,7 +791,7 @@
                         null,
                         null, null,
                         mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
-                        mImeDispatcher);
+                        UserHandle.myUserId(), mImeDispatcher);
             }
         }
 
@@ -2331,22 +2332,22 @@
         // Okay we are now ready to call into the served view and have it
         // do its stuff.
         // Life is good: let's hook everything up!
-        EditorInfo tba = new EditorInfo();
+        EditorInfo editorInfo = new EditorInfo();
         // Note: Use Context#getOpPackageName() rather than Context#getPackageName() so that the
         // system can verify the consistency between the uid of this process and package name passed
         // from here. See comment of Context#getOpPackageName() for details.
-        tba.packageName = view.getContext().getOpPackageName();
-        tba.autofillId = view.getAutofillId();
-        tba.fieldId = view.getId();
-        InputConnection ic = view.onCreateInputConnection(tba);
-        if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
+        editorInfo.packageName = view.getContext().getOpPackageName();
+        editorInfo.autofillId = view.getAutofillId();
+        editorInfo.fieldId = view.getId();
+        InputConnection ic = view.onCreateInputConnection(editorInfo);
+        if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic);
 
         // Clear autofill and field ids if a connection could not be established.
         // This ensures that even disconnected EditorInfos have well-defined attributes,
         // making them consistently and straightforwardly comparable.
         if (ic == null) {
-            tba.autofillId = AutofillId.NO_AUTOFILL_ID;
-            tba.fieldId = 0;
+            editorInfo.autofillId = AutofillId.NO_AUTOFILL_ID;
+            editorInfo.fieldId = 0;
         }
 
         final Handler icHandler;
@@ -2378,7 +2379,7 @@
             }
 
             // Hook 'em up and let 'er rip.
-            mCurrentEditorInfo = tba.createCopyInternal();
+            mCurrentEditorInfo = editorInfo.createCopyInternal();
             // Store the previously served connection so that we can determine whether it is safe
             // to skip the call to startInputOrWindowGainedFocus in the IMMS
             final RemoteInputConnectionImpl previouslyServedConnection = mServedInputConnection;
@@ -2391,8 +2392,8 @@
             }
             final RemoteInputConnectionImpl servedInputConnection;
             if (ic != null) {
-                mCursorSelStart = tba.initialSelStart;
-                mCursorSelEnd = tba.initialSelEnd;
+                mCursorSelStart = editorInfo.initialSelStart;
+                mCursorSelEnd = editorInfo.initialSelEnd;
                 mInitialSelStart = mCursorSelStart;
                 mInitialSelEnd = mCursorSelEnd;
                 mCursorCandStart = -1;
@@ -2418,7 +2419,7 @@
 
             if (DEBUG) {
                 Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
-                        + ic + " tba=" + tba + " startInputFlags="
+                        + ic + " editorInfo=" + editorInfo + " startInputFlags="
                         + InputMethodDebug.startInputFlagsToString(startInputFlags));
             }
 
@@ -2437,19 +2438,21 @@
                 }
                 return false;
             }
+            final int targetUserId = editorInfo.targetInputMethodUser != null
+                    ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();
             res = mServiceInvoker.startInputOrWindowGainedFocus(
                     startInputReason, mClient, windowGainingFocus, startInputFlags,
-                    softInputMode, windowFlags, tba, servedInputConnection,
+                    softInputMode, windowFlags, editorInfo, servedInputConnection,
                     servedInputConnection == null ? null
                             : servedInputConnection.asIRemoteAccessibilityInputConnection(),
-                    view.getContext().getApplicationInfo().targetSdkVersion,
+                    view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
                     mImeDispatcher);
             if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
             if (res == null) {
                 Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
                         + " null. startInputReason="
                         + InputMethodDebug.startInputReasonToString(startInputReason)
-                        + " editorInfo=" + tba
+                        + " editorInfo=" + editorInfo
                         + " startInputFlags="
                         + InputMethodDebug.startInputFlagsToString(startInputFlags));
                 return false;
@@ -2491,9 +2494,9 @@
         if (ic != null && res != null && res.method != null) {
             if (DEBUG) {
                 Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view
-                        + ", ic=" + ic + ", tba=" + tba + ", handler=" + icHandler);
+                        + ", ic=" + ic + ", editorInfo=" + editorInfo + ", handler=" + icHandler);
             }
-            reportInputConnectionOpened(ic, tba, icHandler, view);
+            reportInputConnectionOpened(ic, editorInfo, icHandler, view);
         }
 
         return true;
@@ -2541,8 +2544,8 @@
     }
 
     private void reportInputConnectionOpened(
-            InputConnection ic, EditorInfo tba, Handler icHandler, View view) {
-        view.onInputConnectionOpenedInternal(ic, tba, icHandler);
+            InputConnection ic, EditorInfo editorInfo, Handler icHandler, View view) {
+        view.onInputConnectionOpenedInternal(ic, editorInfo, icHandler);
         final ViewRootImpl viewRoot = view.getViewRootImpl();
         if (viewRoot != null) {
             viewRoot.getHandwritingInitiator().onInputConnectionCreated(view);
diff --git a/core/java/com/android/internal/app/procstats/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
index a2eca3a..84b2a35 100644
--- a/core/java/com/android/internal/app/procstats/IProcessStats.aidl
+++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
@@ -21,7 +21,9 @@
 import com.android.internal.app.procstats.ProcessStats;
 
 interface IProcessStats {
+    @EnforcePermission("PACKAGE_USAGE_STATS")
     byte[] getCurrentStats(out List<ParcelFileDescriptor> historic);
+    @EnforcePermission("PACKAGE_USAGE_STATS")
     ParcelFileDescriptor getStatsOverTime(long minTime);
     int getCurrentMemoryState();
 
@@ -43,6 +45,7 @@
      * @param List of Files of individual commits in protobuf binary or one that is merged from them.
      * @param ProcessStats object that will be used to return the full set of merged stats.
      */
+     @EnforcePermission("PACKAGE_USAGE_STATS")
      long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate,
         out List<ParcelFileDescriptor> committedStats, out ProcessStats mergedStats);
 
diff --git a/core/java/com/android/internal/inputmethod/ImeTracing.java b/core/java/com/android/internal/inputmethod/ImeTracing.java
index 8b21b7e..ee67131 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracing.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracing.java
@@ -17,6 +17,7 @@
 package com.android.internal.inputmethod;
 
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.app.ActivityThread;
 import android.content.Context;
 import android.os.RemoteException;
@@ -93,6 +94,7 @@
     /**
      * Calling {@link IInputMethodManager#startImeTrace()}} to capture IME trace.
      */
+    @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
     public final void startImeTrace() {
         try {
             mService.startImeTrace();
@@ -104,6 +106,7 @@
     /**
      * Calling {@link IInputMethodManager#stopImeTrace()} to stop IME trace.
      */
+    @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING)
     public final void stopImeTrace() {
         try {
             mService.stopImeTrace();
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1b77147..eb8b860 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -72,7 +72,8 @@
             /* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags,
             in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
             in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion, in ImeOnBackInvokedDispatcher imeDispatcher);
+            int unverifiedTargetSdkVersion, int userId,
+            in ImeOnBackInvokedDispatcher imeDispatcher);
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
@@ -105,12 +106,22 @@
 
     /** Remove the IME surface. Requires passing the currently focused window. */
     oneway void removeImeSurfaceFromWindowAsync(in IBinder windowToken);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
     void startProtoDump(in byte[] protoDump, int source, String where);
+
     boolean isImeTraceEnabled();
 
     // Starts an ime trace.
+    @EnforcePermission("CONTROL_UI_TRACING")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.CONTROL_UI_TRACING)")
     void startImeTrace();
+
     // Stops an ime trace.
+    @EnforcePermission("CONTROL_UI_TRACING")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.CONTROL_UI_TRACING)")
     void stopImeTrace();
 
     /** Start Stylus handwriting session **/
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index d52c70b..ae1c9461 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -907,6 +907,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "-1237827119": {
+      "message": "Schedule remove starting %s startingWindow=%s animate=%b Callers=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STARTING_WINDOW",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1228653755": {
       "message": "Launch on display check: displayId=%d callingPid=%d callingUid=%d",
       "level": "DEBUG",
@@ -1015,12 +1021,6 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
-    "-1128015008": {
-      "message": "Schedule remove starting %s startingWindow=%s startingView=%s Callers=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-1117599386": {
       "message": "Deferring rotation, display is not enabled.",
       "level": "VERBOSE",
@@ -3139,6 +3139,12 @@
       "group": "WM_DEBUG_LOCKTASK",
       "at": "com\/android\/server\/wm\/LockTaskController.java"
     },
+    "956467125": {
+      "message": "Reparenting Activity to embedded TaskFragment, but the Activity is not collected",
+      "level": "WARN",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
+    },
     "958338552": {
       "message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found",
       "level": "VERBOSE",
@@ -3877,12 +3883,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "1742235936": {
-      "message": "Removing startingView=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "1746778201": {
       "message": "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
       "level": "INFO",
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 596d061..f4f5e1e 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -50,6 +50,7 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.LruCache;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1396,6 +1397,41 @@
         }
     }
 
+    /**
+     * Change default typefaces for testing purpose.
+     *
+     * Note: The existing TextView or Paint instance still holds the old Typeface.
+     *
+     * @param defaults array of [default, default_bold, default_italic, default_bolditalic].
+     * @param genericFamilies array of [sans-serif, serif, monospace]
+     * @return return the old defaults and genericFamilies
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static Pair<List<Typeface>, List<Typeface>> changeDefaultFontForTest(
+            @NonNull List<Typeface> defaults,
+            @NonNull List<Typeface> genericFamilies
+    ) {
+        synchronized (SYSTEM_FONT_MAP_LOCK) {
+            List<Typeface> oldDefaults = Arrays.asList(sDefaults);
+            sDefaults = defaults.toArray(new Typeface[4]);
+            setDefault(defaults.get(0));
+
+            ArrayList<Typeface> oldGenerics = new ArrayList<>();
+            oldGenerics.add(sSystemFontMap.get("sans-serif"));
+            sSystemFontMap.put("sans-serif", genericFamilies.get(0));
+
+            oldGenerics.add(sSystemFontMap.get("serif"));
+            sSystemFontMap.put("serif", genericFamilies.get(1));
+
+            oldGenerics.add(sSystemFontMap.get("monospace"));
+            sSystemFontMap.put("monospace", genericFamilies.get(2));
+
+            return new Pair<>(oldDefaults, oldGenerics);
+        }
+    }
+
     static {
         // Preload Roboto-Regular.ttf in Zygote for improving app launch performance.
         preloadFontFile("/system/fonts/Roboto-Regular.ttf");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f85f9d6..5dd5149 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -411,8 +411,8 @@
 
     @WMSingleton
     @Provides
-    static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper() {
-        return new PipSurfaceTransactionHelper();
+    static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
+        return new PipSurfaceTransactionHelper(context);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index c0bc108..3ac08a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -39,6 +39,10 @@
     private int mCornerRadius;
     private int mShadowRadius;
 
+    public PipSurfaceTransactionHelper(Context context) {
+        onDensityOrFontScaleChanged(context);
+    }
+
     /**
      * Called when display size or font size of settings changed
      *
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 913e4b7..1155ea1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -445,7 +445,7 @@
             // When exit to fullscreen with Shell transition enabled, we update the Task windowing
             // mode directly so that it can also trigger display rotation and visibility update in
             // the same transition if there will be any.
-            wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+            wct.setWindowingMode(mToken, getOutPipWindowingMode());
             // We can inherit the parent bounds as it is going to be fullscreen. The
             // destinationBounds calculated above will be incorrect if this is with rotation.
             wct.setBounds(mToken, null);
@@ -544,7 +544,7 @@
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             wct.setBounds(mToken, null);
-            wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+            wct.setWindowingMode(mToken, getOutPipWindowingMode());
             wct.reorder(mToken, false);
             mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
                     null /* destinationBounds */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 53ec39d..7fb961f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -352,41 +352,31 @@
 
     public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
             @SplitPosition int position, @Nullable Bundle options) {
+        if (fillInIntent == null) {
+            fillInIntent = new Intent();
+        }
+        // Flag this as a no-user-action launch to prevent sending user leaving event to the
+        // current top activity since it's going to be put into another side of the split. This
+        // prevents the current top activity from going into pip mode due to user leaving event.
+        fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
+
+        // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
+        // split.
+        if (isLaunchingAdjacently(intent.getIntent(), position)) {
+            fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+        }
+
         if (!ENABLE_SHELL_TRANSITIONS) {
             startIntentLegacy(intent, fillInIntent, position, options);
             return;
         }
 
-        try {
-            options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
-                    null /* wct */);
-
-            if (fillInIntent == null) {
-                fillInIntent = new Intent();
-            }
-            // Flag this as a no-user-action launch to prevent sending user leaving event to the
-            // current top activity since it's going to be put into another side of the split. This
-            // prevents the current top activity from going into pip mode due to user leaving event.
-            fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
-
-            // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
-            // split.
-            if (isLaunchingAdjacently(intent.getIntent(), position)) {
-                fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
-            }
-
-            intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
-                    null /* requiredPermission */, options);
-        } catch (PendingIntent.CanceledException e) {
-            Slog.e(TAG, "Failed to launch task", e);
-        }
+        mStageCoordinator.startIntent(intent, fillInIntent, position, options);
     }
 
-    private void startIntentLegacy(PendingIntent intent, @Nullable Intent fillInIntent,
+    private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
             @SplitPosition int position, @Nullable Bundle options) {
-        boolean startSameActivityAdjacently = isLaunchingAdjacently(intent.getIntent(), position);
-
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         mStageCoordinator.prepareEvictChildTasks(position, evictWct);
 
@@ -397,8 +387,8 @@
                     IRemoteAnimationFinishedCallback finishedCallback,
                     SurfaceControl.Transaction t) {
                 if (apps == null || apps.length == 0) {
-                    if (startSameActivityAdjacently) {
-                        // Switch split position if dragging the same activity to another side.
+                    // Switch the split position if launching as MULTIPLE_TASK failed.
+                    if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
                         setSideStagePosition(SplitLayout.reversePosition(
                                 mStageCoordinator.getSideStagePosition()));
                     }
@@ -408,8 +398,6 @@
                     return;
                 }
 
-                mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
-                        false /* applyResizingOffset */);
                 for (int i = 0; i < apps.length; ++i) {
                     if (apps[i].mode == MODE_OPENING) {
                         t.show(apps[i].leash);
@@ -432,18 +420,6 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
 
-        // Flag this as a no-user-action launch to prevent sending user leaving event to the current
-        // top activity since it's going to be put into another side of the split. This prevents the
-        // current top activity from going into pip mode due to user leaving event.
-        if (fillInIntent == null) {
-            fillInIntent = new Intent();
-        }
-        fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
-        if (startSameActivityAdjacently) {
-            fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
-        }
-
         wct.sendPendingIntent(intent, fillInIntent, options);
         mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 056cd58..83bdf8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -62,13 +62,12 @@
     private final Runnable mOnFinish;
 
     DismissTransition mPendingDismiss = null;
-    IBinder mPendingEnter = null;
-    IBinder mPendingRecent = null;
+    TransitSession mPendingEnter = null;
+    TransitSession mPendingRecent = null;
 
     private IBinder mAnimatingTransition = null;
     OneShotRemoteHandler mPendingRemoteHandler = null;
     private OneShotRemoteHandler mActiveRemoteHandler = null;
-    private boolean mEnterTransitionMerged;
 
     private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
 
@@ -145,7 +144,7 @@
                 continue;
             }
 
-            if (transition == mPendingEnter && (mainRoot.equals(change.getContainer())
+            if (isPendingEnter(transition) && (mainRoot.equals(change.getContainer())
                     || sideRoot.equals(change.getContainer()))) {
                 t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
                 t.setWindowCrop(leash, change.getEndAbsBounds().width(),
@@ -171,12 +170,40 @@
         onFinish(null /* wct */, null /* wctCB */);
     }
 
+    boolean isPendingTransition(IBinder transition) {
+        return isPendingEnter(transition)
+                || isPendingDismiss(transition)
+                || isPendingRecent(transition);
+    }
+
+    boolean isPendingEnter(IBinder transition) {
+        return mPendingEnter != null && mPendingEnter.mTransition == transition;
+    }
+
+    boolean isPendingRecent(IBinder transition) {
+        return mPendingRecent != null && mPendingRecent.mTransition == transition;
+    }
+
+    boolean isPendingDismiss(IBinder transition) {
+        return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
+    }
+
     /** Starts a transition to enter split with a remote transition animator. */
-    IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
-            @NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
-            @NonNull Transitions.TransitionHandler handler) {
+    IBinder startEnterTransition(
+            @WindowManager.TransitionType int transitType,
+            WindowContainerTransaction wct,
+            @Nullable RemoteTransition remoteTransition,
+            Transitions.TransitionHandler handler,
+            @Nullable TransitionCallback callback) {
         final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
-        mPendingEnter = transition;
+        setEnterTransition(transition, remoteTransition, callback);
+        return transition;
+    }
+
+    /** Sets a transition to enter split. */
+    void setEnterTransition(@NonNull IBinder transition,
+            @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
+        mPendingEnter = new TransitSession(transition, callback);
 
         if (remoteTransition != null) {
             // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -184,7 +211,9 @@
                     mTransitions.getMainExecutor(), remoteTransition);
             mPendingRemoteHandler.setTransition(transition);
         }
-        return transition;
+
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
+                + " deduced Enter split screen");
     }
 
     /** Starts a transition to dismiss split. */
@@ -209,8 +238,8 @@
     }
 
     void setRecentTransition(@NonNull IBinder transition,
-            @Nullable RemoteTransition remoteTransition) {
-        mPendingRecent = transition;
+            @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
+        mPendingRecent = new TransitSession(transition, callback);
 
         if (remoteTransition != null) {
             // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -226,6 +255,18 @@
     void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
             IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
         if (mergeTarget != mAnimatingTransition) return;
+
+        if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) {
+            mPendingRecent.mCallback = new TransitionCallback() {
+                @Override
+                public void onTransitionFinished(WindowContainerTransaction finishWct,
+                        SurfaceControl.Transaction finishT) {
+                    // Since there's an entering transition merged, recent transition no longer
+                    // need to handle entering split screen after the transition finished.
+                }
+            };
+        }
+
         if (mActiveRemoteHandler != null) {
             mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
         } else {
@@ -247,38 +288,55 @@
     }
 
     void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
-        if (aborted) return;
+        if (isPendingEnter(transition)) {
+            if (!aborted) {
+                // An enter transition got merged, appends the rest operations to finish entering
+                // split screen.
+                // TODO (b/238856352): Passed-in the proper finish transition to merge instead.
+                if (mFinishTransaction == null) {
+                    mFinishTransaction = mTransactionPool.acquire();
+                }
+                mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+            }
 
-        // Once a pending enter transition got merged, make sure to append the reset of finishing
-        // operations to the finish transition.
-        if (transition == mPendingEnter) {
-            mFinishTransaction = mTransactionPool.acquire();
-            mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+            mPendingEnter.mCallback.onTransitionConsumed(aborted);
             mPendingEnter = null;
             mPendingRemoteHandler = null;
-            mEnterTransitionMerged = true;
+        } else if (isPendingDismiss(transition)) {
+            mPendingDismiss.mCallback.onTransitionConsumed(aborted);
+            mPendingDismiss = null;
+        } else if (isPendingRecent(transition)) {
+            mPendingRecent.mCallback.onTransitionConsumed(aborted);
+            mPendingRecent = null;
+            mPendingRemoteHandler = null;
         }
     }
 
     void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
         if (!mAnimations.isEmpty()) return;
-        if (mAnimatingTransition == mPendingEnter) {
+
+        TransitionCallback callback = null;
+        if (isPendingEnter(mAnimatingTransition)) {
+            callback = mPendingEnter.mCallback;
             mPendingEnter = null;
         }
-        if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
+        if (isPendingDismiss(mAnimatingTransition)) {
+            callback = mPendingDismiss.mCallback;
             mPendingDismiss = null;
         }
-        if (mAnimatingTransition == mPendingRecent) {
-            if (!mEnterTransitionMerged) {
-                if (wct == null) wct = new WindowContainerTransaction();
-                mStageCoordinator.onRecentTransitionFinished(wct, mFinishTransaction);
-            }
+        if (isPendingRecent(mAnimatingTransition)) {
+            callback = mPendingRecent.mCallback;
             mPendingRecent = null;
         }
+
+        if (callback != null) {
+            if (wct == null) wct = new WindowContainerTransaction();
+            callback.onTransitionFinished(wct, mFinishTransaction);
+        }
+
         mPendingRemoteHandler = null;
         mActiveRemoteHandler = null;
         mAnimatingTransition = null;
-        mEnterTransitionMerged = false;
 
         mOnFinish.run();
         if (mFinishTransaction != null) {
@@ -382,17 +440,34 @@
                 || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
     }
 
+    /** Clean-up callbacks for transition. */
+    interface TransitionCallback {
+        /** Calls when the transition got consumed. */
+        default void onTransitionConsumed(boolean aborted) {}
+
+        /** Calls when the transition finished. */
+        default void onTransitionFinished(WindowContainerTransaction finishWct,
+                SurfaceControl.Transaction finishT) {}
+    }
+
+    /** Session for a transition and its clean-up callback. */
+    static class TransitSession {
+        final IBinder mTransition;
+        TransitionCallback mCallback;
+
+        TransitSession(IBinder transition, @Nullable TransitionCallback callback) {
+            mTransition = transition;
+            mCallback = callback != null ? callback : new TransitionCallback() {};
+        }
+    }
+
     /** Bundled information of dismiss transition. */
-    static class DismissTransition {
-        IBinder mTransition;
-
-        int mReason;
-
-        @SplitScreen.StageType
-        int mDismissTop;
+    static class DismissTransition extends TransitSession {
+        final int mReason;
+        final @SplitScreen.StageType int mDismissTop;
 
         DismissTransition(IBinder transition, int reason, int dismissTop) {
-            this.mTransition = transition;
+            super(transition, null /* callback */);
             this.mReason = reason;
             this.mDismissTop = dismissTop;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2229e26..3c7db33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -24,6 +24,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -214,6 +215,33 @@
                 }
             };
 
+    private final SplitScreenTransitions.TransitionCallback mRecentTransitionCallback =
+            new SplitScreenTransitions.TransitionCallback() {
+        @Override
+        public void onTransitionFinished(WindowContainerTransaction finishWct,
+                SurfaceControl.Transaction finishT) {
+            // Check if the recent transition is finished by returning to the current split, so we
+            // can restore the divider bar.
+            for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
+                final WindowContainerTransaction.HierarchyOp op =
+                        finishWct.getHierarchyOps().get(i);
+                final IBinder container = op.getContainer();
+                if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
+                        && (mMainStage.containsContainer(container)
+                        || mSideStage.containsContainer(container))) {
+                    setDividerVisibility(true, finishT);
+                    return;
+                }
+            }
+
+            // Dismiss the split screen if it's not returning to split.
+            prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
+            setSplitsVisible(false);
+            setDividerVisibility(false, finishT);
+            logExit(EXIT_REASON_UNKNOWN);
+        }
+    };
+
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             ShellTaskOrganizer taskOrganizer, DisplayController displayController,
             DisplayImeController displayImeController,
@@ -337,15 +365,23 @@
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         targetStage.evictAllChildren(evictWct);
         targetStage.addTask(task, wct);
-        if (!evictWct.isEmpty()) {
-            wct.merge(evictWct, true /* transfer */);
-        }
 
         if (ENABLE_SHELL_TRANSITIONS) {
             prepareEnterSplitScreen(wct);
-            mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE,
-                    wct, null, this);
+            mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
+                    null, this, new SplitScreenTransitions.TransitionCallback() {
+                        @Override
+                        public void onTransitionFinished(WindowContainerTransaction finishWct,
+                                SurfaceControl.Transaction finishT) {
+                            if (!evictWct.isEmpty()) {
+                                finishWct.merge(evictWct, true);
+                            }
+                        }
+                    });
         } else {
+            if (!evictWct.isEmpty()) {
+                wct.merge(evictWct, true /* transfer */);
+            }
             mTaskOrganizer.applyTransaction(wct);
         }
         return true;
@@ -365,6 +401,39 @@
         return result;
     }
 
+    /** Launches an activity into split. */
+    void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
+            @Nullable Bundle options) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+        prepareEvictChildTasks(position, evictWct);
+
+        options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
+        wct.sendPendingIntent(intent, fillInIntent, options);
+        prepareEnterSplitScreen(wct, null /* taskInfo */, position);
+
+        mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this,
+                new SplitScreenTransitions.TransitionCallback() {
+                    @Override
+                    public void onTransitionConsumed(boolean aborted) {
+                        // Switch the split position if launching as MULTIPLE_TASK failed.
+                        if (aborted
+                                && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+                            setSideStagePositionAnimated(
+                                    SplitLayout.reversePosition(mSideStagePosition));
+                        }
+                    }
+
+                    @Override
+                    public void onTransitionFinished(WindowContainerTransaction finishWct,
+                            SurfaceControl.Transaction finishT) {
+                        if (!evictWct.isEmpty()) {
+                            finishWct.merge(evictWct, true);
+                        }
+                    }
+                });
+    }
+
     /** Starts 2 tasks in one transition. */
     void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
             @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@@ -395,7 +464,7 @@
         wct.startTask(sideTaskId, sideOptions);
 
         mSplitTransitions.startEnterTransition(
-                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
+                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null);
     }
 
     /** Starts 2 tasks in one legacy transition. */
@@ -617,11 +686,13 @@
     }
 
     int getTaskId(@SplitPosition int splitPosition) {
-        if (mSideStagePosition == splitPosition) {
-            return mSideStage.getTopVisibleChildTaskId();
-        } else {
-            return mMainStage.getTopVisibleChildTaskId();
+        if (splitPosition == SPLIT_POSITION_UNDEFINED) {
+            return INVALID_TASK_ID;
         }
+
+        return mSideStagePosition == splitPosition
+                ? mSideStage.getTopVisibleChildTaskId()
+                : mMainStage.getTopVisibleChildTaskId();
     }
 
     void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) {
@@ -861,6 +932,7 @@
         mSplitLayout.init();
         setDividerVisibility(true, t);
         updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+        t.show(mRootTaskLeash);
         setSplitsVisible(true);
         mShouldUpdateRecents = true;
         updateRecentTasksSplitPair();
@@ -1211,7 +1283,7 @@
     private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
         final boolean hasChildren = stageListener.mHasChildren;
         final boolean isSideStage = stageListener == mSideStageListener;
-        if (!hasChildren && !mIsExiting) {
+        if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
             if (isSideStage && mMainStageListener.mVisible) {
                 // Exit to main stage if side stage no longer has children.
                 if (ENABLE_SHELL_TRANSITIONS) {
@@ -1231,7 +1303,7 @@
                             EXIT_REASON_APP_FINISHED);
                 }
             }
-        } else if (isSideStage && !mMainStage.isActive()) {
+        } else if (isSideStage && hasChildren && !mMainStage.isActive()) {
             if (mFocusingTaskInfo != null && !isValidToEnterSplitScreen(mFocusingTaskInfo)) {
                 final WindowContainerTransaction wct = new WindowContainerTransaction();
                 mSideStage.removeAllTasks(wct, true);
@@ -1543,14 +1615,14 @@
                 } else if (activityType == ACTIVITY_TYPE_HOME
                         || activityType == ACTIVITY_TYPE_RECENTS) {
                     // Enter overview panel, so start recent transition.
-                    mSplitTransitions.setRecentTransition(transition,
-                            request.getRemoteTransition());
+                    mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
+                            mRecentTransitionCallback);
                 } else if (mSplitTransitions.mPendingRecent == null) {
                     // If split-task is not controlled by recents animation
                     // and occluded by the other fullscreen task, dismiss both.
                     prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
-                    mSplitTransitions.setDismissTransition(transition,
-                            STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
+                    mSplitTransitions.setDismissTransition(
+                            transition, STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
                 }
             }
         } else {
@@ -1558,7 +1630,8 @@
                 // One task is appearing into split, prepare to enter split screen.
                 out = new WindowContainerTransaction();
                 prepareEnterSplitScreen(out);
-                mSplitTransitions.mPendingEnter = transition;
+                mSplitTransitions.setEnterTransition(
+                        transition, request.getRemoteTransition(), null /* callback */);
             }
         }
         return out;
@@ -1614,10 +1687,7 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        if (transition != mSplitTransitions.mPendingEnter
-                && transition != mSplitTransitions.mPendingRecent
-                && (mSplitTransitions.mPendingDismiss == null
-                        || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
+        if (!mSplitTransitions.isPendingTransition(transition)) {
             // Not entering or exiting, so just do some house-keeping and validation.
 
             // If we're not in split-mode, just abort so something else can handle it.
@@ -1664,12 +1734,11 @@
         }
 
         boolean shouldAnimate = true;
-        if (mSplitTransitions.mPendingEnter == transition) {
+        if (mSplitTransitions.isPendingEnter(transition)) {
             shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
-        } else if (mSplitTransitions.mPendingRecent == transition) {
+        } else if (mSplitTransitions.isPendingRecent(transition)) {
             shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
-        } else if (mSplitTransitions.mPendingDismiss != null
-                && mSplitTransitions.mPendingDismiss.mTransition == transition) {
+        } else if (mSplitTransitions.isPendingDismiss(transition)) {
             shouldAnimate = startPendingDismissAnimation(
                     mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
         }
@@ -1837,28 +1906,6 @@
         return true;
     }
 
-    void onRecentTransitionFinished(WindowContainerTransaction wct,
-            SurfaceControl.Transaction finishT) {
-        // Check if the recent transition is finished by returning to the current split so we can
-        // restore the divider bar.
-        for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
-            final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
-            final IBinder container = op.getContainer();
-            if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
-                    && (mMainStage.containsContainer(container)
-                    || mSideStage.containsContainer(container))) {
-                setDividerVisibility(true, finishT);
-                return;
-            }
-        }
-
-        // Dismiss the split screen is it's not returning to split.
-        prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
-        setSplitsVisible(false);
-        setDividerVisibility(false, finishT);
-        logExit(EXIT_REASON_UNKNOWN);
-    }
-
     private void addDividerBarToTransition(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, boolean show) {
         final SurfaceControl leash = mSplitLayout.getDividerLeash();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index dcd6277..0bec543 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -483,11 +483,11 @@
                     postStartTransactionCallbacks.add(t ->
                             startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
                                     mTransactionPool, mMainExecutor, mAnimExecutor,
-                                    null /* position */, cornerRadius, clipRect));
+                                    change.getEndRelOffset(), cornerRadius, clipRect));
                 } else {
                     startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
-                            mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
-                            cornerRadius, clipRect);
+                            mTransactionPool, mMainExecutor, mAnimExecutor,
+                            change.getEndRelOffset(), cornerRadius, clipRect);
                 }
 
                 if (info.getAnimationOptions() != null) {
@@ -934,7 +934,7 @@
         a.restrictDuration(MAX_ANIMATION_DURATION);
         a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
-                mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top),
+                mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
                 cornerRadius, change.getEndAbsBounds());
     }
 
@@ -959,7 +959,7 @@
         a.restrictDuration(MAX_ANIMATION_DURATION);
         a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
-                mMainExecutor, mAnimExecutor, null /* position */,
+                mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
                 cornerRadius, change.getEndAbsBounds());
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index c19a33a..4855fbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -222,10 +222,10 @@
 
         float shadowRadius = outResult.mDensity * shadowRadiusDp;
         int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
-        mTmpColor[0] = Color.red(backgroundColorInt);
-        mTmpColor[1] = Color.green(backgroundColorInt);
-        mTmpColor[2] = Color.blue(backgroundColorInt);
-        t.setCrop(mTaskBackgroundSurface, taskBounds)
+        mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
+        mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
+        mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
+        t.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(), taskBounds.height())
                 .setShadowRadius(mTaskBackgroundSurface, shadowRadius)
                 .setColor(mTaskBackgroundSurface, mTmpColor);
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java
index 4922872..f8b3fb3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java
@@ -34,13 +34,12 @@
      * given {@link SurfaceControl} when calling {@link SurfaceControl.Builder#build()}.
      *
      * @param mockSurfaceControl the first {@link SurfaceControl} to return
-     * @param mockSurfaceControls following {@link SurfaceControl} to return
      * @return the mock of {@link SurfaceControl.Builder}
      */
     public static SurfaceControl.Builder createMockSurfaceControlBuilder(
-            SurfaceControl mockSurfaceControl, SurfaceControl... mockSurfaceControls) {
+            SurfaceControl mockSurfaceControl) {
         final SurfaceControl.Builder mockBuilder = mock(SurfaceControl.Builder.class, RETURNS_SELF);
-        doReturn(mockSurfaceControl, (Object[]) mockSurfaceControls)
+        doReturn(mockSurfaceControl)
                 .when(mockBuilder)
                 .build();
         return mockBuilder;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 52d78ca..5880ffb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -37,6 +37,7 @@
 import android.view.SurfaceControl;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.wm.shell.MockSurfaceControlHelper;
 import com.android.wm.shell.ShellTestCase;
@@ -62,19 +63,18 @@
 
     @Mock
     private TaskInfo mTaskInfo;
-
     @Mock
     private PipAnimationController.PipAnimationCallback mPipAnimationCallback;
 
     @Before
     public void setUp() throws Exception {
-        mPipAnimationController = new PipAnimationController(
-                new PipSurfaceTransactionHelper());
+        MockitoAnnotations.initMocks(this);
+        mPipAnimationController = new PipAnimationController(new PipSurfaceTransactionHelper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext()));
         mLeash = new SurfaceControl.Builder()
                 .setContainerLayer()
                 .setName("FakeLeash")
                 .build();
-        MockitoAnnotations.initMocks(this);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 304ca66..1d038f4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -182,7 +182,7 @@
 
         IBinder transition = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(testRemote), mStageCoordinator);
+                new RemoteTransition(testRemote), mStageCoordinator, null);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -422,7 +422,7 @@
         TransitionInfo enterInfo = createEnterPairInfo();
         IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(new TestRemoteTransition()), mStageCoordinator);
+                new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 680034bd..d1b837e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -21,23 +21,32 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
+import android.util.DisplayMetrics;
 import android.view.Display;
+import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowManager.LayoutParams;
 import android.window.WindowContainerTransaction;
 
 import androidx.test.filters.SmallTest;
@@ -53,6 +62,8 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Supplier;
 
 /**
@@ -66,6 +77,8 @@
 public class WindowDecorationTests extends ShellTestCase {
     private static final int CAPTION_HEIGHT_DP = 32;
     private static final int SHADOW_RADIUS_DP = 5;
+    private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
+    private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
 
     private final Rect mOutsetsDp = new Rect();
     private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
@@ -84,12 +97,11 @@
     @Mock
     private WindowContainerTransaction mMockWindowContainerTransaction;
 
-    private SurfaceControl.Builder mMockSurfaceControlBuilder;
+    private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>();
     private SurfaceControl.Transaction mMockSurfaceControlTransaction;
 
     @Before
     public void setUp() {
-        mMockSurfaceControlBuilder = createMockSurfaceControlBuilder(mock(SurfaceControl.class));
         mMockSurfaceControlTransaction = createMockSurfaceControlTransaction();
 
         doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
@@ -97,6 +109,128 @@
     }
 
     @Test
+    public void testLayoutResultCalculation_invisibleTask() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder decorContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(decorContainerSurface);
+        mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+                createMockSurfaceControlBuilder(taskBackgroundSurface);
+        mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+
+        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+                new ActivityManager.TaskDescription.Builder()
+                        .setBackgroundColor(Color.YELLOW);
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setTaskDescriptionBuilder(taskDescriptionBuilder)
+                .setBounds(TASK_BOUNDS)
+                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+                .setVisible(false)
+                .build();
+        taskInfo.isFocused = false;
+        // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
+        // 64px.
+        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+        mOutsetsDp.set(10, 20, 30, 40);
+
+        final SurfaceControl taskSurface = mock(SurfaceControl.class);
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+        windowDecor.relayout(taskInfo);
+
+        verify(decorContainerSurfaceBuilder, never()).build();
+        verify(taskBackgroundSurfaceBuilder, never()).build();
+        verify(mMockSurfaceControlViewHostFactory, never())
+                .create(any(), any(), any(), anyBoolean());
+
+        verify(mMockSurfaceControlTransaction).hide(taskSurface);
+
+        assertNull(mRelayoutResult.mRootView);
+    }
+
+    @Test
+    public void testLayoutResultCalculation_visibleFocusedTask() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder decorContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(decorContainerSurface);
+        mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+                createMockSurfaceControlBuilder(taskBackgroundSurface);
+        mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+
+        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+                new ActivityManager.TaskDescription.Builder()
+                        .setBackgroundColor(Color.YELLOW);
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setTaskDescriptionBuilder(taskDescriptionBuilder)
+                .setBounds(TASK_BOUNDS)
+                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+                .setVisible(true)
+                .build();
+        taskInfo.isFocused = true;
+        // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
+        // 64px.
+        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+        mOutsetsDp.set(10, 20, 30, 40);
+
+        final SurfaceControl taskSurface = mock(SurfaceControl.class);
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+        windowDecor.relayout(taskInfo);
+
+        verify(decorContainerSurfaceBuilder).setParent(taskSurface);
+        verify(decorContainerSurfaceBuilder).setContainerLayer();
+        verify(mMockSurfaceControlTransaction).setTrustedOverlay(decorContainerSurface, true);
+        verify(mMockSurfaceControlTransaction).setPosition(decorContainerSurface, -20, -40);
+        verify(mMockSurfaceControlTransaction).setWindowCrop(decorContainerSurface, 380, 220);
+
+        verify(taskBackgroundSurfaceBuilder).setParent(taskSurface);
+        verify(taskBackgroundSurfaceBuilder).setEffectLayer();
+        verify(mMockSurfaceControlTransaction).setWindowCrop(taskBackgroundSurface, 300, 100);
+        verify(mMockSurfaceControlTransaction)
+                .setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
+        verify(mMockSurfaceControlTransaction).setShadowRadius(taskBackgroundSurface, 10);
+
+        verify(mMockSurfaceControlViewHostFactory)
+                .create(any(), eq(defaultDisplay), any(), anyBoolean());
+        verify(mMockSurfaceControlViewHost)
+                .setView(same(mMockView),
+                        argThat(lp -> lp.height == 64
+                                && lp.width == 300
+                                && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
+        if (ViewRootImpl.CAPTION_ON_SHELL) {
+            verify(mMockView).setTaskFocusState(true);
+            verify(mMockWindowContainerTransaction)
+                    .addRectInsetsProvider(taskInfo.token,
+                            new Rect(100, 300, 400, 364),
+                            new int[] { InsetsState.ITYPE_CAPTION_BAR });
+        }
+
+        verify(mMockSurfaceControlTransaction)
+                .setPosition(taskSurface, TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y);
+        verify(mMockSurfaceControlTransaction)
+                .setCrop(taskSurface, new Rect(-20, -40, 360, 180));
+        verify(mMockSurfaceControlTransaction)
+                .show(taskSurface);
+
+        assertEquals(380, mRelayoutResult.mWidth);
+        assertEquals(220, mRelayoutResult.mHeight);
+        assertEquals(2, mRelayoutResult.mDensity, 0.f);
+    }
+
+    @Test
     public void testNotCrashWhenDisplayAppearsAfterTask() {
         doReturn(mock(Display.class)).when(mMockDisplayController)
                 .getDisplay(Display.DEFAULT_DISPLAY);
@@ -145,10 +279,24 @@
     private TestWindowDecoration createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
         return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
-                taskInfo, testSurface, () -> mMockSurfaceControlBuilder,
+                taskInfo, testSurface, new MockSurfaceControlBuilderSupplier(),
                 mMockSurfaceControlViewHostFactory);
     }
 
+    private class MockSurfaceControlBuilderSupplier implements Supplier<SurfaceControl.Builder> {
+        private int mNumOfCalls = 0;
+
+        @Override
+        public SurfaceControl.Builder get() {
+            final SurfaceControl.Builder builder =
+                    mNumOfCalls < mMockSurfaceControlBuilders.size()
+                            ? mMockSurfaceControlBuilders.get(mNumOfCalls)
+                            : createMockSurfaceControlBuilder(mock(SurfaceControl.class));
+            ++mNumOfCalls;
+            return builder;
+        }
+    }
+
     private static class TestView extends View implements TaskFocusStateConsumer {
         private TestView(Context context) {
             super(context);
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 30d6d00..19efc5f 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -21,6 +21,7 @@
 #include <minikin/FontFamily.h>
 #include <minikin/FontFileParser.h>
 #include <minikin/LocaleList.h>
+#include <minikin/MinikinFontFactory.h>
 #include <minikin/SystemFonts.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
@@ -206,9 +207,18 @@
     return entry;
 }
 
-static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::BufferReader);
+class MinikinFontSkiaFactory : minikin::MinikinFontFactory {
+private:
+    MinikinFontSkiaFactory() : MinikinFontFactory() { MinikinFontFactory::setInstance(this); }
 
-static minikin::Font::TypefaceLoader* readMinikinFontSkia(minikin::BufferReader* reader) {
+public:
+    static void init() { static MinikinFontSkiaFactory factory; }
+    void skip(minikin::BufferReader* reader) const override;
+    std::shared_ptr<minikin::MinikinFont> create(minikin::BufferReader reader) const override;
+    void write(minikin::BufferWriter* writer, const minikin::MinikinFont* typeface) const override;
+};
+
+void MinikinFontSkiaFactory::skip(minikin::BufferReader* reader) const {
     // Advance reader's position.
     reader->skipString(); // fontPath
     reader->skip<int>(); // fontIndex
@@ -218,10 +228,10 @@
         reader->skip<uint32_t>(); // expectedFontRevision
         reader->skipString(); // expectedPostScriptName
     }
-    return &loadMinikinFontSkia;
 }
 
-static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::BufferReader reader) {
+std::shared_ptr<minikin::MinikinFont> MinikinFontSkiaFactory::create(
+        minikin::BufferReader reader) const {
     std::string_view fontPath = reader.readString();
     std::string path(fontPath.data(), fontPath.size());
     ATRACE_FORMAT("Loading font %s", path.c_str());
@@ -270,8 +280,8 @@
     return minikinFont;
 }
 
-static void writeMinikinFontSkia(minikin::BufferWriter* writer,
-        const minikin::MinikinFont* typeface) {
+void MinikinFontSkiaFactory::write(minikin::BufferWriter* writer,
+                                   const minikin::MinikinFont* typeface) const {
     // When you change the format of font metadata, please update code to parse
     // typefaceMetadataReader() in
     // frameworks/base/libs/hwui/jni/fonts/Font.cpp too.
@@ -296,6 +306,7 @@
 }
 
 static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
+    MinikinFontSkiaFactory::init();
     ScopedLongArrayRO faces(env, faceHandles);
     std::vector<Typeface*> typefaces;
     typefaces.reserve(faces.size());
@@ -312,7 +323,7 @@
             fontCollections.push_back(typeface->fFontCollection);
         }
     }
-    minikin::FontCollection::writeVector<writeMinikinFontSkia>(&writer, fontCollections);
+    minikin::FontCollection::writeVector(&writer, fontCollections);
     writer.write<uint32_t>(typefaces.size());
     for (Typeface* typeface : typefaces) {
       writer.write<uint32_t>(fcToIndex.find(typeface->fFontCollection)->second);
@@ -324,11 +335,12 @@
 }
 
 static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
+    MinikinFontSkiaFactory::init();
     void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
     if (addr == nullptr) return nullptr;
     minikin::BufferReader reader(addr);
     std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections =
-            minikin::FontCollection::readVector<readMinikinFontSkia>(&reader);
+            minikin::FontCollection::readVector(&reader);
     uint32_t typefaceCount = reader.read<uint32_t>();
     std::vector<jlong> faceHandles;
     faceHandles.reserve(typefaceCount);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 57f1056..a7a21e7 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -48,6 +48,7 @@
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
@@ -124,6 +125,7 @@
     private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>();
 
     private final AtomicInteger mNextRequestId = new AtomicInteger(1);
+    private final AtomicBoolean mIsScanning = new AtomicBoolean(/* initialValue= */ false);
 
     final Handler mHandler;
 
@@ -255,7 +257,9 @@
     @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
     public void startScan() {
         if (isSystemRouter()) {
-            sManager.startScan();
+            if (!mIsScanning.getAndSet(true)) {
+                sManager.registerScanRequest();
+            }
         }
     }
 
@@ -281,7 +285,9 @@
     @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
     public void stopScan() {
         if (isSystemRouter()) {
-            sManager.stopScan();
+            if (mIsScanning.getAndSet(false)) {
+                sManager.unregisterScanRequest();
+            }
         }
     }
 
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index c84f5b0..44c0b54 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -82,6 +82,7 @@
     @GuardedBy("sLock")
     private Client mClient;
     private final IMediaRouterService mMediaRouterService;
+    private final AtomicInteger mScanRequestCount = new AtomicInteger(/* initialValue= */ 0);
     final Handler mHandler;
     final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords = new CopyOnWriteArrayList<>();
 
@@ -155,22 +156,18 @@
     }
 
     /**
-     * Starts scanning remote routes.
-     * <p>
-     * Route discovery can happen even when the {@link #startScan()} is not called.
-     * This is because the scanning could be started before by other apps.
-     * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean
-     * that the routes found before are removed and added again.
-     * <p>
-     * Use {@link Callback} to get the route related events.
-     * <p>
-     * @see #stopScan()
+     * Registers a request to scan for remote routes.
+     *
+     * <p>Increases the count of active scanning requests. When the count transitions from zero to
+     * one, sends a request to the system server to start scanning.
+     *
+     * <p>Clients must {@link #unregisterScanRequest() unregister their scan requests} when scanning
+     * is no longer needed, to avoid unnecessary resource usage.
      */
-    public void startScan() {
-        Client client = getOrCreateClient();
-        if (client != null) {
+    public void registerScanRequest() {
+        if (mScanRequestCount.getAndIncrement() == 0) {
             try {
-                mMediaRouterService.startScan(client);
+                mMediaRouterService.startScan(getOrCreateClient());
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -178,23 +175,26 @@
     }
 
     /**
-     * Stops scanning remote routes to reduce resource consumption.
-     * <p>
-     * Route discovery can be continued even after this method is called.
-     * This is because the scanning is only turned off when all the apps stop scanning.
-     * Therefore, calling this method does not necessarily mean the routes are removed.
-     * Also, for the same reason it does not mean that {@link Callback#onRoutesAdded(List)}
-     * is not called afterwards.
-     * <p>
-     * Use {@link Callback} to get the route related events.
+     * Unregisters a scan request made by {@link #registerScanRequest()}.
      *
-     * @see #startScan()
+     * <p>Decreases the count of active scanning requests. When the count transitions from one to
+     * zero, sends a request to the system server to stop scanning.
+     *
+     * @throws IllegalStateException If called while there are no active scan requests.
      */
-    public void stopScan() {
-        Client client = getOrCreateClient();
-        if (client != null) {
+    public void unregisterScanRequest() {
+        if (mScanRequestCount.updateAndGet(
+                count -> {
+                    if (count == 0) {
+                        throw new IllegalStateException(
+                                "No active scan requests to unregister.");
+                    } else {
+                        return --count;
+                    }
+                })
+                == 0) {
             try {
-                mMediaRouterService.stopScan(client);
+                mMediaRouterService.stopScan(getOrCreateClient());
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -423,15 +423,11 @@
      */
     @NonNull
     public List<RoutingSessionInfo> getRemoteSessions() {
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                return mMediaRouterService.getRemoteSessions(client);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            return mMediaRouterService.getRemoteSessions(getOrCreateClient());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return Collections.emptyList();
     }
 
     /**
@@ -514,14 +510,12 @@
             return;
         }
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.setRouteVolumeWithManager(client, requestId, route, volume);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.setRouteVolumeWithManager(
+                    getOrCreateClient(), requestId, route, volume);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -543,15 +537,12 @@
             return;
         }
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.setSessionVolumeWithManager(
-                        client, requestId, sessionInfo.getId(), volume);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.setSessionVolumeWithManager(
+                    getOrCreateClient(), requestId, sessionInfo.getId(), volume);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -808,15 +799,12 @@
             return;
         }
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.selectRouteWithManager(
-                        client, requestId, sessionInfo.getId(), route);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.selectRouteWithManager(
+                    getOrCreateClient(), requestId, sessionInfo.getId(), route);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -850,15 +838,12 @@
             return;
         }
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.deselectRouteWithManager(
-                        client, requestId, sessionInfo.getId(), route);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.deselectRouteWithManager(
+                    getOrCreateClient(), requestId, sessionInfo.getId(), route);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -875,15 +860,12 @@
     public void releaseSession(@NonNull RoutingSessionInfo sessionInfo) {
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.releaseSessionWithManager(
-                        client, requestId, sessionInfo.getId());
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.releaseSessionWithManager(
+                    getOrCreateClient(), requestId, sessionInfo.getId());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -896,14 +878,11 @@
             @NonNull MediaRoute2Info route) {
         int requestId = createTransferRequest(session, route);
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                mMediaRouterService.transferToRouteWithManager(
-                        client, requestId, session.getId(), route);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            mMediaRouterService.transferToRouteWithManager(
+                    getOrCreateClient(), requestId, session.getId(), route);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -916,14 +895,11 @@
 
         int requestId = createTransferRequest(oldSession, route);
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                mMediaRouterService.requestCreateSessionWithManager(
-                        client, requestId, oldSession, route);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            mMediaRouterService.requestCreateSessionWithManager(
+                    getOrCreateClient(), requestId, oldSession, route);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index b4aad9d..4086dec 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -39,6 +39,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 import android.Manifest;
@@ -121,7 +122,7 @@
         MediaRouter2ManagerTestActivity.startActivity(mContext);
 
         mManager = MediaRouter2Manager.getInstance(mContext);
-        mManager.startScan();
+        mManager.registerScanRequest();
         mRouter2 = MediaRouter2.getInstance(mContext);
 
         // If we need to support thread pool executors, change this to thread pool executor.
@@ -152,7 +153,7 @@
 
     @After
     public void tearDown() {
-        mManager.stopScan();
+        mManager.unregisterScanRequest();
 
         // order matters (callbacks should be cleared at the last)
         releaseAllSessions();
@@ -818,6 +819,13 @@
         assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Test
+    public void unregisterScanRequest_enforcesANonNegativeCount() {
+        mManager.unregisterScanRequest(); // One request was made in the test setup.
+        assertThrows(IllegalStateException.class, () -> mManager.unregisterScanRequest());
+        mManager.registerScanRequest(); // So that the cleanup doesn't fail.
+    }
+
     /**
      * Tests if getSelectableRoutes and getDeselectableRoutes filter routes based on
      * selected routes
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 58944f6..4714ff9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -576,6 +576,15 @@
             return;
         }
 
+        // The profiles list's sequence will affect the bluetooth icon at
+        // BluetoothUtils.getBtClassDrawableWithDescription(Context,CachedBluetoothDevice).
+
+        // Moving the LE audio profile to be the first priority if the device supports LE audio.
+        if (ArrayUtils.contains(uuids, BluetoothUuid.LE_AUDIO) && mLeAudioProfile != null) {
+            profiles.add(mLeAudioProfile);
+            removedProfiles.remove(mLeAudioProfile);
+        }
+
         if (mHeadsetProfile != null) {
             if ((ArrayUtils.contains(localUuids, BluetoothUuid.HSP_AG)
                     && ArrayUtils.contains(uuids, BluetoothUuid.HSP))
@@ -660,11 +669,6 @@
             removedProfiles.remove(mHearingAidProfile);
         }
 
-        if (ArrayUtils.contains(uuids, BluetoothUuid.LE_AUDIO) && mLeAudioProfile != null) {
-            profiles.add(mLeAudioProfile);
-            removedProfiles.remove(mLeAudioProfile);
-        }
-
         if (mSapProfile != null && ArrayUtils.contains(uuids, BluetoothUuid.SAP)) {
             profiles.add(mSapProfile);
             removedProfiles.remove(mSapProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 192872f..d9262cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -92,14 +92,14 @@
     public void startScan() {
         mMediaDevices.clear();
         mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
-        mRouterManager.startScan();
+        mRouterManager.registerScanRequest();
         refreshDevices();
     }
 
     @Override
     public void stopScan() {
         mRouterManager.unregisterCallback(mMediaRouterCallback);
-        mRouterManager.stopScan();
+        mRouterManager.unregisterScanRequest();
     }
 
     /**
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
index 4d5014c..b8639e6 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
@@ -37,7 +37,7 @@
  * Important: Use M3 colors from MaterialTheme.colorScheme whenever possible instead. In the future,
  * most of the colors in this class will be removed in favor of their M3 counterpart.
  */
-class AndroidColorScheme internal constructor(private val context: Context) {
+class AndroidColorScheme internal constructor(context: Context) {
     val colorPrimary = getColor(context, R.attr.colorPrimary)
     val colorPrimaryDark = getColor(context, R.attr.colorPrimaryDark)
     val colorAccent = getColor(context, R.attr.colorAccent)
diff --git a/packages/SystemUI/compose/gallery/Android.bp b/packages/SystemUI/compose/gallery/Android.bp
index bddc1e6..40504dc 100644
--- a/packages/SystemUI/compose/gallery/Android.bp
+++ b/packages/SystemUI/compose/gallery/Android.bp
@@ -34,6 +34,7 @@
     ],
 
     static_libs: [
+        "SystemUI-core",
         "SystemUIComposeCore",
         "SystemUIComposeFeatures",
 
diff --git a/packages/SystemUI/compose/gallery/AndroidManifest.xml b/packages/SystemUI/compose/gallery/AndroidManifest.xml
index 4fcce0b..2f30651 100644
--- a/packages/SystemUI/compose/gallery/AndroidManifest.xml
+++ b/packages/SystemUI/compose/gallery/AndroidManifest.xml
@@ -16,7 +16,40 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.android.systemui.compose.gallery">
     <!-- To emulate a display size and density. -->
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+    <application
+        android:name="android.app.Application"
+        android:appComponentFactory="androidx.core.app.AppComponentFactory"
+        tools:replace="android:name,android:appComponentFactory">
+        <!-- Disable providers from SystemUI -->
+        <provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider"
+            android:authorities="com.android.systemui.test.keyguard.disabled"
+            android:enabled="false"
+            tools:replace="android:authorities"
+            tools:node="remove" />
+        <provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle"
+            android:authorities="com.android.systemui.test.keyguard.disabled"
+            android:enabled="false"
+            tools:replace="android:authorities"
+            tools:node="remove" />
+        <provider android:name="com.android.keyguard.clock.ClockOptionsProvider"
+            android:authorities="com.android.systemui.test.keyguard.clock.disabled"
+            android:enabled="false"
+            tools:replace="android:authorities"
+            tools:node="remove" />
+        <provider android:name="com.android.systemui.people.PeopleProvider"
+            android:authorities="com.android.systemui.test.people.disabled"
+            android:enabled="false"
+            tools:replace="android:authorities"
+            tools:node="remove" />
+        <provider android:name="androidx.core.content.FileProvider"
+            android:authorities="com.android.systemui.test.fileprovider.disabled"
+            android:enabled="false"
+            tools:replace="android:authorities"
+            tools:node="remove"/>
+    </application>
 </manifest>
diff --git a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml
index e7d496c..1f3fd8c 100644
--- a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml
+++ b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml
@@ -16,6 +16,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.android.systemui.compose.gallery.app">
     <application
         android:allowBackup="true"
@@ -23,7 +24,8 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:theme="@style/Theme.SystemUIGallery">
+        android:theme="@style/Theme.SystemUI.Gallery"
+        tools:replace="android:icon,android:theme,android:label">
         <activity
             android:name="com.android.systemui.compose.gallery.GalleryActivity"
             android:exported="true"
diff --git a/packages/SystemUI/compose/gallery/res/values-night/themes.xml b/packages/SystemUI/compose/gallery/res/values-night/themes.xml
deleted file mode 100644
index 58d20b8..0000000
--- a/packages/SystemUI/compose/gallery/res/values-night/themes.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<resources xmlns:tools="http://schemas.android.com/tools">
-    <style name="Theme.SystemUIGallery" parent="Theme.AppCompat.DayNight.NoActionBar">
-        <item name="android:statusBarColor" tools:targetApi="l">
-            @android:color/transparent
-        </item>
-        <item name="android:navigationBarColor" tools:targetApi="l">
-            @android:color/transparent
-        </item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/compose/gallery/res/values/themes.xml b/packages/SystemUI/compose/gallery/res/values/themes.xml
index 6e5e9983..45fa1f5d 100644
--- a/packages/SystemUI/compose/gallery/res/values/themes.xml
+++ b/packages/SystemUI/compose/gallery/res/values/themes.xml
@@ -15,7 +15,10 @@
      limitations under the License.
 -->
 <resources xmlns:tools="http://schemas.android.com/tools">
-    <style name="Theme.SystemUIGallery" parent="Theme.AppCompat.DayNight.NoActionBar">
+    <style name="Theme.SystemUI.Gallery">
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+
         <item name="android:statusBarColor" tools:targetApi="l">
             @android:color/transparent
         </item>
@@ -24,4 +27,4 @@
         </item>
         <item name="android:windowLightStatusBar">true</item>
     </style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt
index 06bdad8..990d060 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt
@@ -9,11 +9,12 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyRow
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.BrightnessHigh
-import androidx.compose.material.icons.filled.BrightnessLow
+import androidx.compose.material.icons.filled.DarkMode
 import androidx.compose.material.icons.filled.FormatSize
 import androidx.compose.material.icons.filled.FormatTextdirectionLToR
 import androidx.compose.material.icons.filled.FormatTextdirectionRToL
+import androidx.compose.material.icons.filled.InvertColors
+import androidx.compose.material.icons.filled.LightMode
 import androidx.compose.material.icons.filled.Smartphone
 import androidx.compose.material.icons.filled.Tablet
 import androidx.compose.material3.Button
@@ -44,12 +45,13 @@
 /** A configuration panel that allows to toggle the theme, font scale and layout direction. */
 @Composable
 fun ConfigurationControls(
-    isDarkTheme: Boolean,
+    theme: Theme,
     fontScale: FontScale,
     layoutDirection: LayoutDirection,
     onChangeTheme: () -> Unit,
     onChangeLayoutDirection: () -> Unit,
     onChangeFontScale: () -> Unit,
+    modifier: Modifier = Modifier,
 ) {
     // The display we are emulating, if any.
     var emulatedDisplayName by rememberSaveable { mutableStateOf<String?>(null) }
@@ -84,18 +86,26 @@
 
     // TODO(b/231131244): Fork FlowRow from Accompanist and use that instead to make sure that users
     // don't miss any available configuration.
-    LazyRow {
+    LazyRow(modifier) {
         // Dark/light theme.
         item {
             TextButton(onChangeTheme) {
                 val text: String
                 val icon: ImageVector
-                if (isDarkTheme) {
-                    icon = Icons.Default.BrightnessHigh
-                    text = "Dark"
-                } else {
-                    icon = Icons.Default.BrightnessLow
-                    text = "Light"
+
+                when (theme) {
+                    Theme.System -> {
+                        icon = Icons.Default.InvertColors
+                        text = "System"
+                    }
+                    Theme.Dark -> {
+                        icon = Icons.Default.DarkMode
+                        text = "Dark"
+                    }
+                    Theme.Light -> {
+                        icon = Icons.Default.LightMode
+                        text = "Light"
+                    }
                 }
 
                 Icon(icon, null)
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt
index d18b454..bb2d2fe 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.compose.gallery
 
+import android.app.UiModeManager
+import android.content.Context
 import android.os.Bundle
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
@@ -23,7 +25,7 @@
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.graphics.Color
 import androidx.core.view.WindowCompat
@@ -33,22 +35,46 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         WindowCompat.setDecorFitsSystemWindows(window, false)
+        val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
 
         setContent {
-            val isSystemInDarkTheme = isSystemInDarkTheme()
-            var isDarkTheme by remember { mutableStateOf(isSystemInDarkTheme) }
-            val onChangeTheme = { isDarkTheme = !isDarkTheme }
+            var theme by rememberSaveable { mutableStateOf(Theme.System) }
+            val onChangeTheme = {
+                // Change to the next theme for a toggle behavior.
+                theme =
+                    when (theme) {
+                        Theme.System -> Theme.Dark
+                        Theme.Dark -> Theme.Light
+                        Theme.Light -> Theme.System
+                    }
+            }
 
+            val isSystemInDarkTheme = isSystemInDarkTheme()
+            val isDark = theme == Theme.Dark || (theme == Theme.System && isSystemInDarkTheme)
+            val useDarkIcons = !isDark
             val systemUiController = rememberSystemUiController()
-            val useDarkIcons = !isDarkTheme
             SideEffect {
                 systemUiController.setSystemBarsColor(
                     color = Color.Transparent,
                     darkIcons = useDarkIcons,
                 )
+
+                uiModeManager.setApplicationNightMode(
+                    when (theme) {
+                        Theme.System -> UiModeManager.MODE_NIGHT_AUTO
+                        Theme.Dark -> UiModeManager.MODE_NIGHT_YES
+                        Theme.Light -> UiModeManager.MODE_NIGHT_NO
+                    }
+                )
             }
 
-            GalleryApp(isDarkTheme, onChangeTheme)
+            GalleryApp(theme, onChangeTheme)
         }
     }
 }
+
+enum class Theme {
+    System,
+    Dark,
+    Light,
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
index 0ac3a63..c341867 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
@@ -23,36 +23,38 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
 import androidx.navigation.compose.rememberNavController
 import com.android.systemui.compose.theme.SystemUITheme
 
-enum class Screen {
-    Home,
-    Typography,
-    MaterialColors,
-    AndroidColors,
-    ExampleFeature,
+/** The gallery app screens. */
+object GalleryAppScreens {
+    val Typography = ChildScreen("typography") { TypographyScreen() }
+    val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
+    val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
+    val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
+
+    val Home =
+        ParentScreen(
+            "home",
+            mapOf(
+                "Typography" to Typography,
+                "Material colors" to MaterialColors,
+                "Android colors" to AndroidColors,
+                "Example feature" to ExampleFeature,
+            )
+        )
 }
 
-/** The main content of the app, that shows the [HomeScreen] by default. */
+/** The main content of the app, that shows [GalleryAppScreens.Home] by default. */
 @Composable
 private fun MainContent() {
     Box(Modifier.fillMaxSize()) {
         val navController = rememberNavController()
         NavHost(
             navController = navController,
-            startDestination = Screen.Home.name,
+            startDestination = GalleryAppScreens.Home.identifier,
         ) {
-            composable(Screen.Home.name) {
-                HomeScreen(
-                    onScreenSelected = { navController.navigate(it.name) },
-                )
-            }
-            composable(Screen.Typography.name) { TypographyScreen() }
-            composable(Screen.MaterialColors.name) { MaterialColorsScreen() }
-            composable(Screen.AndroidColors.name) { AndroidColorsScreen() }
-            composable(Screen.ExampleFeature.name) { ExampleFeatureScreen() }
+            screen(GalleryAppScreens.Home, navController)
         }
     }
 }
@@ -63,7 +65,7 @@
  */
 @Composable
 fun GalleryApp(
-    isDarkTheme: Boolean,
+    theme: Theme,
     onChangeTheme: () -> Unit,
 ) {
     val systemFontScale = LocalDensity.current.fontScale
@@ -98,14 +100,14 @@
         LocalDensity provides density,
         LocalLayoutDirection provides layoutDirection,
     ) {
-        SystemUITheme(isDarkTheme) {
+        SystemUITheme {
             Surface(
                 Modifier.fillMaxSize(),
                 color = MaterialTheme.colorScheme.background,
             ) {
                 Column(Modifier.fillMaxSize().systemBarsPadding().padding(16.dp)) {
                     ConfigurationControls(
-                        isDarkTheme,
+                        theme,
                         fontScale,
                         layoutDirection,
                         onChangeTheme,
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt
deleted file mode 100644
index b1869da..0000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/HomeScreen.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.systemui.compose.gallery
-
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-
-/** The home screen shown when starting the app. */
-@Composable
-fun HomeScreen(onScreenSelected: (Screen) -> Unit) {
-    LazyColumn(
-        verticalArrangement = Arrangement.spacedBy(8.dp),
-    ) {
-        Screen.values()
-            .filter { it != Screen.Home }
-            .forEach { screen ->
-                item {
-                    Surface(
-                        Modifier.fillMaxWidth(),
-                        color = MaterialTheme.colorScheme.secondaryContainer,
-                        shape = CircleShape,
-                    ) {
-                        Column(
-                            Modifier.clickable { onScreenSelected(screen) }.padding(16.dp),
-                            horizontalAlignment = Alignment.CenterHorizontally,
-                        ) {
-                            Text(screen.name)
-                        }
-                    }
-                }
-            }
-    }
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt
new file mode 100644
index 0000000..467dac04
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.systemui.compose.gallery
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.navigation
+
+/**
+ * A screen in an app. It is either an [ParentScreen] which lists its child screens to navigate to
+ * them or a [ChildScreen] which shows some content.
+ */
+sealed class Screen(val identifier: String)
+
+class ParentScreen(
+    identifier: String,
+    val children: Map<String, Screen>,
+) : Screen(identifier)
+
+class ChildScreen(
+    identifier: String,
+    val content: @Composable (NavController) -> Unit,
+) : Screen(identifier)
+
+/** Create the navigation graph for [screen]. */
+fun NavGraphBuilder.screen(screen: Screen, navController: NavController) {
+    when (screen) {
+        is ChildScreen -> composable(screen.identifier) { screen.content(navController) }
+        is ParentScreen -> {
+            val menuRoute = "${screen.identifier}_menu"
+            navigation(startDestination = menuRoute, route = screen.identifier) {
+                // The menu to navigate to one of the children screens.
+                composable(menuRoute) { ScreenMenu(screen, navController) }
+
+                // The content of the child screens.
+                screen.children.forEach { (_, child) -> screen(child, navController) }
+            }
+        }
+    }
+}
+
+@Composable
+private fun ScreenMenu(
+    screen: ParentScreen,
+    navController: NavController,
+) {
+    LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+        screen.children.forEach { (name, child) ->
+            item {
+                Surface(
+                    Modifier.fillMaxWidth(),
+                    color = MaterialTheme.colorScheme.secondaryContainer,
+                    shape = CircleShape,
+                ) {
+                    Column(
+                        Modifier.clickable { navController.navigate(child.identifier) }
+                            .padding(16.dp),
+                        horizontalAlignment = Alignment.CenterHorizontally,
+                    ) {
+                        Text(name)
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2624412..ddbe6d6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -890,7 +890,7 @@
     <!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either
          direction that elements are moved to prevent burn-in on AOD-->
     <dimen name="udfps_burn_in_offset_x">7px</dimen>
-    <dimen name="udfps_burn_in_offset_y">28px</dimen>
+    <dimen name="udfps_burn_in_offset_y">20px</dimen>
 
     <!-- The absolute side margins of quick settings -->
     <dimen name="quick_settings_bottom_margin_media">8dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index e3f5687..4222744 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.recents.model;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
@@ -245,12 +246,16 @@
      */
     public static Task from(TaskKey taskKey, TaskInfo taskInfo, boolean isLocked) {
         ActivityManager.TaskDescription td = taskInfo.taskDescription;
+        // Also consider undefined activity type to include tasks in overview right after rebooting
+        // the device.
+        final boolean isDockable = taskInfo.supportsMultiWindow
+                && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode())
+                && (taskInfo.getActivityType() == ACTIVITY_TYPE_UNDEFINED
+                || ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()));
         return new Task(taskKey,
                 td != null ? td.getPrimaryColor() : 0,
-                td != null ? td.getBackgroundColor() : 0, taskInfo.supportsMultiWindow
-                && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
-                && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode()),
-                isLocked, td, taskInfo.topActivity);
+                td != null ? td.getBackgroundColor() : 0, isDockable , isLocked, td,
+                taskInfo.topActivity);
     }
 
     public Task(TaskKey key) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
index 8c8f54f..ad073c0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -49,8 +49,8 @@
 ) : UserFileManager, CoreStartable(context) {
     companion object {
         private const val FILES = "files"
-        private const val SHARED_PREFS = "shared_prefs"
-        internal const val ID = "UserFileManager"
+        @VisibleForTesting internal const val SHARED_PREFS = "shared_prefs"
+        @VisibleForTesting internal const val ID = "UserFileManager"
     }
 
    private val broadcastReceiver = object : BroadcastReceiver() {
@@ -85,13 +85,15 @@
                 fileName
             )
         } else {
-            Environment.buildPath(
+            val secondaryFile = Environment.buildPath(
                 context.filesDir,
                 ID,
                 userId.toString(),
                 FILES,
                 fileName
             )
+            ensureParentDirExists(secondaryFile)
+            secondaryFile
         }
     }
 
@@ -114,6 +116,7 @@
             fileName
         )
 
+        ensureParentDirExists(secondaryUserDir)
         return context.getSharedPreferences(secondaryUserDir, mode)
     }
 
@@ -141,4 +144,18 @@
             }
         }
     }
+
+    /**
+     * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs
+     * recursively.
+     */
+    @VisibleForTesting
+    internal fun ensureParentDirExists(file: File) {
+        val parent = file.parentFile
+        if (!parent.exists()) {
+            if (!parent.mkdirs()) {
+                Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}")
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index d414660..533c231 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -233,46 +233,29 @@
     }
 
     @Test
-    public void onStart_isBroadcasting_verifyRegisterLeBroadcastServiceCallBack() {
+    public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() {
         when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
                 mLocalBluetoothLeBroadcast);
         mIsBroadcasting = true;
 
         mMediaOutputBaseDialogImpl.onStart();
-
         verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any());
-    }
-
-    @Test
-    public void onStart_notBroadcasting_noRegisterLeBroadcastServiceCallBack() {
-        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
-                mLocalBluetoothLeBroadcast);
-        mIsBroadcasting = false;
-
-        mMediaOutputBaseDialogImpl.onStart();
-
-        verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any());
-    }
-
-    @Test
-    public void onStart_isBroadcasting_verifyUnregisterLeBroadcastServiceCallBack() {
-        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
-                mLocalBluetoothLeBroadcast);
-        mIsBroadcasting = true;
 
         mMediaOutputBaseDialogImpl.onStop();
-
         verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any());
     }
 
     @Test
-    public void onStop_notBroadcasting_noUnregisterLeBroadcastServiceCallBack() {
+    public void
+            whenNotBroadcasting_verifyLeBroadcastServiceCallBackIsNotRegisteredOrUnregistered() {
         when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
                 mLocalBluetoothLeBroadcast);
         mIsBroadcasting = false;
 
+        mMediaOutputBaseDialogImpl.onStart();
         mMediaOutputBaseDialogImpl.onStop();
 
+        verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any());
         verify(mLocalBluetoothLeBroadcast, never()).unregisterServiceCallBack(any());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
index 73226fa..6d9b01e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Executor
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -62,6 +63,14 @@
             broadcastDispatcher, backgroundExecutor)
     }
 
+    @After
+    fun end() {
+        val dir = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID)
+        dir.deleteRecursively()
+    }
+
     @Test
     fun testGetFile() {
         assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path)
@@ -72,8 +81,19 @@
 
     @Test
     fun testGetSharedPreferences() {
+        val secondarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)
+        val secondaryUserDir = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID,
+            "11",
+            UserFileManagerImpl.SHARED_PREFS,
+            TEST_FILE_NAME
+        )
+
+        assertThat(secondarySharedPref).isNotNull()
+        assertThat(secondaryUserDir.exists())
         assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0))
-            .isNotEqualTo(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11))
+            .isNotEqualTo(secondarySharedPref)
     }
 
     @Test
@@ -115,6 +135,19 @@
         verify(userManager).aliveUsers
         assertThat(secondaryUserDir.exists()).isFalse()
         assertThat(file.exists()).isFalse()
-        dir.deleteRecursively()
+    }
+
+    @Test
+    fun testEnsureParentDirExists() {
+        val file = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID,
+            "11",
+            "files",
+            TEST_FILE_NAME
+        )
+        assertThat(file.parentFile.exists()).isFalse()
+        userFileManager.ensureParentDirExists(file)
+        assertThat(file.parentFile.exists()).isTrue()
     }
 }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 73d9cc7..d29e25c 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -862,10 +862,10 @@
         return list;
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.SHUTDOWN)
     @Override
     public void shutdown() {
         // TODO: remove from aidl if nobody calls externally
-        mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
 
         Slog.i(TAG, "Shutting down");
     }
@@ -1203,9 +1203,9 @@
         setUidOnMeteredNetworkList(uid, true, enable);
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.NETWORK_SETTINGS)
     @Override
     public boolean setDataSaverModeEnabled(boolean enable) {
-        mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
 
         if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
         synchronized (mQuotaLock) {
@@ -1741,9 +1741,9 @@
         return NetdUtils.removeRoutesFromLocalNetwork(mNetdService, routes);
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
     @Override
     public boolean isNetworkRestricted(int uid) {
-        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         return isNetworkRestrictedInternal(uid);
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 7371d07..33e4070 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -564,10 +564,9 @@
         return res;
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @Override
     public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
-        mAm.mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
         Parcel current = Parcel.obtain();
         synchronized (mLock) {
             long now = SystemClock.uptimeMillis();
@@ -619,11 +618,10 @@
      * @return List of proto binary of individual commit files or one that is merged from them;
      *         the merged, final ProcessStats object.
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @Override
     public long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate,
             List<ParcelFileDescriptor> committedStats, ProcessStats mergedStats) {
-        mAm.mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
 
         long newHighWaterMark = highWaterMarkMs;
         mFileLock.lock();
@@ -708,10 +706,9 @@
         return fds[0];
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @Override
     public ParcelFileDescriptor getStatsOverTime(long minTime) {
-        mAm.mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
         Parcel current = Parcel.obtain();
         long curTime;
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 44aefcc..fe9e68d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -897,17 +897,6 @@
                     throw new IllegalStateException("Injection should not result in TARGET_MISMATCH"
                             + " when it is not targeted into to a specific uid.");
                 }
-                // TODO(b/228161340): Remove the fallback of targeting injection into all windows
-                //  when the caller has the injection permission.
-                // Explicitly maintain the same behavior as previous versions of Android, where
-                // injection is allowed into all windows if the caller has the INJECT_EVENTS
-                // permission, even if it is targeting a certain uid.
-                if (checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
-                        "injectInputEvent-target-mismatch-fallback")) {
-                    Slog.w(TAG, "Targeted input event was not directed at a window owned by uid "
-                            + targetUid + ". Falling back to injecting into all windows.");
-                    return injectInputEventToTarget(event, mode, Process.INVALID_UID);
-                }
                 throw new IllegalArgumentException(
                     "Targeted input event injection from pid " + pid
                             + " was not directed at a window owned by uid "
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e29f00b..bbf9410 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3574,23 +3574,18 @@
             int windowFlags, @Nullable EditorInfo editorInfo,
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
             @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
-        return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
-                startInputFlags, softInputMode, windowFlags, editorInfo, inputConnection,
-                remoteAccessibilityInputConnection, unverifiedTargetSdkVersion,
-                imeDispatcher);
-    }
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
 
-    @NonNull
-    private InputBindResult startInputOrWindowGainedFocusInternal(
-            @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
-            @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
-            int windowFlags, @Nullable EditorInfo editorInfo,
-            @Nullable IRemoteInputConnection inputConnection,
-            @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            if (editorInfo == null || editorInfo.targetInputMethodUser == null
+                    || editorInfo.targetInputMethodUser.getIdentifier() != userId) {
+                throw new InvalidParameterException("EditorInfo#targetInputMethodUser must also be "
+                        + "specified for cross-user startInputOrWindowGainedFocus()");
+            }
+        }
+
         if (windowToken == null) {
             Slog.e(TAG, "windowToken cannot be null.");
             return InputBindResult.NULL;
@@ -3600,26 +3595,6 @@
                     "IMMS.startInputOrWindowGainedFocus");
             ImeTracing.getInstance().triggerManagerServiceDump(
                     "InputMethodManagerService#startInputOrWindowGainedFocus");
-            final int callingUserId = UserHandle.getCallingUserId();
-            final int userId;
-            if (editorInfo != null && editorInfo.targetInputMethodUser != null
-                    && editorInfo.targetInputMethodUser.getIdentifier() != callingUserId) {
-                mContext.enforceCallingPermission(
-                        Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                        "Using EditorInfo.targetInputMethodUser requires"
-                                + " INTERACT_ACROSS_USERS_FULL.");
-                userId = editorInfo.targetInputMethodUser.getIdentifier();
-                if (!mUserManagerInternal.isUserRunning(userId)) {
-                    // There is a chance that we hit here because of race condition. Let's just
-                    // return an error code instead of crashing the caller process, which at
-                    // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
-                    // important process.
-                    Slog.e(TAG, "User #" + userId + " is not running.");
-                    return InputBindResult.INVALID_USER;
-                }
-            } else {
-                userId = callingUserId;
-            }
             final InputBindResult result;
             synchronized (ImfLock.class) {
                 final long ident = Binder.clearCallingIdentity();
@@ -3668,9 +3643,19 @@
                     + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                     + " windowFlags=#" + Integer.toHexString(windowFlags)
                     + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
+                    + " userId=" + userId
                     + " imeDispatcher=" + imeDispatcher);
         }
 
+        if (!mUserManagerInternal.isUserRunning(userId)) {
+            // There is a chance that we hit here because of race condition. Let's just
+            // return an error code instead of crashing the caller process, which at
+            // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
+            // important process.
+            Slog.w(TAG, "User #" + userId + " is not running.");
+            return InputBindResult.INVALID_USER;
+        }
+
         final ClientState cs = mClients.get(client.asBinder());
         if (cs == null) {
             throw new IllegalArgumentException("unknown client " + client.asBinder());
@@ -4556,6 +4541,7 @@
     }
 
     @BinderThread
+    @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
     @Override
     public void startImeTrace() {
         ImeTracing.getInstance().startTrace(null /* printwriter */);
@@ -4571,6 +4557,7 @@
     }
 
     @BinderThread
+    @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
     @Override
     public void stopImeTrace() {
         ImeTracing.getInstance().stopTrace(null /* printwriter */);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index c963154..4a0a07b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -38,6 +38,7 @@
 import android.app.ActivityManager.ProcessCapability;
 import android.net.NetworkPolicyManager;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
 
@@ -79,6 +80,8 @@
     private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
     private static final int EVENT_METERED_ALLOWLIST_CHANGED = 15;
     private static final int EVENT_METERED_DENYLIST_CHANGED = 16;
+    private static final int EVENT_ROAMING_CHANGED = 17;
+    private static final int EVENT_INTERFACES_CHANGED = 18;
 
     private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
     private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
@@ -265,6 +268,24 @@
         }
     }
 
+    void roamingChanged(int netId, boolean newRoaming) {
+        synchronized (mLock) {
+            if (LOGD || mDebugUid != INVALID_UID) {
+                Slog.d(TAG, getRoamingChangedLog(netId, newRoaming));
+            }
+            mEventsBuffer.roamingChanged(netId, newRoaming);
+        }
+    }
+
+    void interfacesChanged(int netId, ArraySet<String> newIfaces) {
+        synchronized (mLock) {
+            if (LOGD || mDebugUid != INVALID_UID) {
+                Slog.d(TAG, getInterfacesChangedLog(netId, newIfaces.toString()));
+            }
+            mEventsBuffer.interfacesChanged(netId, newIfaces.toString());
+        }
+    }
+
     void setDebugUid(int uid) {
         mDebugUid = uid;
     }
@@ -348,6 +369,14 @@
         return "metered-denylist for " + uid + " changed to " + added;
     }
 
+    private static String getRoamingChangedLog(int netId, boolean newRoaming) {
+        return "Roaming of netId=" + netId + " changed to " + newRoaming;
+    }
+
+    private static String getInterfacesChangedLog(int netId, String newIfaces) {
+        return "Interfaces of netId=" + netId + " changed to " + newIfaces;
+    }
+
     private static String getFirewallChainName(int chain) {
         switch (chain) {
             case FIREWALL_CHAIN_DOZABLE:
@@ -570,6 +599,28 @@
             data.timeStamp = System.currentTimeMillis();
         }
 
+        public void roamingChanged(int netId, boolean newRoaming) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_ROAMING_CHANGED;
+            data.ifield1 = netId;
+            data.bfield1 = newRoaming;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void interfacesChanged(int netId, String newIfaces) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_INTERFACES_CHANGED;
+            data.ifield1 = netId;
+            data.sfield1 = newIfaces;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
         public void reverseDump(IndentingPrintWriter pw) {
             final Data[] allData = toArray();
             for (int i = allData.length - 1; i >= 0; --i) {
@@ -621,6 +672,10 @@
                     return getMeteredAllowlistChangedLog(data.ifield1, data.bfield1);
                 case EVENT_METERED_DENYLIST_CHANGED:
                     return getMeteredDenylistChangedLog(data.ifield1, data.bfield1);
+                case EVENT_ROAMING_CHANGED:
+                    return getRoamingChangedLog(data.ifield1, data.bfield1);
+                case EVENT_INTERFACES_CHANGED:
+                    return getInterfacesChangedLog(data.ifield1, data.sfield1);
                 default:
                     return String.valueOf(data.type);
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 99a488c..de83f5a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1367,8 +1367,17 @@
                 final boolean roamingChanged = updateCapabilityChange(
                         mNetworkRoaming, newRoaming, network);
 
-                if (meteredChanged || roamingChanged) {
+                final boolean shouldUpdateNetworkRules = meteredChanged || roamingChanged;
+
+                if (meteredChanged) {
                     mLogger.meterednessChanged(network.getNetId(), newMetered);
+                }
+
+                if (roamingChanged) {
+                    mLogger.roamingChanged(network.getNetId(), newRoaming);
+                }
+
+                if (shouldUpdateNetworkRules) {
                     updateNetworkRulesNL();
                 }
             }
@@ -1381,6 +1390,7 @@
                 final boolean ifacesChanged = updateNetworkToIfacesNL(network.getNetId(),
                         newIfaces);
                 if (ifacesChanged) {
+                    mLogger.interfacesChanged(network.getNetId(), newIfaces);
                     updateNetworkRulesNL();
                 }
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f8604c0..2070c2b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6763,9 +6763,8 @@
 
     protected void doChannelWarningToast(int forUid, CharSequence toastText) {
         Binder.withCleanCallingIdentity(() -> {
-            final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
             final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
-                    Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
+                    Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 0) != 0;
             if (warningEnabled) {
                 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
                         Toast.LENGTH_SHORT);
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index b142ba6..122ab10 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -116,7 +116,7 @@
             mScanFlags = scanFlags;
         }
         mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
-        mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+        mSystemScanFlags = mScanFlags | SCAN_AS_SYSTEM;
         mExecutorService = ParallelPackageParser.makeExecutorService();
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3f052c0..2bdf62bd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1829,8 +1829,6 @@
         mAppDataHelper = new AppDataHelper(this);
         mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
         mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
-        mInitAppsHelper = new InitAppsHelper(this, mApexManager, mApexPackageInfo,
-                mInstallPackageHelper, mInjector.getSystemPartitions());
         mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
                 mAppDataHelper);
         mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
@@ -1951,6 +1949,9 @@
                         + ver.fingerprint + " to " + PackagePartitions.FINGERPRINT);
             }
 
+            mInitAppsHelper = new InitAppsHelper(this, mApexManager, mApexPackageInfo,
+                mInstallPackageHelper, mInjector.getSystemPartitions());
+
             // when upgrading from pre-M, promote system app permissions from install to runtime
             mPromoteSystemApps =
                     mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6c1f3bb..69bbff3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2728,8 +2728,13 @@
         }
 
         final StartingSurfaceController.StartingSurface surface;
-        final StartingData startingData = mStartingData;
+        final boolean animate;
         if (mStartingData != null) {
+            animate = prepareAnimation && mStartingData.needRevealAnimation()
+                    && mStartingWindow.isVisibleByPolicy();
+            ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
+                            + " animate=%b Callers=%s", this, mStartingWindow, animate,
+                    Debug.getCallers(5));
             surface = mStartingSurface;
             mStartingData = null;
             mStartingSurface = null;
@@ -2747,21 +2752,7 @@
             return;
         }
 
-
-        ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
-                + " startingView=%s Callers=%s", this, mStartingWindow, mStartingSurface,
-                Debug.getCallers(5));
-
-        final Runnable removeSurface = () -> {
-            ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
-            try {
-                surface.remove(prepareAnimation && startingData.needRevealAnimation());
-            } catch (Exception e) {
-                Slog.w(TAG_WM, "Exception when removing starting window", e);
-            }
-        };
-
-        removeSurface.run();
+        surface.remove(animate);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3e62a29..4066fe1 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2935,6 +2935,10 @@
                 newParent = candidateTf;
             }
         }
+        if (newParent.canHaveEmbeddingActivityTransition(mStartActivity)) {
+            // Make sure the embedded TaskFragment is included in the start activity transition.
+            newParent.collectEmbeddedTaskFragmentIfNeeded();
+        }
         if (mStartActivity.getTaskFragment() == null
                 || mStartActivity.getTaskFragment() == newParent) {
             newParent.addChild(mStartActivity, POSITION_TOP);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 3e6546e..1c20fb3 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2316,6 +2316,26 @@
         return !startBounds.equals(getBounds());
     }
 
+    boolean canHaveEmbeddingActivityTransition(@NonNull ActivityRecord child) {
+        if (!isOrganizedTaskFragment() || !mTransitionController.isShellTransitionsEnabled()) {
+            return false;
+        }
+        // The activity should request open transition when it is becoming visible.
+        return child.isVisibleRequested();
+    }
+
+    void collectEmbeddedTaskFragmentIfNeeded() {
+        if (!isOrganizedTaskFragment() || mTransitionController.isCollecting(this)) {
+            return;
+        }
+        if (getChildCount() == 0) {
+            // The TaskFragment is new created, and just becoming non-empty.
+            mTransitionController.collectExistenceChange(this);
+        } else {
+            mTransitionController.collect(this);
+        }
+    }
+
     @Override
     void setSurfaceControl(SurfaceControl sc) {
         super.setSurfaceControl(sc);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 91f69a5..31d8eb8 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1149,6 +1149,26 @@
         return false;
     }
 
+    private static boolean isTranslucent(@NonNull WindowContainer wc) {
+        final TaskFragment taskFragment = wc.asTaskFragment();
+        if (taskFragment != null) {
+            if (taskFragment.isTranslucent(null /* starting */)) {
+                return true;
+            }
+            final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+            if (adjacentTaskFragment != null) {
+                // Treat the TaskFragment as translucent if its adjacent TF is, otherwise everything
+                // behind two adjacent TaskFragments are occluded.
+                return adjacentTaskFragment.isTranslucent(null /* starting */);
+            }
+        }
+        // TODO(b/172695805): hierarchical check. This is non-trivial because for containers
+        //                    it is effected by child visibility but needs to work even
+        //                    before visibility is committed. This means refactoring some
+        //                    checks to use requested visibility.
+        return !wc.fillsParent();
+    }
+
     /**
      * Under some conditions (eg. all visible targets within a parent container are transitioning
      * the same way) the transition can be "promoted" to the parent container. This means an
@@ -1701,20 +1721,13 @@
             if (mShowWallpaper || wc.showWallpaper()) {
                 flags |= FLAG_SHOW_WALLPAPER;
             }
-            if (!wc.fillsParent()) {
-                // TODO(b/172695805): hierarchical check. This is non-trivial because for containers
-                //                    it is effected by child visibility but needs to work even
-                //                    before visibility is committed. This means refactoring some
-                //                    checks to use requested visibility.
+            if (isTranslucent(wc)) {
                 flags |= FLAG_TRANSLUCENT;
             }
             final Task task = wc.asTask();
             if (task != null && task.voiceSession != null) {
                 flags |= FLAG_IS_VOICE_INTERACTION;
             }
-            if (task != null && task.isTranslucent(null)) {
-                flags |= FLAG_TRANSLUCENT;
-            }
             final ActivityRecord record = wc.asActivityRecord();
             if (record != null) {
                 if (record.mUseTransferredAnimation) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d979753..0f5655c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -843,6 +844,8 @@
                     break;
                 }
 
+                prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(parent,
+                        activity);
                 activity.reparent(parent, POSITION_TOP);
                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
                 break;
@@ -1069,6 +1072,41 @@
         return effects;
     }
 
+    private void prepareActivityEmbeddingTransitionForReparentActivityToTaskFragment(
+            @NonNull TaskFragment taskFragment, @NonNull ActivityRecord activity) {
+        if (!taskFragment.canHaveEmbeddingActivityTransition(activity)) {
+            return;
+        }
+
+        // The reparent can happen in the following cases:
+        // 1. Reparent an existing activity to split when app launches new intent.
+        //    - This happens after app calls to start activity, but before the activity is actually
+        //      started, so we don't expect any collecting transition, but if it does, we can't
+        //      queue the WCT because the start activity won't wait.
+        // 2. Reparent an existing activity to split to launch placeholder when Task size changed.
+        //    - We expect to have a collecting transition for the Task resize, so just collect.
+        // 3. Reparent a new launching activity to an always-expand container.
+        // 4. Reparent a new launching activity to split to launch placeholder together.
+        // 5. Reparent a new launching activity to an existing split.
+        //    - The new launching activity should have start an OPEN transition, so just collect.
+        // 6. Reparent PiP activity back to the original Task.
+        //    - This should be part of the exiting PiP transition, so just collect.
+
+        if (!taskFragment.getBounds().equals(activity.getBounds()) && activity.isVisible()
+                && !mTransitionController.isCollecting()) {
+            // 1. Reparent an existing activity to split when app launches new intent.
+            mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE, activity);
+        }
+
+        // We expect the activity to be in the transition already, so just collect the TaskFragment.
+        if (mTransitionController.isCollecting(activity)) {
+            taskFragment.collectEmbeddedTaskFragmentIfNeeded();
+        } else {
+            ProtoLog.w(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Reparenting Activity"
+                    + " to embedded TaskFragment, but the Activity is not collected");
+        }
+    }
+
     /** A helper method to send minimum dimension violation error to the client. */
     private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
             IBinder errorCallbackToken, String reason) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3ef4aae..366ca3b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -137,7 +137,6 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.WINDOW;
@@ -4957,10 +4956,6 @@
                 || isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
     }
 
-    boolean isExitAnimationRunningSelfOrChild() {
-        return isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION);
-    }
-
     private boolean shouldFinishAnimatingExit() {
         // Exit animation might be applied soon.
         if (inTransition()) {
@@ -5916,6 +5911,10 @@
         if (!super.prepareSync()) {
             return false;
         }
+        if (mIsWallpaper) {
+            // TODO(b/233286785): Add sync support to wallpaper.
+            return false;
+        }
         // In the WindowContainer implementation we immediately mark ready
         // since a generic WindowContainer only needs to wait for its
         // children to finish and is immediately ready from its own
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
index cdb6324..e44ce3c 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
@@ -34,4 +34,5 @@
     ],
     platform_apis: true,
     test_suites: ["device-tests"],
+    data: [":AppEnumerationSyncProviderTestApp"],
 }
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
index f48974a..cca8ec7 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
@@ -30,7 +30,7 @@
     </target_preparer>
 
     <!-- Load additional APKs onto device -->
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" />
         <option name="push" value="AppEnumerationHasAppOpPermissionTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationHasAppOpPermissionTestApp.apk" />
         <option name="push" value="AppEnumerationSharedUserTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSharedUserTestApp.apk" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 60a159154..4640d36 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -88,6 +88,12 @@
     @Test
     override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
 
+    /** {@inheritDoc} */
+    @FlakyTest(bugId = 240238245)
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 386c0cb..a9fb0f2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -83,6 +83,7 @@
         transitions {
             tapl.launchedAppState.quickSwitchToPreviousApp()
             wmHelper.StateSyncBuilder()
+                .withFullScreenApp(testApp1)
                 .withNavOrTaskBarVisible()
                 .withStatusBarVisible()
                 .waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index cefcb35..3b60212 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -79,6 +79,7 @@
                 testApp2.launchViaIntent(wmHelper)
                 tapl.launchedAppState.quickSwitchToPreviousApp()
                 wmHelper.StateSyncBuilder()
+                    .withFullScreenApp(testApp1)
                     .withNavOrTaskBarVisible()
                     .withStatusBarVisible()
                     .waitForAndVerify()
@@ -89,6 +90,7 @@
         transitions {
             tapl.launchedAppState.quickSwitchToPreviousAppSwipeLeft()
             wmHelper.StateSyncBuilder()
+                .withFullScreenApp(testApp2)
                 .withNavOrTaskBarVisible()
                 .withStatusBarVisible()
                 .waitForAndVerify()