Merge "Delete DataProducer abstraction." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index af3981c..7a1add3 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -242,6 +242,12 @@
     aconfig_declarations: "com.android.text.flags-aconfig",
 }
 
+rust_aconfig_library {
+    name: "libandroid_text_flags_rust",
+    crate_name: "android_text_flags",
+    aconfig_declarations: "com.android.text.flags-aconfig",
+}
+
 // Location
 aconfig_declarations {
     name: "android.location.flags-aconfig",
@@ -372,6 +378,7 @@
     min_sdk_version: "30",
     apex_available: [
         "//apex_available:platform",
+        "com.android.btservices",
         "com.android.mediaprovider",
         "com.android.permission",
     ],
@@ -412,17 +419,6 @@
 cc_aconfig_library {
     name: "android.companion.virtualdevice.flags-aconfig-cc",
     aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
-}
-
-cc_aconfig_library {
-    name: "android.companion.virtualdevice.flags-aconfig-cc-host",
-    aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
-    host_supported: true,
-}
-
-cc_aconfig_library {
-    name: "android.companion.virtualdevice.flags-aconfig-cc-test",
-    aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
     host_supported: true,
     mode: "test",
 }
diff --git a/Android.bp b/Android.bp
index f0aa62c..eabd9c7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -417,7 +417,6 @@
         "modules-utils-fastxmlserializer",
         "modules-utils-preconditions",
         "modules-utils-statemachine",
-        "modules-utils-synchronous-result-receiver",
         "modules-utils-os",
         "modules-utils-uieventlogger-interface",
         "framework-permission-aidl-java",
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 1653edc..856dba3 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -38,3 +38,10 @@
     ],
     certificate: "platform",
 }
+
+filegroup {
+    name: "multi_user_trace_config",
+    srcs: [
+        "trace_configs/trace_config_multi_user.textproto",
+    ],
+}
diff --git a/api/Android.bp b/api/Android.bp
index 89a0c18..d931df1 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -62,40 +62,8 @@
 metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
 metalava_cmd += " --quiet "
 
-soong_config_module_type {
-    name: "enable_crashrecovery_module",
-    module_type: "combined_apis_defaults",
-    config_namespace: "ANDROID",
-    bool_variables: ["release_crashrecovery_module"],
-    properties: [
-        "bootclasspath",
-        "system_server_classpath",
-    ],
-}
-
-soong_config_bool_variable {
-    name: "release_crashrecovery_module",
-}
-
-enable_crashrecovery_module {
-    name: "crashrecovery_module_defaults",
-    soong_config_variables: {
-        release_crashrecovery_module: {
-            bootclasspath: [
-                "framework-crashrecovery",
-            ],
-            system_server_classpath: [
-                "service-crashrecovery",
-            ],
-        },
-    },
-}
-
 combined_apis {
     name: "frameworks-base-api",
-    defaults: [
-        "crashrecovery_module_defaults",
-    ],
     bootclasspath: [
         "android.net.ipsec.ike",
         "art.module.public.api",
@@ -128,7 +96,12 @@
         "framework-virtualization",
         "framework-wifi",
         "i18n.module.public.api",
-    ],
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [
+            "framework-crashrecovery",
+        ],
+        default: [],
+    }),
     system_server_classpath: [
         "service-art",
         "service-configinfrastructure",
@@ -137,7 +110,12 @@
         "service-permission",
         "service-rkp",
         "service-sdksandbox",
-    ],
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [
+            "service-crashrecovery",
+        ],
+        default: [],
+    }),
 }
 
 genrule {
diff --git a/api/api.go b/api/api.go
index f0d1f42..b6b1a7e 100644
--- a/api/api.go
+++ b/api/api.go
@@ -63,7 +63,6 @@
 
 type CombinedApis struct {
 	android.ModuleBase
-	android.DefaultableModuleBase
 
 	properties CombinedApisProperties
 }
@@ -74,7 +73,6 @@
 
 func registerBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
-	ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory)
 }
 
 var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
@@ -576,7 +574,6 @@
 	module := &CombinedApis{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
-	android.InitDefaultableModule(module)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
 	return module
 }
@@ -613,16 +610,3 @@
 	}
 	return s2
 }
-
-// Defaults
-type CombinedApisModuleDefaults struct {
-	android.ModuleBase
-	android.DefaultsModuleBase
-}
-
-func CombinedApisModuleDefaultsFactory() android.Module {
-	module := &CombinedApisModuleDefaults{}
-	module.AddProperties(&CombinedApisProperties{})
-	android.InitDefaultsModule(module)
-	return module
-}
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 845a346..ac37113 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -1381,6 +1381,18 @@
             }
             int toId = findLatestEventIdForTime(playTime);
             handleAnimationEvents(-1, toId, playTime);
+
+            if (mSeekState.isActive()) {
+                // Pump a frame to the on-going animators
+                for (int i = 0; i < mPlayingSet.size(); i++) {
+                    Node node = mPlayingSet.get(i);
+                    if (!node.mEnded) {
+                        pulseFrame(node, getPlayTimeForNodeIncludingDelay(playTime, node));
+                    }
+                }
+            }
+
+            // Remove all the finished anims
             for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
                 if (mPlayingSet.get(i).mEnded) {
                     mPlayingSet.remove(i);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 36b1eab..6df971a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2105,8 +2105,7 @@
         @Override
         public void scheduleTaskFragmentTransaction(@NonNull ITaskFragmentOrganizer organizer,
                 @NonNull TaskFragmentTransaction transaction) throws RemoteException {
-            // TODO(b/260873529): ITaskFragmentOrganizer can be cleanup to be a IBinder token
-            // after flag removal.
+            // TODO(b/352665082): ITaskFragmentOrganizer can be cleanup to be a IBinder token
             organizer.onTransactionReady(transaction);
         }
 
diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java
index 598bd8a..e86ca37 100644
--- a/core/java/android/app/servertransaction/ObjectPool.java
+++ b/core/java/android/app/servertransaction/ObjectPool.java
@@ -16,70 +16,39 @@
 
 package android.app.servertransaction;
 
-import com.android.window.flags.Flags;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * An object pool that can provide reused objects if available.
+ *
  * @hide
+ * @deprecated This class is deprecated. Directly create new instances of objects instead of
+ * obtaining them from this pool.
+ * TODO(b/311089192): Clean up usages of the pool.
  */
+@Deprecated
 class ObjectPool {
 
-    private static final Object sPoolSync = new Object();
-    private static final Map<Class, ArrayList<? extends ObjectPoolItem>> sPoolMap =
-            new HashMap<>();
-
-    private static final int MAX_POOL_SIZE = 50;
-
     /**
      * Obtain an instance of a specific class from the pool
-     * @param itemClass The class of the object we're looking for.
+     *
+     * @param ignoredItemClass The class of the object we're looking for.
      * @return An instance or null if there is none.
+     * @deprecated This method is deprecated. Directly create new instances of objects instead of
+     * obtaining them from this pool.
      */
-    public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) {
-        if (Flags.disableObjectPool()) {
-            return null;
-        }
-        synchronized (sPoolSync) {
-            @SuppressWarnings("unchecked")
-            final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass);
-            if (itemPool != null && !itemPool.isEmpty()) {
-                return itemPool.remove(itemPool.size() - 1);
-            }
-            return null;
-        }
+    @Deprecated
+    public static <T extends ObjectPoolItem> T obtain(Class<T> ignoredItemClass) {
+        return null;
     }
 
     /**
      * Recycle the object to the pool. The object should be properly cleared before this.
-     * @param item The object to recycle.
+     *
+     * @param ignoredItem The object to recycle.
      * @see ObjectPoolItem#recycle()
+     * @deprecated This method is deprecated. The object pool is no longer used, so there's
+     * no need to recycle objects.
      */
-    public static <T extends ObjectPoolItem> void recycle(T item) {
-        if (Flags.disableObjectPool()) {
-            return;
-        }
-        synchronized (sPoolSync) {
-            @SuppressWarnings("unchecked")
-            ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(item.getClass());
-            if (itemPool == null) {
-                itemPool = new ArrayList<>();
-                sPoolMap.put(item.getClass(), itemPool);
-            }
-            // Check if the item is already in the pool
-            final int size = itemPool.size();
-            for (int i = 0; i < size; i++) {
-                if (itemPool.get(i) == item) {
-                    throw new IllegalStateException("Trying to recycle already recycled item");
-                }
-            }
-
-            if (size < MAX_POOL_SIZE) {
-                itemPool.add(item);
-            }
-        }
+    @Deprecated
+    public static <T extends ObjectPoolItem> void recycle(T ignoredItem) {
     }
 }
diff --git a/core/java/android/app/servertransaction/ObjectPoolItem.java b/core/java/android/app/servertransaction/ObjectPoolItem.java
index 17bd4f3..0141f6e 100644
--- a/core/java/android/app/servertransaction/ObjectPoolItem.java
+++ b/core/java/android/app/servertransaction/ObjectPoolItem.java
@@ -18,12 +18,20 @@
 
 /**
  * Base interface for all lifecycle items that can be put in object pool.
+ *
  * @hide
+ * @deprecated This interface is deprecated. Objects should no longer be pooled.
+ * TODO(b/311089192): Clean up usages of this interface.
  */
+@Deprecated
 public interface ObjectPoolItem {
     /**
      * Clear the contents of the item and putting it to a pool. The implementation should call
      * {@link ObjectPool#recycle(ObjectPoolItem)} passing itself.
+     *
+     * @deprecated This method is deprecated. The object pool is no longer used, so there's
+     * no need to recycle objects.
      */
+    @Deprecated
     void recycle();
 }
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index a49ee7d..0c34c6f 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -110,92 +110,6 @@
     void shutdown();
 
     /**
-     ** TETHERING RELATED
-     **/
-
-    /**
-     * Returns true if IP forwarding is enabled
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Use {@code android.net.INetd#ipfwdEnabled}")
-    boolean getIpForwardingEnabled();
-
-    /**
-     * Enables/Disables IP Forwarding
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
-            + "{@code android.net.TetheringManager#startTethering}. See also "
-            + "{@code INetd#ipfwdEnableForwarding(String)}.")
-    void setIpForwardingEnabled(boolean enabled);
-
-    /**
-     * Start tethering services with the specified dhcp server range
-     * arg is a set of start end pairs defining the ranges.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "{@code android.net.TetheringManager#startTethering}")
-    void startTethering(in String[] dhcpRanges);
-
-    /**
-     * Stop currently running tethering services
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "{@code android.net.TetheringManager#stopTethering(int)}")
-    void stopTethering();
-
-    /**
-     * Returns true if tethering services are started
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Generally track your own tethering requests. "
-            + "See also {@code android.net.INetd#tetherIsEnabled()}")
-    boolean isTetheringStarted();
-
-    /**
-     * Tethers the specified interface
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
-            + "{@code android.net.TetheringManager#startTethering}. See also "
-            + "{@code com.android.net.module.util.NetdUtils#tetherInterface}.")
-    void tetherInterface(String iface);
-
-    /**
-     * Untethers the specified interface
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, disable "
-            + "tethering with {@code android.net.TetheringManager#stopTethering(int)}. "
-            + "See also {@code NetdUtils#untetherInterface}.")
-    void untetherInterface(String iface);
-
-    /**
-     * Returns a list of currently tethered interfaces
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "{@code android.net.TetheringManager#getTetheredIfaces()}")
-    String[] listTetheredInterfaces();
-
-    /**
-     *  Enables Network Address Translation between two interfaces.
-     *  The address and netmask of the external interface is used for
-     *  the NAT'ed network.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
-            + "{@code android.net.TetheringManager#startTethering}.")
-    void enableNat(String internalInterface, String externalInterface);
-
-    /**
-     *  Disables Network Address Translation between two interfaces.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, disable tethering with "
-            + "{@code android.net.TetheringManager#stopTethering(int)}.")
-    void disableNat(String internalInterface, String externalInterface);
-
-    /**
      ** DATA USAGE RELATED
      **/
 
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 0be2d3e3..e95c6a4 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -277,7 +277,7 @@
             if (service != null) {
                 return service;
             } else {
-                return Binder.allowBlocking(getIServiceManager().checkService(name));
+                return Binder.allowBlocking(getIServiceManager().checkService(name).getBinder());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "error in checkService", e);
@@ -425,7 +425,7 @@
     private static IBinder rawGetService(String name) throws RemoteException {
         final long start = sStatLogger.getTime();
 
-        final IBinder binder = getIServiceManager().getService(name);
+        final IBinder binder = getIServiceManager().getService(name).getBinder();
 
         final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
 
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 7b91dd5..6c9a5c7 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -58,12 +58,12 @@
     }
 
     @UnsupportedAppUsage
-    public IBinder getService(String name) throws RemoteException {
+    public Service getService(String name) throws RemoteException {
         // Same as checkService (old versions of servicemanager had both methods).
-        return mServiceManager.checkService(name);
+        return checkService(name);
     }
 
-    public IBinder checkService(String name) throws RemoteException {
+    public Service checkService(String name) throws RemoteException {
         return mServiceManager.checkService(name);
     }
 
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 292e6bd..50b73a9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -126,20 +126,18 @@
  * method:
  *
  * <pre>
- * public void onCreate() {
- *     StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
- *             .detectDiskReads()
- *             .detectDiskWrites()
- *             .detectNetwork()   // or .detectAll() for all detectable problems
- *             .penaltyLog()
- *             .build());
- *     StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
- *             .detectLeakedSqlLiteObjects()
- *             .detectLeakedClosableObjects()
- *             .penaltyLog()
- *             .penaltyDeath()
- *             .build());
- *     super.onCreate();
+ * override fun onCreate(savedInstanceState: Bundle?) {
+ *     super.onCreate(savedInstanceState)
+ *     StrictMode.setThreadPolicy(
+ *         StrictMode.ThreadPolicy.Builder()
+ *         .detectAll()
+ *         .build()
+ *     )
+ *     StrictMode.setVmPolicy(
+ *         StrictMode.VmPolicy.Builder()
+ *         .detectAll()
+ *         .build()
+ *     )
  * }
  * </pre>
  *
@@ -354,7 +352,7 @@
     public static final int NETWORK_POLICY_LOG = 1;
     /** {@hide} */
     public static final int NETWORK_POLICY_REJECT = 2;
-  
+
     /**
      * Detect explicit calls to {@link Runtime#gc()}.
      */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2562c8e..ff38920 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11075,6 +11075,13 @@
         public static final String MANDATORY_BIOMETRICS = "mandatory_biometrics";
 
         /**
+         * Whether or not requirements for mandatory biometrics is satisfied.
+         * @hide
+         */
+        public static final String MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED =
+                "mandatory_biometrics_requirements_satisfied";
+
+        /**
          * Whether or not active unlock triggers on wake.
          * @hide
          */
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index e4fc1cd..fbeab84 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -787,7 +787,6 @@
      */
     public void setInteractive(boolean interactive) {
         mInteractive = interactive;
-        updateAccessibilityMessage();
     }
 
     /**
@@ -1641,9 +1640,9 @@
         if (mWindow == null) return;
         if (mDreamAccessibility == null) {
             final View rootView = mWindow.getDecorView();
-            mDreamAccessibility = new DreamAccessibility(this, rootView);
+            mDreamAccessibility = new DreamAccessibility(this, rootView, this::wakeUp);
         }
-        mDreamAccessibility.updateAccessibilityConfiguration(isInteractive());
+        mDreamAccessibility.updateAccessibilityConfiguration();
     }
 
     private boolean getWindowFlagValue(int flag, boolean defaultValue) {
diff --git a/core/java/android/service/dreams/utils/DreamAccessibility.java b/core/java/android/service/dreams/utils/DreamAccessibility.java
index c38f41b..f504ff7 100644
--- a/core/java/android/service/dreams/utils/DreamAccessibility.java
+++ b/core/java/android/service/dreams/utils/DreamAccessibility.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Bundle;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -32,22 +33,22 @@
     private final Context mContext;
     private final View mView;
     private final View.AccessibilityDelegate mAccessibilityDelegate;
+    private final Runnable mDismissCallback;
 
-    public DreamAccessibility(@NonNull Context context, @NonNull View view) {
+    public DreamAccessibility(@NonNull Context context, @NonNull View view,
+            @NonNull Runnable dismissCallback) {
         mContext = context;
         mView = view;
         mAccessibilityDelegate = createNewAccessibilityDelegate(mContext);
+        mDismissCallback = dismissCallback;
     }
 
     /**
-     * @param interactive
-     * Removes and add accessibility configuration depending if the dream is interactive or not
+     *  Adds default accessibility configuration if none exist on the dream
      */
-    public void updateAccessibilityConfiguration(Boolean interactive) {
-        if (!interactive) {
+    public void updateAccessibilityConfiguration() {
+        if (mView.getAccessibilityDelegate() == null) {
             addAccessibilityConfiguration();
-        } else {
-            removeCustomAccessibilityAction();
         }
     }
 
@@ -58,31 +59,28 @@
         mView.setAccessibilityDelegate(mAccessibilityDelegate);
     }
 
-    /**
-     * Removes Configured the accessibility actions for the given root view.
-     */
-    private void removeCustomAccessibilityAction() {
-        if (mView.getAccessibilityDelegate() == mAccessibilityDelegate) {
-            mView.setAccessibilityDelegate(null);
-        }
-    }
-
     private View.AccessibilityDelegate createNewAccessibilityDelegate(Context context) {
         return new View.AccessibilityDelegate() {
             @Override
             public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
                 super.onInitializeAccessibilityNodeInfo(host, info);
-                for (AccessibilityNodeInfo.AccessibilityAction action : info.getActionList()) {
-                    if (action.getId() == AccessibilityNodeInfo.ACTION_CLICK) {
-                        info.removeAction(action);
-                        break;
-                    }
-                }
                 info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
-                        AccessibilityNodeInfo.ACTION_CLICK,
+                        AccessibilityNodeInfo.ACTION_DISMISS,
                         context.getResources().getString(R.string.dream_accessibility_action_click)
                 ));
             }
+
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                switch(action){
+                    case AccessibilityNodeInfo.ACTION_DISMISS:
+                        if (mDismissCallback != null) {
+                            mDismissCallback.run();
+                        }
+                        break;
+                }
+                return true;
+            }
         };
     }
 }
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index 5d84d17..b07534f 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -68,4 +68,11 @@
     public static boolean fixMisalignedContextMenu() {
         return TextFlags.isFeatureEnabled(Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU);
     }
+
+    /**
+     * @see Flags#clearFontVariationSettings()
+     */
+    public static boolean clearFontVariationSettings() {
+        return TextFlags.isFeatureEnabled(Flags.FLAG_CLEAR_FONT_VARIATION_SETTINGS);
+    }
 }
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 9e02460..4dca284 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -61,6 +61,7 @@
             Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE,
             Flags.FLAG_ICU_BIDI_MIGRATION,
             Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU,
+            Flags.FLAG_CLEAR_FONT_VARIATION_SETTINGS,
     };
 
     /**
@@ -75,6 +76,7 @@
             Flags.fixLineHeightForLocale(),
             Flags.icuBidiMigration(),
             Flags.fixMisalignedContextMenu(),
+            Flags.clearFontVariationSettings(),
     };
 
     /**
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 8836c8a..02c63db 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -220,3 +220,23 @@
   is_fixed_read_only: true
   bug: "346915432"
 }
+
+flag {
+  name: "clear_font_variation_settings"
+  namespace: "text"
+  description: "The font variation settings must be cleared when the new Typeface is set"
+  bug: "353609778"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "portuguese_hyphenator"
+  namespace: "text"
+  description: "Portuguese taiored hyphenator"
+  bug: "344656282"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index fedbe4a..42d66ce 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -366,13 +366,8 @@
                         Log.e(TAG, "Received invalid input event");
                         return;
                     }
-                    try {
-                        vri.processingBackKey(true);
-                        vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
-                                true /* processImmediately */);
-                    } finally {
-                        vri.processingBackKey(false);
-                    }
+                    vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
+                            true /* processImmediately */);
                 });
         }
     };
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3a1d833..2f204f9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -727,8 +727,6 @@
     boolean mUpcomingWindowFocus;
     @GuardedBy("this")
     boolean mUpcomingInTouchMode;
-    // While set, allow this VRI to handle back key without drop it.
-    private boolean mProcessingBackKey;
     /**
      * Compatibility {@link OnBackInvokedCallback} for windowless window, to forward the back
      * key event host app.
@@ -7269,7 +7267,7 @@
             // Find a reason for dropping or canceling the event.
             final String reason;
             // The embedded window is focused, allow this VRI to handle back key.
-            if (!mAttachInfo.mHasWindowFocus && !(mProcessingBackKey && isBack(q.mEvent))
+            if (!mAttachInfo.mHasWindowFocus && !isBack(q.mEvent)
                     && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
                     && !isAutofillUiShowing()) {
                 // This is a non-pointer event and the window doesn't currently have input focus
@@ -11218,11 +11216,6 @@
         mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget();
     }
 
-    // Make this VRI able to process back key without drop it.
-    void processingBackKey(boolean processing) {
-        mProcessingBackKey = processing;
-    }
-
     /**
      * Collect and include any ScrollCaptureCallback instances registered with the window.
      *
@@ -12554,15 +12547,8 @@
      * @return whether the event was handled (i.e. onKeyPreIme consumed it if preImeOnly=true)
      */
     public boolean injectBackKeyEvents(boolean preImeOnly) {
-        boolean consumed;
-        try {
-            processingBackKey(true);
-            sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly);
-            consumed = sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly);
-        } finally {
-            processingBackKey(false);
-        }
-        return consumed;
+        sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly);
+        return sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly);
     }
 
     private boolean sendBackKeyEvent(int action, boolean preImeOnly) {
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 5b1c7d5..0ab51e4 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -563,7 +563,7 @@
         return DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_AUTOFILL,
                 DEVICE_CONFIG_ENABLE_RELAYOUT,
-                true);
+                false);
     }
 
     /** @hide */
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 9512347..0dadbe3 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -7438,8 +7438,7 @@
             // If the user interacts with a visible element it is safe to assume they consent that
             // something is going to start.
             opts.setPendingIntentBackgroundActivityStartMode(
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
-            opts.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
             return Pair.create(intent, opts);
         }
     }
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 48fb2b3..f739622 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -98,6 +98,13 @@
 }
 
 flag {
+  name: "scrolling_from_letterbox"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether to enable app scrolling from gestures from letterbox area"
+  bug: "353697519"
+}
+
+flag {
   name: "app_compat_refactoring"
   namespace: "large_screen_experiences_app_compat"
   description: "Whether the changes about app compat refactoring are enabled./n"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index ae9d757..13d465f 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -3,16 +3,6 @@
 
 # Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
 
-# Using a fixed read only flag because there are ClientTransaction scheduling before
-# WindowManagerService creation.
-flag {
-    namespace: "windowing_sdk"
-    name: "bundle_client_transaction_flag"
-    description: "To bundle multiple ClientTransactionItems into one ClientTransaction"
-    bug: "260873529"
-    is_fixed_read_only: true
-}
-
 flag {
     namespace: "windowing_sdk"
     name: "activity_embedding_overlay_presentation_flag"
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 24971f5..488e06f 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -580,10 +580,15 @@
         }
         PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter();
         for (int i = 0; i < uidStats.size(); i++) {
+            String formattedStats = uidStatsFormatter.format(uidStats.valueAt(i));
+            if (formattedStats.isBlank()) {
+                continue;
+            }
+
             pw.print("UID ");
             pw.print(UserHandle.formatUid(uidStats.keyAt(i)));
             pw.print(": ");
-            pw.print(uidStatsFormatter.format(uidStats.valueAt(i)));
+            pw.print(formattedStats);
             pw.println();
         }
         pw.decreaseIndent();
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
index cb3f99c..33a46d0 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
@@ -35,6 +35,8 @@
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
 
 @MediumTest
 public class AnimatorSetCallsTest {
@@ -447,6 +449,43 @@
         mActivity.runOnUiThread(() -> {});
     }
 
+    @Test
+    public void startAfterSeek() throws Throwable {
+        ArrayList<Float> values = new ArrayList<>();
+        AtomicReference<CountDownLatch> drawLatch = new AtomicReference<>(new CountDownLatch(1));
+
+        mActivity.runOnUiThread(() -> {
+            mAnimator.setDuration(300);
+            mAnimator.setInterpolator(null);
+            View view = (View) mAnimator.getTarget();
+            view.getViewTreeObserver().addOnDrawListener(() -> {
+                values.add(view.getTranslationX());
+                drawLatch.get().countDown();
+            });
+            mSet1.setCurrentPlayTime(150);
+        });
+
+        assertTrue(drawLatch.get().await(1, TimeUnit.SECONDS));
+        drawLatch.set(new CountDownLatch(1));
+
+        mActivity.runOnUiThread(() -> {
+            assertEquals(1, values.size());
+            assertEquals(50f, values.get(0), 0.01f);
+            mSet1.start();
+        });
+
+        assertTrue(drawLatch.get().await(1, TimeUnit.SECONDS));
+
+        mActivity.runOnUiThread(() -> {
+            assertTrue(values.size() >= 2);
+            float lastValue = values.get(0);
+            for (int i = 1; i < values.size(); i++) {
+                assertTrue(values.get(i) >= lastValue);
+                lastValue = values.get(i);
+            }
+        });
+    }
+
     private void waitForOnUiThread(PollingCheck.PollingCheckCondition condition) {
         final boolean[] value = new boolean[1];
         PollingCheck.waitFor(() -> {
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 918235b..e429cfc 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -21,12 +21,7 @@
 import static android.app.servertransaction.TestUtils.referrerIntentList;
 import static android.app.servertransaction.TestUtils.resultInfoList;
 
-import static com.android.window.flags.Flags.FLAG_DISABLE_OBJECT_POOL;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertSame;
 
 import android.annotation.NonNull;
 import android.app.ActivityOptions;
@@ -41,14 +36,11 @@
 import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.window.ActivityWindowInfo;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.window.flags.Flags;
-
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -56,12 +48,8 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import java.util.List;
 import java.util.function.Supplier;
 
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
 /**
  * Tests for {@link ObjectPool}.
  *
@@ -71,31 +59,19 @@
  * <p>This test class is a part of Window Manager Service tests and specified in
  * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
-@RunWith(ParameterizedAndroidJunit4.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
 public class ObjectPoolTests {
 
-    @Parameters(name = "{0}")
-    public static List<FlagsParameterization> getParams() {
-        return FlagsParameterization.allCombinationsOf(FLAG_DISABLE_OBJECT_POOL);
-    }
-
     @Rule
     public final MockitoRule mocks = MockitoJUnit.rule();
 
-    @Rule
-    public SetFlagsRule mSetFlagsRule;
-
     @Mock
     private IApplicationThread mApplicationThread;
     @Mock
     private IBinder mActivityToken;
 
-    public ObjectPoolTests(FlagsParameterization flags) {
-        mSetFlagsRule = new SetFlagsRule(flags);
-    }
-
     // 1. Check if two obtained objects from pool are not the same.
     // 2. Check if the state of the object is cleared after recycling.
     // 3. Check if the same object is obtained from pool after recycling.
@@ -219,30 +195,11 @@
         item.recycle();
         final ObjectPoolItem item2 = obtain.get();
 
-        if (Flags.disableObjectPool()) {
-            assertNotSame(item, item2);  // Different instance.
-        } else {
-            assertSame(item, item2);
-        }
+        assertNotSame(item, item2);  // Different instance.
 
         // Create new object when the pool is empty.
         final ObjectPoolItem item3 = obtain.get();
 
         assertNotSame(item, item3);
-        if (Flags.disableObjectPool()) {
-            // Skip recycle if flag enabled, compare unnecessary.
-            return;
-        }
-        assertEquals(item, item3);
-
-        // Reset fields after recycle.
-        item.recycle();
-
-        assertNotEquals(item, item3);
-
-        // Recycled objects are equal.
-        item3.recycle();
-
-        assertEquals(item, item3);
     }
 }
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index df95a91..b83931f 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -35,6 +35,7 @@
 import android.graphics.fonts.FontVariationAxis;
 import android.os.Build;
 import android.os.LocaleList;
+import android.text.ClientFlags;
 import android.text.GraphicsOperations;
 import android.text.SpannableString;
 import android.text.SpannedString;
@@ -1540,8 +1541,21 @@
      * @return         typeface
      */
     public Typeface setTypeface(Typeface typeface) {
+        return setTypefaceInternal(typeface, true);
+    }
+
+    private Typeface setTypefaceInternal(Typeface typeface, boolean clearFontVariationSettings) {
         final long typefaceNative = typeface == null ? 0 : typeface.native_instance;
         nSetTypeface(mNativePaint, typefaceNative);
+
+        if (ClientFlags.clearFontVariationSettings()) {
+            if (clearFontVariationSettings && !Objects.equals(mTypeface, typeface)) {
+                // We cannot call setFontVariationSetting with empty string or null because it calls
+                // setTypeface method. To avoid recursive setTypeface call, manually resetting
+                // mFontVariationSettings.
+                mFontVariationSettings = null;
+            }
+        }
         mTypeface = typeface;
         return typeface;
     }
@@ -2037,6 +2051,14 @@
      * </li>
      * </ul>
      *
+     * Note: This method replaces the Typeface previously set to this instance.
+     * Until API {@link Build.VERSION_CODES.VANILLA_ICE_CREAM}, any caller of
+     * {@link #setTypeface(Typeface)} should call this method with empty settings, then call
+     * {@link #setTypeface(Typeface)}, then call this method with preferred variation settings.
+     * The device API more than {@link Build.VERSION_CODES.VANILLA_ICE_CREAM}, the
+     * {@link #setTypeface(Typeface)} method clears font variation settings. So caller of
+     * {@link #setTypeface(Typeface)} should call this method again for applying variation settings.
+     *
      * @param fontVariationSettings font variation settings. You can pass null or empty string as
      *                              no variation settings.
      *
@@ -2059,8 +2081,8 @@
 
         if (settings == null || settings.length() == 0) {
             mFontVariationSettings = null;
-            setTypeface(Typeface.createFromTypefaceWithVariation(mTypeface,
-                      Collections.emptyList()));
+            setTypefaceInternal(Typeface.createFromTypefaceWithVariation(mTypeface,
+                      Collections.emptyList()), false);
             return true;
         }
 
@@ -2078,7 +2100,8 @@
             return false;
         }
         mFontVariationSettings = settings;
-        setTypeface(Typeface.createFromTypefaceWithVariation(targetTypeface, filteredAxes));
+        setTypefaceInternal(Typeface.createFromTypefaceWithVariation(targetTypeface, filteredAxes),
+                false);
         return true;
     }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index ecf4720..7f11fea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -39,6 +39,8 @@
 import androidx.window.extensions.layout.WindowLayoutComponent;
 import androidx.window.extensions.layout.WindowLayoutComponentImpl;
 
+import com.android.window.flags.Flags;
+
 import java.util.Objects;
 
 
@@ -55,11 +57,9 @@
      */
     private static final int NO_LEVEL_OVERRIDE = -1;
 
-    /**
-     * The min version of the WM Extensions that must be supported in the current platform version.
-     */
-    @VisibleForTesting
-    static final int EXTENSIONS_VERSION_CURRENT_PLATFORM = 6;
+    private static final int EXTENSIONS_VERSION_V7 = 7;
+
+    private static final int EXTENSIONS_VERSION_V6 = 6;
 
     private final Object mLock = new Object();
     private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
@@ -67,7 +67,6 @@
     private volatile SplitController mSplitController;
     private volatile WindowAreaComponent mWindowAreaComponent;
 
-    private final int mVersion = EXTENSIONS_VERSION_CURRENT_PLATFORM;
     private final boolean mIsActivityEmbeddingEnabled;
 
     WindowExtensionsImpl() {
@@ -76,9 +75,22 @@
         Log.i(TAG, generateLogMessage());
     }
 
+    /**
+     * The min version of the WM Extensions that must be supported in the current platform version.
+     */
+    @VisibleForTesting
+    static int getExtensionsVersionCurrentPlatform() {
+        if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+            // Activity Embedding animation customization is the only major feature for v7.
+            return EXTENSIONS_VERSION_V7;
+        } else {
+            return EXTENSIONS_VERSION_V6;
+        }
+    }
+
     private String generateLogMessage() {
         final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, "
-                + "vendor API level=" + mVersion);
+                + "vendor API level=" + getExtensionsVersionCurrentPlatform());
         final int levelOverride = getLevelOverride();
         if (levelOverride != NO_LEVEL_OVERRIDE) {
             logBuilder.append(", override to ").append(levelOverride);
@@ -91,7 +103,12 @@
     @Override
     public int getVendorApiLevel() {
         final int levelOverride = getLevelOverride();
-        return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion;
+        return hasLevelOverride() ? levelOverride : getExtensionsVersionCurrentPlatform();
+    }
+
+    @VisibleForTesting
+    boolean hasLevelOverride() {
+        return getLevelOverride() != NO_LEVEL_OVERRIDE;
     }
 
     private int getLevelOverride() {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index c5aaddc..92f4814 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -16,7 +16,7 @@
 
 package androidx.window.extensions;
 
-import static androidx.window.extensions.WindowExtensionsImpl.EXTENSIONS_VERSION_CURRENT_PLATFORM;
+import static androidx.window.extensions.WindowExtensionsImpl.getExtensionsVersionCurrentPlatform;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -59,7 +59,8 @@
     @Test
     public void testGetVendorApiLevel_extensionsEnabled_matchesCurrentVersion() {
         assumeTrue(WindowManager.hasWindowExtensionsEnabled());
-        assertThat(mVersion).isEqualTo(EXTENSIONS_VERSION_CURRENT_PLATFORM);
+        assumeFalse(((WindowExtensionsImpl) mExtensions).hasLevelOverride());
+        assertThat(mVersion).isEqualTo(getExtensionsVersionCurrentPlatform());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
index f0d80a0..d3fc49b 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -39,8 +39,6 @@
 
   /**
    * Determines state of flag based on the actual flag and desktop mode developer option overrides.
-   *
-   * Note, this method makes sure that a constant developer toggle overrides is read until reboot.
    */
   fun isEnabled(context: Context): Boolean =
       if (!Flags.showDesktopWindowingDevOption() ||
@@ -65,7 +63,7 @@
             ?: run {
               val override = getToggleOverrideFromSystem(context)
               // Cache toggle override the first time we encounter context. Override does not change
-              // with context, as context is just used to fetch System Property and Settings.Global
+              // with context, as context is just used to fetch Settings.Global
               cachedToggleOverride = override
               Log.d(TAG, "Toggle override initialized to: $override")
               override
@@ -74,29 +72,13 @@
     return override
   }
 
-  private fun getToggleOverrideFromSystem(context: Context): ToggleOverride {
-    // A non-persistent System Property is used to store override to ensure it remains
-    // constant till reboot.
-    val overrideFromSystemProperties: ToggleOverride? =
-        System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null).convertToToggleOverride()
-    return overrideFromSystemProperties
-        ?: run {
-          // Read Setting Global if System Property is not present (just after reboot)
-          // or not valid (user manually changed the value)
-          val overrideFromSettingsGlobal =
-              convertToToggleOverrideWithFallback(
-                  Settings.Global.getInt(
-                      context.contentResolver,
-                      Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
-                      ToggleOverride.OVERRIDE_UNSET.setting),
-                  ToggleOverride.OVERRIDE_UNSET)
-          // Initialize System Property
-          System.setProperty(
-              SYSTEM_PROPERTY_OVERRIDE_KEY, overrideFromSettingsGlobal.setting.toString())
-
-          overrideFromSettingsGlobal
-        }
-  }
+  private fun getToggleOverrideFromSystem(context: Context): ToggleOverride =
+      convertToToggleOverrideWithFallback(
+          Settings.Global.getInt(
+              context.contentResolver,
+              Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+              ToggleOverride.OVERRIDE_UNSET.setting),
+          ToggleOverride.OVERRIDE_UNSET)
 
   /**
    * Override state of desktop mode developer option toggle.
@@ -113,27 +95,12 @@
     OVERRIDE_ON(1)
   }
 
-  private fun String?.convertToToggleOverride(): ToggleOverride? {
-    val intValue = this?.toIntOrNull() ?: return null
-    return settingToToggleOverrideMap[intValue]
-        ?: run {
-          Log.w(TAG, "Unknown toggleOverride int $intValue")
-          null
-        }
-  }
-
   companion object {
     private const val TAG = "DesktopModeFlags"
 
     /**
-     * Key for non-persistent System Property which is used to store desktop windowing developer
-     * option overrides.
-     */
-    private const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
-
-    /**
      * Local cache for toggle override, which is initialized once on its first access. It needs to
-     * be refreshed only on reboots as overridden state takes effect on reboots.
+     * be refreshed only on reboots as overridden state is expected to take effect on reboots.
      */
     private var cachedToggleOverride: ToggleOverride? = null
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 64a1b0c..140d776 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Size;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
@@ -42,9 +43,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -69,26 +68,36 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface StashType {}
 
+    public static final int NAMED_KCA_LAUNCHER_SHELF = 0;
+    public static final int NAMED_KCA_TABLETOP_MODE = 1;
+
+    @IntDef(prefix = { "NAMED_KCA_" }, value = {
+            NAMED_KCA_LAUNCHER_SHELF,
+            NAMED_KCA_TABLETOP_MODE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NamedKca {}
+
     private static final String TAG = PipBoundsState.class.getSimpleName();
 
-    private final @NonNull Rect mBounds = new Rect();
-    private final @NonNull Rect mMovementBounds = new Rect();
-    private final @NonNull Rect mNormalBounds = new Rect();
-    private final @NonNull Rect mExpandedBounds = new Rect();
-    private final @NonNull Rect mNormalMovementBounds = new Rect();
-    private final @NonNull Rect mExpandedMovementBounds = new Rect();
-    private final @NonNull PipDisplayLayoutState mPipDisplayLayoutState;
+    @NonNull private final Rect mBounds = new Rect();
+    @NonNull private final Rect mMovementBounds = new Rect();
+    @NonNull private final Rect mNormalBounds = new Rect();
+    @NonNull private final Rect mExpandedBounds = new Rect();
+    @NonNull private final Rect mNormalMovementBounds = new Rect();
+    @NonNull private final Rect mExpandedMovementBounds = new Rect();
+    @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final Point mMaxSize = new Point();
     private final Point mMinSize = new Point();
-    private final @NonNull Context mContext;
+    @NonNull private final Context mContext;
     private float mAspectRatio;
     private int mStashedState = STASH_TYPE_NONE;
     private int mStashOffset;
-    private @Nullable PipReentryState mPipReentryState;
+    @Nullable private PipReentryState mPipReentryState;
     private final LauncherState mLauncherState = new LauncherState();
-    private final @NonNull SizeSpecSource mSizeSpecSource;
-    private @Nullable ComponentName mLastPipComponentName;
-    private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
+    @NonNull private final SizeSpecSource mSizeSpecSource;
+    @Nullable private ComponentName mLastPipComponentName;
+    @NonNull private final MotionBoundsState mMotionBoundsState = new MotionBoundsState();
     private boolean mIsImeShowing;
     private int mImeHeight;
     private boolean mIsShelfShowing;
@@ -120,12 +129,18 @@
      * as unrestricted keep clear area. Values in this map would be appended to
      * {@link #getUnrestrictedKeepClearAreas()} and this is meant for internal usage only.
      */
-    private final Map<String, Rect> mNamedUnrestrictedKeepClearAreas = new HashMap<>();
+    private final SparseArray<Rect> mNamedUnrestrictedKeepClearAreas = new SparseArray<>();
 
-    private @Nullable Runnable mOnMinimalSizeChangeCallback;
-    private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
-    private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
-    private List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();
+    @Nullable private Runnable mOnMinimalSizeChangeCallback;
+    @Nullable private TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
+    private final List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
+    private final List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();
+
+    /**
+     * This is used to set the launcher shelf height ahead of non-auto-enter-pip animation,
+     * to avoid the race condition. See also {@link #NAMED_KCA_LAUNCHER_SHELF}.
+     */
+    public final Rect mCachedLauncherShelfHeightKeepClearArea = new Rect();
 
     // the size of the current bounds relative to the max size spec
     private float mBoundsScale;
@@ -430,17 +445,32 @@
         mUnrestrictedKeepClearAreas.addAll(unrestrictedAreas);
     }
 
-    /** Add a named unrestricted keep clear area. */
-    public void addNamedUnrestrictedKeepClearArea(@NonNull String name, Rect unrestrictedArea) {
-        mNamedUnrestrictedKeepClearAreas.put(name, unrestrictedArea);
+    /** Set a named unrestricted keep clear area. */
+    public void setNamedUnrestrictedKeepClearArea(
+            @NamedKca int tag, @Nullable Rect unrestrictedArea) {
+        if (unrestrictedArea == null) {
+            mNamedUnrestrictedKeepClearAreas.remove(tag);
+        } else {
+            mNamedUnrestrictedKeepClearAreas.put(tag, unrestrictedArea);
+            if (tag == NAMED_KCA_LAUNCHER_SHELF) {
+                mCachedLauncherShelfHeightKeepClearArea.set(unrestrictedArea);
+            }
+        }
     }
 
-    /** Remove a named unrestricted keep clear area. */
-    public void removeNamedUnrestrictedKeepClearArea(@NonNull String name) {
-        mNamedUnrestrictedKeepClearAreas.remove(name);
+    /**
+     * Forcefully set the keep-clear-area for launcher shelf height if applicable.
+     * This is used for entering PiP in button navigation mode to make sure the destination bounds
+     * calculation includes the shelf height, to avoid race conditions that such callback is sent
+     * from Launcher after the entering animation is started.
+     */
+    public void mayUseCachedLauncherShelfHeight() {
+        if (!mCachedLauncherShelfHeightKeepClearArea.isEmpty()) {
+            setNamedUnrestrictedKeepClearArea(
+                    NAMED_KCA_LAUNCHER_SHELF, mCachedLauncherShelfHeightKeepClearArea);
+        }
     }
 
-
     /**
      * @return restricted keep clear areas.
      */
@@ -454,9 +484,12 @@
      */
     @NonNull
     public Set<Rect> getUnrestrictedKeepClearAreas() {
-        if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
+        if (mNamedUnrestrictedKeepClearAreas.size() == 0) return mUnrestrictedKeepClearAreas;
         final Set<Rect> unrestrictedAreas = new ArraySet<>(mUnrestrictedKeepClearAreas);
-        unrestrictedAreas.addAll(mNamedUnrestrictedKeepClearAreas.values());
+        for (int i = 0; i < mNamedUnrestrictedKeepClearAreas.size(); i++) {
+            final int key = mNamedUnrestrictedKeepClearAreas.keyAt(i);
+            unrestrictedAreas.add(mNamedUnrestrictedKeepClearAreas.get(key));
+        }
         return unrestrictedAreas;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 80f6a63..700742a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -534,7 +534,8 @@
             MultiInstanceHelper multiInstanceHelper,
             @ShellMainThread ShellExecutor mainExecutor,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
-            Optional<RecentTasksController> recentTasksController) {
+            Optional<RecentTasksController> recentTasksController,
+            InteractionJankMonitor interactionJankMonitor) {
         return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
                 displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
                 dragAndDropController, transitions, keyguardManager, enterDesktopTransitionHandler,
@@ -542,7 +543,8 @@
                 dragToDesktopTransitionHandler, desktopModeTaskRepository,
                 desktopModeLoggerTransitionObserver, launchAdjacentController,
                 recentsTransitionHandler, multiInstanceHelper,
-                mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null));
+                mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null),
+                interactionJankMonitor);
     }
 
     @WMSingleton
@@ -568,9 +570,10 @@
             Context context,
             Transitions transitions,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            Optional<DesktopTasksLimiter> desktopTasksLimiter) {
+            Optional<DesktopTasksLimiter> desktopTasksLimiter,
+            InteractionJankMonitor interactionJankMonitor) {
         return new DragToDesktopTransitionHandler(context, transitions,
-                rootTaskDisplayAreaOrganizer);
+                rootTaskDisplayAreaOrganizer, interactionJankMonitor);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 1a9c304..037fbb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -206,12 +206,13 @@
     @WMSingleton
     @Provides
     static PipMotionHelper providePipMotionHelper(Context context,
+            @ShellMainThread ShellExecutor mainExecutor,
             PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer,
             PhonePipMenuController menuController, PipSnapAlgorithm pipSnapAlgorithm,
             PipTransitionController pipTransitionController,
             FloatingContentCoordinator floatingContentCoordinator,
             Optional<PipPerfHintController> pipPerfHintControllerOptional) {
-        return new PipMotionHelper(context, pipBoundsState, pipTaskOrganizer,
+        return new PipMotionHelper(context, mainExecutor, pipBoundsState, pipTaskOrganizer,
                 menuController, pipSnapAlgorithm, pipTransitionController,
                 floatingContentCoordinator, pipPerfHintControllerOptional);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index de901b5..9fd2c27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -49,6 +49,9 @@
 import android.window.WindowContainerTransaction
 import androidx.annotation.BinderThread
 import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.internal.protolog.ProtoLog
 import com.android.window.flags.Flags
@@ -123,7 +126,8 @@
     private val multiInstanceHelper: MultiInstanceHelper,
     @ShellMainThread private val mainExecutor: ShellExecutor,
     private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
-    private val recentTasksController: RecentTasksController?
+    private val recentTasksController: RecentTasksController?,
+    private val interactionJankMonitor: InteractionJankMonitor
 ) :
     RemoteCallable<DesktopTasksController>,
     Transitions.TransitionHandler,
@@ -378,12 +382,15 @@
     fun startDragToDesktop(
         taskInfo: RunningTaskInfo,
         dragToDesktopValueAnimator: MoveToDesktopAnimator,
+        taskSurface: SurfaceControl,
     ) {
         ProtoLog.v(
             WM_SHELL_DESKTOP_MODE,
             "DesktopTasksController: startDragToDesktop taskId=%d",
             taskInfo.taskId
         )
+        interactionJankMonitor.begin(taskSurface, context,
+            CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
         dragToDesktopTransitionHandler.startDragToDesktopTransition(
             taskInfo.taskId,
             dragToDesktopValueAnimator
@@ -1340,13 +1347,19 @@
     fun onDragPositioningEndThroughStatusBar(
         inputCoordinates: PointF,
         taskInfo: RunningTaskInfo,
+        taskSurface: SurfaceControl,
     ): IndicatorType {
+        // End the drag_hold CUJ interaction.
+        interactionJankMonitor.end(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
         val indicator = getVisualIndicator() ?: return IndicatorType.NO_INDICATOR
         val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
         when (indicatorType) {
             IndicatorType.TO_DESKTOP_INDICATOR -> {
                 val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
                     ?: return IndicatorType.NO_INDICATOR
+                // Start a new jank interaction for the drag release to desktop window animation.
+                interactionJankMonitor.begin(taskSurface, context,
+                    CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, "to_desktop")
                 if (Flags.enableWindowingDynamicInitialBounds()) {
                     finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo))
                 } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index ddee8fa..9e79eddb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -30,6 +30,9 @@
 import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
 import com.android.internal.protolog.ProtoLog
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
 import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
@@ -57,17 +60,20 @@
     private val context: Context,
     private val transitions: Transitions,
     private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
-    private val transactionSupplier: Supplier<SurfaceControl.Transaction>
+    private val interactionJankMonitor: InteractionJankMonitor,
+    private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
 ) : TransitionHandler {
 
     constructor(
         context: Context,
         transitions: Transitions,
-        rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+        rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+        interactionJankMonitor: InteractionJankMonitor
     ) : this(
         context,
         transitions,
         rootTaskDisplayAreaOrganizer,
+        interactionJankMonitor,
         Supplier { SurfaceControl.Transaction() }
     )
 
@@ -567,6 +573,8 @@
                                 onTaskResizeAnimationListener.onAnimationEnd(state.draggedTaskId)
                                 startTransitionFinishCb.onTransitionFinished(null /* null */)
                                 clearState()
+                                interactionJankMonitor.end(
+                                    CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
                             }
                         }
                     )
@@ -604,6 +612,10 @@
                 "DragToDesktop: onTransitionConsumed() start transition aborted"
             )
             state.startAborted = true
+            // Cancel CUJ interaction if the transition is aborted.
+            interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
+        } else if (state.cancelTransitionToken != transition) {
+            interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index a749019..b27c428 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -16,10 +16,12 @@
 
 package com.android.wm.shell.pip;
 
+import android.annotation.NonNull;
 import android.graphics.Rect;
 
 import com.android.wm.shell.shared.annotations.ExternalThread;
 
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -69,9 +71,10 @@
     default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
 
     /**
-     * @return {@link PipTransitionController} instance.
+     * Register {@link PipTransitionController.PipTransitionCallback} to listen on PiP transition
+     * started / finished callbacks.
      */
-    default PipTransitionController getPipTransitionController() {
-        return null;
-    }
+    default void registerPipTransitionCallback(
+            @NonNull PipTransitionController.PipTransitionCallback callback,
+            @NonNull Executor executor) { }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index a8346a9..852382d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -230,6 +230,7 @@
 
     /**
      * Quietly cancel the animator by removing the listeners first.
+     * TODO(b/275003573): deprecate this, cancelling without the proper callbacks is problematic.
      */
     static void quietCancel(@NonNull ValueAnimator animator) {
         animator.removeAllUpdateListeners();
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 8d63ff2..723a531 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
@@ -423,7 +423,8 @@
             });
             mPipTransitionController.setPipOrganizer(this);
             displayController.addDisplayWindowListener(this);
-            pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
+            pipTransitionController.registerPipTransitionCallback(
+                    mPipTransitionCallback, mMainExecutor);
         }
     }
 
@@ -495,7 +496,9 @@
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "startSwipePipToHome: %s, state=%s", componentName, mPipTransitionState);
         mPipTransitionState.setInSwipePipToHomeTransition(true);
-        sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
+        if (!ENABLE_SHELL_TRANSITIONS) {
+            sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
+        }
         setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
         return mPipBoundsAlgorithm.getEntryDestinationBounds();
     }
@@ -2023,7 +2026,7 @@
             removeContentOverlay(mPipOverlay, null /* callback */);
         }
         if (animator != null) {
-            PipAnimationController.quietCancel(animator);
+            animator.cancel();
             mPipAnimationController.resetAnimatorState();
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 1a32018..a52141c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1020,6 +1020,9 @@
             mPipMenuController.attach(leash);
         }
 
+        // Make sure we have the launcher shelf into destination bounds calculation
+        // before the animator starts.
+        mPipBoundsState.mayUseCachedLauncherShelfHeight();
         final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         final Rect currentBounds = pipChange.getStartAbsBounds();
 
@@ -1173,6 +1176,7 @@
                     .setLayer(swipePipToHomeOverlay, Integer.MAX_VALUE);
         }
 
+        sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
         // Both Shell and Launcher calculate their own "adjusted" source-rect-hint values based on
         // appBounds being source bounds when entering PiP.
         final Rect sourceBounds = swipePipToHomeOverlay == null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index b1dd4f1..fc9e2be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -53,8 +53,9 @@
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
 
 /**
  * Responsible supplying PiP Transitions.
@@ -66,7 +67,7 @@
     protected final ShellTaskOrganizer mShellTaskOrganizer;
     protected final PipMenuController mPipMenuController;
     protected final Transitions mTransitions;
-    private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
+    private final Map<PipTransitionCallback, Executor> mPipTransitionCallbacks = new HashMap<>();
     protected PipTaskOrganizer mPipOrganizer;
     protected DefaultMixedHandler mMixedHandler;
 
@@ -183,16 +184,20 @@
     /**
      * Registers {@link PipTransitionCallback} to receive transition callbacks.
      */
-    public void registerPipTransitionCallback(PipTransitionCallback callback) {
-        mPipTransitionCallbacks.add(callback);
+    public void registerPipTransitionCallback(
+            @NonNull PipTransitionCallback callback, @NonNull Executor executor) {
+        mPipTransitionCallbacks.put(callback, executor);
     }
 
     protected void sendOnPipTransitionStarted(
             @PipAnimationController.TransitionDirection int direction) {
         final Rect pipBounds = mPipBoundsState.getBounds();
-        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
-            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
-            callback.onPipTransitionStarted(direction, pipBounds);
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "sendOnPipTransitionStarted direction=%d, bounds=%s", direction, pipBounds);
+        for (Map.Entry<PipTransitionCallback, Executor> entry
+                : mPipTransitionCallbacks.entrySet()) {
+            entry.getValue().execute(
+                    () -> entry.getKey().onPipTransitionStarted(direction, pipBounds));
         }
         if (isInPipDirection(direction) && Flags.enablePipUiStateCallbackOnEntering()) {
             try {
@@ -209,9 +214,12 @@
 
     protected void sendOnPipTransitionFinished(
             @PipAnimationController.TransitionDirection int direction) {
-        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
-            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
-            callback.onPipTransitionFinished(direction);
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "sendOnPipTransitionFinished direction=%d", direction);
+        for (Map.Entry<PipTransitionCallback, Executor> entry
+                : mPipTransitionCallbacks.entrySet()) {
+            entry.getValue().execute(
+                    () -> entry.getKey().onPipTransitionFinished(direction));
         }
         if (isInPipDirection(direction) && Flags.enablePipUiStateCallbackOnEntering()) {
             try {
@@ -228,9 +236,12 @@
 
     protected void sendOnPipTransitionCancelled(
             @PipAnimationController.TransitionDirection int direction) {
-        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
-            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
-            callback.onPipTransitionCanceled(direction);
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "sendOnPipTransitionCancelled direction=%d", direction);
+        for (Map.Entry<PipTransitionCallback, Executor> entry
+                : mPipTransitionCallbacks.entrySet()) {
+            entry.getValue().execute(
+                    () -> entry.getKey().onPipTransitionCanceled(direction));
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 26b7e58..7451d22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -106,6 +106,7 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -478,7 +479,7 @@
         mShellCommandHandler.addDumpCallback(this::dump, this);
         mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
                 INPUT_CONSUMER_PIP, mMainExecutor);
-        mPipTransitionController.registerPipTransitionCallback(this);
+        mPipTransitionController.registerPipTransitionCallback(this, mMainExecutor);
         mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
             mPipDisplayLayoutState.setDisplayId(displayId);
             onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
@@ -645,9 +646,9 @@
                 });
 
         mTabletopModeController.registerOnTabletopModeChangedListener((isInTabletopMode) -> {
-            final String tag = "tabletop-mode";
             if (!isInTabletopMode) {
-                mPipBoundsState.removeNamedUnrestrictedKeepClearArea(tag);
+                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, null);
                 return;
             }
 
@@ -656,14 +657,16 @@
             if (mTabletopModeController.getPreferredHalfInTabletopMode()
                     == TabletopModeController.PREFERRED_TABLETOP_HALF_TOP) {
                 // Prefer top, avoid the bottom half of the display.
-                mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
-                        displayBounds.left, displayBounds.centerY(),
-                        displayBounds.right, displayBounds.bottom));
+                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, new Rect(
+                                displayBounds.left, displayBounds.centerY(),
+                                displayBounds.right, displayBounds.bottom));
             } else {
                 // Prefer bottom, avoid the top half of the display.
-                mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
-                        displayBounds.left, displayBounds.top,
-                        displayBounds.right, displayBounds.centerY()));
+                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, new Rect(
+                                displayBounds.left, displayBounds.top,
+                                displayBounds.right, displayBounds.centerY()));
             }
 
             // Try to move the PiP window if we have entered PiP mode.
@@ -915,10 +918,12 @@
                     0, mPipBoundsState.getDisplayBounds().bottom - height,
                     mPipBoundsState.getDisplayBounds().right,
                     mPipBoundsState.getDisplayBounds().bottom);
-            mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, rect);
+            mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                    PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, rect);
             updatePipPositionForKeepClearAreas();
         } else {
-            mPipBoundsState.removeNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG);
+            mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                    PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, null);
             // postpone moving in response to hide of Launcher in case there's another change
             mMainExecutor.removeCallbacks(mMovePipInResponseToKeepClearAreasChangeCallback);
             mMainExecutor.executeDelayed(
@@ -967,8 +972,8 @@
             int launcherRotation, Rect hotseatKeepClearArea) {
         // preemptively add the keep clear area for Hotseat, so that it is taken into account
         // when calculating the entry destination bounds of PiP window
-        mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG,
-                hotseatKeepClearArea);
+        mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, hotseatKeepClearArea);
         onDisplayRotationChangedNotInPip(mContext, launcherRotation);
         // cache current min/max size
         Point minSize = mPipBoundsState.getMinSize();
@@ -1220,8 +1225,11 @@
         }
 
         @Override
-        public PipTransitionController getPipTransitionController() {
-            return mPipTransitionController;
+        public void registerPipTransitionCallback(
+                PipTransitionController.PipTransitionCallback callback,
+                Executor executor) {
+            mMainExecutor.execute(() -> mPipTransitionController.registerPipTransitionCallback(
+                    callback, executor));
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index e8d6576..df3803d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -38,6 +38,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.FloatProperties;
 import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipBoundsState;
@@ -47,6 +48,7 @@
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
 
 import kotlin.Unit;
 import kotlin.jvm.functions.Function0;
@@ -171,7 +173,9 @@
         public void onPipTransitionCanceled(int direction) {}
     };
 
-    public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
+    public PipMotionHelper(Context context,
+            @ShellMainThread ShellExecutor mainExecutor,
+            @NonNull PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController,
             PipSnapAlgorithm snapAlgorithm, PipTransitionController pipTransitionController,
             FloatingContentCoordinator floatingContentCoordinator,
@@ -183,7 +187,7 @@
         mSnapAlgorithm = snapAlgorithm;
         mFloatingContentCoordinator = floatingContentCoordinator;
         mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
-        pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
+        pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback, mainExecutor);
         mResizePipUpdateListener = (target, values) -> {
             if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
                 mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 62c0944..0ed5079 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -257,7 +257,7 @@
     }
 
     private void onInit() {
-        mPipTransitionController.registerPipTransitionCallback(this);
+        mPipTransitionController.registerPipTransitionCallback(this, mMainExecutor);
 
         reloadResources();
 
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 b3dab85..48d17ec6 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
@@ -68,6 +68,7 @@
     DismissSession mPendingDismiss = null;
     EnterSession mPendingEnter = null;
     TransitSession mPendingResize = null;
+    TransitSession mPendingRemotePassthrough = null;
 
     private IBinder mAnimatingTransition = null;
     private OneShotRemoteHandler mActiveRemoteHandler = null;
@@ -320,6 +321,11 @@
         return mPendingResize != null && mPendingResize.mTransition == transition;
     }
 
+    boolean isPendingPassThrough(IBinder transition) {
+        return mPendingRemotePassthrough != null &&
+                mPendingRemotePassthrough.mTransition == transition;
+    }
+
     @Nullable
     private TransitSession getPendingTransition(IBinder transition) {
         if (isPendingEnter(transition)) {
@@ -331,6 +337,9 @@
         } else if (isPendingResize(transition)) {
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
             return mPendingResize;
+        } else if (isPendingPassThrough(transition)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved passThrough transition");
+            return mPendingRemotePassthrough;
         }
         return null;
     }
@@ -378,6 +387,19 @@
                 extraTransitType, resizeAnim);
     }
 
+    /** Sets a transition to enter split. */
+    void setRemotePassThroughTransition(@NonNull IBinder transition,
+            @Nullable RemoteTransition remoteTransition) {
+        mPendingRemotePassthrough = new TransitSession(
+                transition, null, null,
+                remoteTransition, Transitions.TRANSIT_SPLIT_PASSTHROUGH);
+
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "  splitTransition "
+                + " deduced remote passthrough split screen");
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setRemotePassThrough: transitType=%d remote=%s",
+                Transitions.TRANSIT_SPLIT_PASSTHROUGH, remoteTransition);
+    }
+
     /** Starts a transition to dismiss split. */
     IBinder startDismissTransition(WindowContainerTransaction wct,
             Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
@@ -474,6 +496,12 @@
             mPendingResize.onConsumed(aborted);
             mPendingResize = null;
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
+        } else if (isPendingPassThrough(transition)) {
+            mPendingRemotePassthrough.onConsumed(aborted);
+            mPendingRemotePassthrough.mRemoteHandler.onTransitionConsumed(transition, aborted,
+                    finishT);
+            mPendingRemotePassthrough = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for passThrough transition");
         }
 
         // TODO: handle transition consumed for active remote handler
@@ -495,6 +523,10 @@
             mPendingResize.onFinished(wct, mFinishTransaction);
             mPendingResize = null;
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
+        } else if (isPendingPassThrough(mAnimatingTransition)) {
+            mPendingRemotePassthrough.onFinished(wct, mFinishTransaction);
+            mPendingRemotePassthrough = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for passThrough transition");
         }
 
         mActiveRemoteHandler = null;
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 a4f32c4..d7ee563 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
@@ -2710,7 +2710,7 @@
             @Nullable TransitionRequestInfo request) {
         final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
         if (triggerTask == null) {
-            if (isSplitScreenVisible()) {
+            if (isSplitActive()) {
                 ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation",
                         request.getDebugId());
                 // Check if the display is rotating.
@@ -2720,6 +2720,10 @@
                         && displayChange.getStartRotation() != displayChange.getEndRotation()) {
                     mSplitLayout.setFreezeDividerWindow(true);
                 }
+                if (request.getRemoteTransition() != null) {
+                    mSplitTransitions.setRemotePassThroughTransition(transition,
+                            request.getRemoteTransition());
+                }
                 // Still want to monitor everything while in split-screen, so return non-null.
                 return new WindowContainerTransaction();
             } else {
@@ -3046,6 +3050,13 @@
                 notifySplitAnimationFinished();
                 return true;
             }
+        } else if (mSplitTransitions.isPendingPassThrough(transition)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                    "startAnimation: passThrough transition=%d", info.getDebugId());
+            mSplitTransitions.mPendingRemotePassthrough.mRemoteHandler.startAnimation(transition,
+                    info, startTransaction, finishTransaction, finishCallback);
+            notifySplitAnimationFinished();
+            return true;
         }
 
         return startPendingAnimation(transition, info, startTransaction, finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index fc8b1d2..874cca5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -190,6 +190,9 @@
             // TRANSIT_FIRST_CUSTOM + 17
             TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_DRAG_RESIZE;
 
+    /** Remote Transition that split accepts but ultimately needs to be animated by the remote. */
+    public static final int TRANSIT_SPLIT_PASSTHROUGH = TRANSIT_FIRST_CUSTOM + 18;
+
     /** Transition type for desktop mode transitions. */
     public static final int TRANSIT_DESKTOP_MODE_TYPES =
             WindowManager.TRANSIT_FIRST_CUSTOM + 100;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 8312aef..0e8fd7c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -970,7 +970,9 @@
                         relevantDecor.updateHoverAndPressStatus(ev);
                         DesktopModeVisualIndicator.IndicatorType resultType =
                                 mDesktopTasksController.onDragPositioningEndThroughStatusBar(
-                                new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo);
+                                        new PointF(ev.getRawX(), ev.getRawY()),
+                                        relevantDecor.mTaskInfo,
+                                        relevantDecor.mTaskSurface);
                         // If we are entering split select, handle will no longer be visible and
                         // should not be receiving any input.
                         if (resultType == TO_SPLIT_LEFT_INDICATOR
@@ -1010,7 +1012,7 @@
                                     mContext, mDragToDesktopAnimationStartBounds,
                                     relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
                             mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
-                                    mMoveToDesktopAnimator);
+                                    mMoveToDesktopAnimator, relevantDecor.mTaskSurface);
                         }
                     }
                     if (mMoveToDesktopAnimator != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index da26898..3fd3656 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -19,7 +19,11 @@
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_HOVER_ENTER;
+import static android.view.MotionEvent.ACTION_HOVER_EXIT;
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
 import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
 import android.graphics.PointF;
@@ -43,7 +47,7 @@
     private final PointF mInputDownPoint = new PointF();
     private int mTouchSlop;
     private boolean mIsDragEvent;
-    private int mDragPointerId;
+    private int mDragPointerId = -1;
 
     private boolean mResultOfDownAction;
 
@@ -67,7 +71,7 @@
      *
      * @return the result returned by {@link #mEventHandler}, or the result when
      * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
-    */
+     */
     boolean onMotionEvent(View v, MotionEvent ev) {
         final boolean isTouchScreen =
                 (ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
@@ -86,10 +90,14 @@
                 return mResultOfDownAction;
             }
             case ACTION_MOVE: {
-                if (ev.findPointerIndex(mDragPointerId) == -1) {
-                    mDragPointerId = ev.getPointerId(0);
+                if (mDragPointerId == -1) {
+                    // The primary pointer was lifted, ignore the rest of the gesture.
+                    return mResultOfDownAction;
                 }
                 final int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
+                if (dragPointerIndex == -1) {
+                    throw new IllegalStateException("Failed to find primary pointer!");
+                }
                 if (!mIsDragEvent) {
                     float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
                     float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
@@ -99,22 +107,52 @@
                 }
                 // The event handler should only be notified about 'move' events if a drag has been
                 // detected.
-                if (mIsDragEvent) {
-                    return mEventHandler.handleMotionEvent(v, ev);
-                } else {
+                if (!mIsDragEvent) {
                     return mResultOfDownAction;
                 }
+                return mEventHandler.handleMotionEvent(v,
+                        getSinglePointerEvent(ev, mDragPointerId));
+            }
+            case ACTION_HOVER_ENTER:
+            case ACTION_HOVER_MOVE:
+            case ACTION_HOVER_EXIT: {
+                return mEventHandler.handleMotionEvent(v,
+                        getSinglePointerEvent(ev, mDragPointerId));
+            }
+            case ACTION_POINTER_UP: {
+                if (mDragPointerId == -1) {
+                    // The primary pointer was lifted, ignore the rest of the gesture.
+                    return mResultOfDownAction;
+                }
+                if (mDragPointerId != ev.getPointerId(ev.getActionIndex())) {
+                    // Ignore a secondary pointer being lifted.
+                    return mResultOfDownAction;
+                }
+                // The primary pointer is being lifted.
+                final int dragPointerId = mDragPointerId;
+                mDragPointerId = -1;
+                return mEventHandler.handleMotionEvent(v, getSinglePointerEvent(ev, dragPointerId));
             }
             case ACTION_UP:
             case ACTION_CANCEL: {
+                final int dragPointerId = mDragPointerId;
                 resetState();
-                return mEventHandler.handleMotionEvent(v, ev);
+                if (dragPointerId == -1) {
+                    // The primary pointer was lifted, ignore the rest of the gesture.
+                    return mResultOfDownAction;
+                }
+                return mEventHandler.handleMotionEvent(v, getSinglePointerEvent(ev, dragPointerId));
             }
             default:
-                return mEventHandler.handleMotionEvent(v, ev);
+                // Ignore other events.
+                return mResultOfDownAction;
         }
     }
 
+    private static MotionEvent getSinglePointerEvent(MotionEvent ev, int pointerId) {
+        return ev.getPointerCount() > 1 ? ev.split(1 << pointerId) : ev;
+    }
+
     void setTouchSlop(int touchSlop) {
         mTouchSlop = touchSlop;
     }
@@ -129,4 +167,4 @@
     interface MotionEventHandler {
         boolean handleMotionEvent(@Nullable View v, MotionEvent ev);
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 8421365..37510ef4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -66,6 +66,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.ExtendedMockito.never
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.window.flags.Flags
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
 import com.android.wm.shell.MockToken
@@ -166,6 +167,9 @@
   @Mock lateinit var desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver
   @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
   @Mock lateinit var recentTasksController: RecentTasksController
+  @Mock
+  private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+  @Mock private lateinit var mockSurface: SurfaceControl
 
   private lateinit var mockitoSession: StaticMockitoSession
   private lateinit var controller: DesktopTasksController
@@ -248,7 +252,8 @@
         multiInstanceHelper,
         shellExecutor,
         Optional.of(desktopTasksLimiter),
-        recentTasksController)
+        recentTasksController,
+        mockInteractionJankMonitor)
   }
 
   @After
@@ -2016,7 +2021,7 @@
     val task = setUpFullscreenTask()
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
@@ -2032,7 +2037,7 @@
     val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
@@ -2049,7 +2054,7 @@
         setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
   }
@@ -2066,7 +2071,7 @@
         setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
@@ -2086,7 +2091,7 @@
             shouldLetterbox = true)
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
   }
@@ -2102,7 +2107,7 @@
     val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
@@ -2121,7 +2126,7 @@
             screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
@@ -2141,7 +2146,7 @@
             shouldLetterbox = true)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
   }
@@ -2161,7 +2166,7 @@
             screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
@@ -2182,7 +2187,7 @@
             shouldLetterbox = true)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
   }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index bbf523b..e4e2bd2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -15,6 +15,7 @@
 import android.window.TransitionInfo.FLAG_IS_WALLPAPER
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -51,6 +52,8 @@
     @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
     @Mock private lateinit var splitScreenController: SplitScreenController
     @Mock private lateinit var dragAnimator: MoveToDesktopAnimator
+    @Mock
+    private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
 
     private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
 
@@ -63,7 +66,8 @@
                     context,
                     transitions,
                     taskDisplayAreaOrganizer,
-                    transactionSupplier
+                    mockInteractionJankMonitor,
+                    transactionSupplier,
                 )
                 .apply { setSplitScreenController(splitScreenController) }
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 6888de5..75d2145 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -182,7 +182,7 @@
 
     @Test
     public void instantiatePipController_registersPipTransitionCallback() {
-        verify(mMockPipTransitionController).registerPipTransitionCallback(any());
+        verify(mMockPipTransitionController).registerPipTransitionCallback(any(), any());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index ace09a8..66f8c0b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -114,8 +114,8 @@
         final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
                 mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm, mPipDisplayLayoutState,
                 mSizeSpecSource);
-        final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
-                mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
+        final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mMainExecutor,
+                mPipBoundsState, mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
                 mMockPipTransitionController, mFloatingContentCoordinator,
                 Optional.empty() /* pipPerfHintControllerOptional */);
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 92762fa..6d18e36 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -116,8 +116,8 @@
         mPipSnapAlgorithm = new PipSnapAlgorithm();
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
                 new PipKeepClearAlgorithmInterface() {}, mPipDisplayLayoutState, mSizeSpecSource);
-        PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
-                mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
+        PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mMainExecutor,
+                mPipBoundsState, mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
                 mMockPipTransitionController, mFloatingContentCoordinator,
                 Optional.empty() /* pipPerfHintControllerOptional */);
         mPipTouchHandler = new PipTouchHandler(mContext, mShellInit, mPhonePipMenuController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
index b1d62f4..dd19d76 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
@@ -184,108 +184,6 @@
   }
 
   @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_noSystemProperty_overrideOn_featureFlagOff_returnsTrueAndStoresPropertyOn() {
-    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
-    setOverride(OVERRIDE_ON.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-    // Store System Property if not present
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_ON.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_noSystemProperty_overrideUnset_featureFlagOn_returnsTrueAndStoresPropertyUnset() {
-    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
-    setOverride(OVERRIDE_UNSET.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-    // Store System Property if not present
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_UNSET.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_noSystemProperty_overrideUnset_featureFlagOff_returnsFalseAndStoresPropertyUnset() {
-    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
-    setOverride(OVERRIDE_UNSET.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-    // Store System Property if not present
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_UNSET.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @Suppress("ktlint:standard:max-line-length")
-  fun isEnabled_systemPropertyNotInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc")
-    setOverride(OVERRIDE_OFF.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-    // Store System Property if currently invalid
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_OFF.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @Suppress("ktlint:standard:max-line-length")
-  fun isEnabled_systemPropertyInvalidInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2")
-    setOverride(OVERRIDE_OFF.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-    // Store System Property if currently invalid
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_OFF.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_systemPropertyOff_overrideOn_featureFlagOn_returnsFalseAndDoesNotUpdateProperty() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_OFF.setting.toString())
-    setOverride(OVERRIDE_ON.setting)
-
-    // Have a consistent override until reboot
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_OFF.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_systemPropertyOn_overrideOff_featureFlagOff_returnsTrueAndDoesNotUpdateProperty() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_ON.setting.toString())
-    setOverride(OVERRIDE_OFF.setting)
-
-    // Have a consistent override until reboot
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_ON.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @Suppress("ktlint:standard:max-line-length")
-  fun isEnabled_systemPropertyUnset_overrideOff_featureFlagOn_returnsTrueAndDoesNotUpdateProperty() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_UNSET.setting.toString())
-    setOverride(OVERRIDE_OFF.setting)
-
-    // Have a consistent override until reboot
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_UNSET.setting.toString())
-  }
-
-  @Test
   @EnableFlags(
       FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
       FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
@@ -445,12 +343,5 @@
       DesktopModeFlags::class.java.getDeclaredField("cachedToggleOverride")
     cachedToggleOverride.isAccessible = true
     cachedToggleOverride.set(null, null)
-
-    // Clear override cache stored in System property
-    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
-  }
-
-  private companion object {
-    const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
   }
 }
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 37ef788..22b408c 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
@@ -51,8 +51,10 @@
 import android.app.ActivityManager;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.window.IRemoteTransition;
 import android.window.RemoteTransition;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
@@ -328,6 +330,32 @@
 
     @Test
     @UiThreadTest
+    public void testRemotePassThroughInvoked() throws RemoteException {
+        RemoteTransition remoteWrapper = mock(RemoteTransition.class);
+        IRemoteTransition remoteTransition = mock(IRemoteTransition.class);
+        IBinder remoteBinder = mock(IBinder.class);
+        doReturn(remoteBinder).when(remoteTransition).asBinder();
+        doReturn(remoteTransition).when(remoteWrapper).getRemoteTransition();
+
+        TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_CHANGE, null,
+                remoteWrapper);
+        IBinder transition = mock(IBinder.class);
+        mMainStage.activate(new WindowContainerTransaction(), false);
+        mStageCoordinator.handleRequest(transition, request);
+        TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+                .build();
+        boolean accepted = mStageCoordinator.startAnimation(transition, info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(Transitions.TransitionFinishCallback.class));
+        assertTrue(accepted);
+
+        verify(remoteTransition, times(1)).startAnimation(any(),
+                any(), any(), any());
+    }
+
+    @Test
+    @UiThreadTest
     public void testEnterRecentsAndRestore() {
         enterSplit();
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
index 3fbab0f..56224b4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -85,6 +85,23 @@
     }
 
     @Test
+    fun testNoMove_mouse_passesDownAndUp() {
+        assertTrue(dragDetector.onMotionEvent(
+            createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        assertTrue(dragDetector.onMotionEvent(
+            createMotionEvent(MotionEvent.ACTION_UP, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+    }
+
+    @Test
     fun testMoveInSlop_touch_passesDownAndUp() {
         `when`(eventHandler.handleMotionEvent(any(), argThat {
             return@argThat it.action == MotionEvent.ACTION_DOWN
@@ -166,6 +183,52 @@
     }
 
     @Test
+    fun testDownMoveDown_shouldIgnoreTheSecondDownMotion() {
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        val newX = X + SLOP + 1
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+    }
+
+    @Test
+    fun testDownMouseMoveDownTouch_shouldIgnoreTheTouchDownMotion() {
+        assertTrue(dragDetector.onMotionEvent(
+            createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        val newX = X + SLOP + 1
+        assertTrue(dragDetector.onMotionEvent(
+            createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+    }
+
+    @Test
     fun testPassesHoverEnter() {
         `when`(eventHandler.handleMotionEvent(any(), argThat {
             it.action == MotionEvent.ACTION_HOVER_ENTER
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index ad963dd..93118aea 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -40,6 +40,7 @@
 namespace android {
 namespace uirenderer {
 
+std::mutex TestUtils::sMutex;
 std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{};
 
 SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 0ede902..8ab2b16 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -305,22 +305,26 @@
                 .onSync =
                         [](int functor, void* client_data, const WebViewSyncData& data) {
                             expectOnRenderThread("onSync");
+                            std::scoped_lock lock(sMutex);
                             sMockFunctorCounts[functor].sync++;
                         },
                 .onContextDestroyed =
                         [](int functor, void* client_data) {
                             expectOnRenderThread("onContextDestroyed");
+                            std::scoped_lock lock(sMutex);
                             sMockFunctorCounts[functor].contextDestroyed++;
                         },
                 .onDestroyed =
                         [](int functor, void* client_data) {
                             expectOnRenderThread("onDestroyed");
+                            std::scoped_lock lock(sMutex);
                             sMockFunctorCounts[functor].destroyed++;
                         },
                 .removeOverlays =
                         [](int functor, void* data,
                            void (*mergeTransaction)(ASurfaceTransaction*)) {
                             expectOnRenderThread("removeOverlays");
+                            std::scoped_lock lock(sMutex);
                             sMockFunctorCounts[functor].removeOverlays++;
                         },
         };
@@ -329,6 +333,7 @@
                 callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params,
                                          const WebViewOverlayData& overlay_params) {
                     expectOnRenderThread("draw");
+                    std::scoped_lock lock(sMutex);
                     sMockFunctorCounts[functor].glesDraw++;
                 };
                 break;
@@ -336,15 +341,18 @@
                 callbacks.vk.initialize = [](int functor, void* data,
                                              const VkFunctorInitParams& params) {
                     expectOnRenderThread("initialize");
+                    std::scoped_lock lock(sMutex);
                     sMockFunctorCounts[functor].vkInitialize++;
                 };
                 callbacks.vk.draw = [](int functor, void* data, const VkFunctorDrawParams& params,
                                        const WebViewOverlayData& overlayParams) {
                     expectOnRenderThread("draw");
+                    std::scoped_lock lock(sMutex);
                     sMockFunctorCounts[functor].vkDraw++;
                 };
                 callbacks.vk.postDraw = [](int functor, void* data) {
                     expectOnRenderThread("postDraw");
+                    std::scoped_lock lock(sMutex);
                     sMockFunctorCounts[functor].vkPostDraw++;
                 };
                 break;
@@ -352,11 +360,16 @@
         return callbacks;
     }
 
-    static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
+    static CallCounts copyCountsForFunctor(int functor) {
+        std::scoped_lock lock(sMutex);
+        return sMockFunctorCounts[functor];
+    }
 
     static SkFont defaultFont();
 
 private:
+    // guards sMockFunctorCounts
+    static std::mutex sMutex;
     static std::unordered_map<int, CallCounts> sMockFunctorCounts;
 
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index e727ea8..690a60a4 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -239,19 +239,21 @@
     TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
         TestUtils::syncHierarchyPropertiesAndDisplayList(node);
     });
-    auto& counts = TestUtils::countsForFunctor(functor);
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
     EXPECT_EQ(0, counts.destroyed);
 
     TestUtils::recordNode(*node, [&](Canvas& canvas) {
         canvas.drawWebViewFunctor(functor);
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
     EXPECT_EQ(0, counts.destroyed);
 
     TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
         TestUtils::syncHierarchyPropertiesAndDisplayList(node);
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(0, counts.destroyed);
 
@@ -265,6 +267,7 @@
     });
     // Fence on any remaining post'd work
     TestUtils::runOnRenderThreadUnmanaged([] (RenderThread&) {});
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(1, counts.destroyed);
 }
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 064d42e..26b4729 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -101,7 +101,7 @@
     SkCanvas dummyCanvas;
 
     int functor1 = TestUtils::createMockFunctor();
-    auto& counts = TestUtils::countsForFunctor(functor1);
+    auto counts = TestUtils::copyCountsForFunctor(functor1);
     skiaDL.mChildFunctors.push_back(
             skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas));
     WebViewFunctor_release(functor1);
@@ -118,6 +118,7 @@
         });
     });
 
+    counts = TestUtils::copyCountsForFunctor(functor1);
     EXPECT_EQ(counts.sync, 1);
     EXPECT_EQ(counts.destroyed, 0);
     EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
@@ -126,6 +127,7 @@
     TestUtils::runOnRenderThread([](auto&) {
         // Fence
     });
+    counts = TestUtils::copyCountsForFunctor(functor1);
     EXPECT_EQ(counts.destroyed, 1);
 }
 
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
index 5e8f13d..09ce98a 100644
--- a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -40,7 +40,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
         // Empty, don't care
     });
-    auto& counts = TestUtils::countsForFunctor(functor);
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     // We never initialized, so contextDestroyed == 0
     EXPECT_EQ(0, counts.contextDestroyed);
     EXPECT_EQ(1, counts.destroyed);
@@ -59,7 +59,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
         // fence
     });
-    auto& counts = TestUtils::countsForFunctor(functor);
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(0, counts.sync);
     EXPECT_EQ(0, counts.contextDestroyed);
     EXPECT_EQ(0, counts.destroyed);
@@ -69,6 +69,7 @@
         handle->sync(syncData);
     });
 
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
 
     TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
@@ -76,6 +77,7 @@
         handle->sync(syncData);
     });
 
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
 
     handle.clear();
@@ -84,6 +86,7 @@
         // fence
     });
 
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(0, counts.contextDestroyed);
     EXPECT_EQ(1, counts.destroyed);
@@ -98,7 +101,6 @@
     auto handle = WebViewFunctorManager::instance().handleFor(functor);
     ASSERT_TRUE(handle);
     WebViewFunctor_release(functor);
-    auto& counts = TestUtils::countsForFunctor(functor);
     for (int i = 0; i < 5; i++) {
         TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
             WebViewSyncData syncData;
@@ -112,6 +114,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
         // fence
     });
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(5, counts.sync);
     EXPECT_EQ(10, counts.glesDraw);
     EXPECT_EQ(1, counts.contextDestroyed);
@@ -127,13 +130,13 @@
     auto handle = WebViewFunctorManager::instance().handleFor(functor);
     ASSERT_TRUE(handle);
     WebViewFunctor_release(functor);
-    auto& counts = TestUtils::countsForFunctor(functor);
     TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
         WebViewSyncData syncData;
         handle->sync(syncData);
         DrawGlInfo drawInfo;
         handle->drawGl(drawInfo);
     });
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
     EXPECT_EQ(1, counts.glesDraw);
     EXPECT_EQ(0, counts.contextDestroyed);
@@ -141,6 +144,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](auto& rt) {
         rt.destroyRenderingContext();
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
     EXPECT_EQ(1, counts.glesDraw);
     EXPECT_EQ(1, counts.contextDestroyed);
@@ -151,6 +155,7 @@
         DrawGlInfo drawInfo;
         handle->drawGl(drawInfo);
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(2, counts.glesDraw);
     EXPECT_EQ(1, counts.contextDestroyed);
@@ -159,6 +164,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
         // fence
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(2, counts.glesDraw);
     EXPECT_EQ(2, counts.contextDestroyed);
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index d6345ce..f36344a 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
 
 allprojects {
     extra["androidTop"] = androidTop
-    extra["jetpackComposeVersion"] = "1.7.0-beta02"
+    extra["jetpackComposeVersion"] = "1.7.0-beta05"
 }
 
 subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index a842009..1cca73a 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
 #
 
 [versions]
-agp = "8.5.0"
+agp = "8.5.1"
 compose-compiler = "1.5.11"
 dexmaker-mockito = "2.28.3"
 jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
deleted file mode 100644
index 77e6ad3..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
new file mode 100644
index 0000000..9a97e46
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index e644113..2c35211 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 91d2a3a..9f29c77 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
 
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=gradle-8.8-bin.zip
+distributionUrl=gradle-8.9-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
index b740cf1..f5feea6 100755
--- a/packages/SettingsLib/Spa/gradlew
+++ b/packages/SettingsLib/Spa/gradlew
@@ -15,6 +15,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+# SPDX-License-Identifier: Apache-2.0
+#
 
 ##############################################################################
 #
@@ -84,7 +86,8 @@
 # shellcheck disable=SC2034
 APP_BASE_NAME=${0##*/}
 # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 492d7c0..ce3d96e 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -53,14 +53,14 @@
 
 dependencies {
     api(project(":SettingsLibColor"))
-    api("androidx.appcompat:appcompat:1.7.0-rc01")
-    api("androidx.compose.material3:material3:1.3.0-beta02")
+    api("androidx.appcompat:appcompat:1.7.0")
+    api("androidx.compose.material3:material3:1.3.0-beta04")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.8.0-beta02")
+    api("androidx.navigation:navigation-compose:2.8.0-beta05")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
     api("com.google.android.material:material:1.11.0")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 5f23651..2b8b23e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -282,5 +282,6 @@
         Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
         Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
         Settings.Secure.MANDATORY_BIOMETRICS,
+        Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index c8da8af..cc5302b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -441,5 +441,7 @@
         VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.MANDATORY_BIOMETRICS, new InclusiveIntegerRangeValidator(0, 1));
+        VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+                new InclusiveIntegerRangeValidator(0, 1));
     }
 }
diff --git a/packages/SystemUI/aconfig/communal.aconfig b/packages/SystemUI/aconfig/communal.aconfig
index afcd8a9..f2b5efa 100644
--- a/packages/SystemUI/aconfig/communal.aconfig
+++ b/packages/SystemUI/aconfig/communal.aconfig
@@ -8,12 +8,3 @@
     bug: "304584416"
 }
 
-flag {
-    name: "enable_widget_picker_size_filter"
-    namespace: "communal"
-    description: "Enables passing a size filter to the widget picker"
-    bug: "345482907"
-   metadata {
-        purpose: PURPOSE_BUGFIX
-   }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 1c02d3f..68e968f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -1004,6 +1004,7 @@
                 }
                 .thenIf(viewModel.isEditMode) {
                     Modifier.semantics {
+                        onClick(clickActionLabel, null)
                         contentDescription = accessibilityLabel
                         val deleteAction =
                             CustomAccessibilityAction(removeWidgetActionLabel) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
index 620892a..b4c1a2e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
@@ -50,7 +50,6 @@
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
@@ -102,8 +101,6 @@
         val interactionSource = remember { MutableInteractionSource() }
         val focusRequester = remember { FocusRequester() }
 
-        val context = LocalContext.current
-
         LaunchedEffect(Unit) {
             // Adding a delay to ensure the animation completes before requesting focus
             delay(250)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 665be53..5b328b8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -52,11 +52,19 @@
  * and [onStop] methods.
  */
 internal interface DragController {
-    /** Drag the current scene by [delta] pixels. */
-    fun onDrag(delta: Float)
+    /**
+     * Drag the current scene by [delta] pixels.
+     *
+     * @return the consumed [delta]
+     */
+    fun onDrag(delta: Float): Float
 
-    /** Starts a transition to a target scene. */
-    fun onStop(velocity: Float, canChangeScene: Boolean)
+    /**
+     * Starts a transition to a target scene.
+     *
+     * @return the consumed [velocity]
+     */
+    fun onStop(velocity: Float, canChangeScene: Boolean): Float
 }
 
 internal class DraggableHandlerImpl(
@@ -272,8 +280,10 @@
      *
      * @return the consumed delta
      */
-    override fun onDrag(delta: Float) {
-        if (delta == 0f || !isDrivingTransition || swipeTransition.isFinishing) return
+    override fun onDrag(delta: Float): Float {
+        if (delta == 0f || !isDrivingTransition || swipeTransition.isFinishing) {
+            return 0f
+        }
         swipeTransition.dragOffset += delta
 
         val (fromScene, acceleratedOffset) =
@@ -289,7 +299,7 @@
 
         if (result == null) {
             onStop(velocity = delta, canChangeScene = true)
-            return
+            return 0f
         }
 
         if (
@@ -314,6 +324,8 @@
 
             updateTransition(swipeTransition)
         }
+
+        return delta
     }
 
     /**
@@ -351,10 +363,10 @@
         }
     }
 
-    override fun onStop(velocity: Float, canChangeScene: Boolean) {
+    override fun onStop(velocity: Float, canChangeScene: Boolean): Float {
         // The state was changed since the drag started; don't do anything.
         if (!isDrivingTransition || swipeTransition.isFinishing) {
-            return
+            return 0f
         }
 
         // Important: Make sure that all the code here references the current transition when
@@ -440,7 +452,7 @@
                 if (result == null) {
                     // We will not animate
                     swipeTransition.snapToScene(fromScene.key)
-                    return
+                    return 0f
                 }
 
                 val newSwipeTransition =
@@ -462,6 +474,9 @@
                 animateTo(targetScene = fromScene, targetOffset = 0f)
             }
         }
+
+        // The onStop animation consumes any remaining velocity.
+        return velocity
     }
 
     /**
@@ -1081,17 +1096,13 @@
                 // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
                 // initiated in a nested child.
                 controller.onDrag(delta = offsetAvailable)
-
-                offsetAvailable
             },
             onStop = { velocityAvailable ->
                 val controller = dragController ?: error("Should be called after onStart")
 
-                controller.onStop(velocity = velocityAvailable, canChangeScene = canChangeScene)
-
-                dragController = null
-                // The onDragStopped animation consumes any remaining velocity.
-                velocityAvailable
+                controller
+                    .onStop(velocity = velocityAvailable, canChangeScene = canChangeScene)
+                    .also { dragController = null }
             },
         )
     }
@@ -1106,7 +1117,7 @@
 internal const val OffsetVisibilityThreshold = 0.5f
 
 private object NoOpDragController : DragController {
-    override fun onDrag(delta: Float) {}
+    override fun onDrag(delta: Float) = 0f
 
-    override fun onStop(velocity: Float, canChangeScene: Boolean) {}
+    override fun onStop(velocity: Float, canChangeScene: Boolean) = 0f
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 7a5a84e..c8bbb14 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -212,7 +212,8 @@
             draggableHandler: DraggableHandler,
             startedPosition: Offset = Offset.Zero,
             overSlop: Float = 0f,
-            pointersDown: Int = 1
+            pointersDown: Int = 1,
+            expectedConsumed: Boolean = true,
         ): DragController {
             val dragController =
                 draggableHandler.onDragStarted(
@@ -222,17 +223,23 @@
                 )
 
             // MultiPointerDraggable will always call onDelta with the initial overSlop right after
-            dragController.onDragDelta(pixels = overSlop)
+            dragController.onDragDelta(pixels = overSlop, expectedConsumed = expectedConsumed)
 
             return dragController
         }
 
-        fun DragController.onDragDelta(pixels: Float) {
-            onDrag(delta = pixels)
+        fun DragController.onDragDelta(pixels: Float, expectedConsumed: Boolean = true) {
+            val consumed = onDrag(delta = pixels)
+            assertThat(consumed).isEqualTo(if (expectedConsumed) pixels else 0f)
         }
 
-        fun DragController.onDragStopped(velocity: Float, canChangeScene: Boolean = true) {
-            onStop(velocity, canChangeScene)
+        fun DragController.onDragStopped(
+            velocity: Float,
+            canChangeScene: Boolean = true,
+            expectedConsumed: Boolean = true
+        ) {
+            val consumed = onStop(velocity, canChangeScene)
+            assertThat(consumed).isEqualTo(if (expectedConsumed) velocity else 0f)
         }
 
         fun NestedScrollConnection.scroll(
@@ -360,10 +367,18 @@
 
     @Test
     fun onDragStartedWithoutActionsInBothDirections_stayIdle() = runGestureTest {
-        onDragStarted(horizontalDraggableHandler, overSlop = up(fractionOfScreen = 0.3f))
+        onDragStarted(
+            horizontalDraggableHandler,
+            overSlop = up(fractionOfScreen = 0.3f),
+            expectedConsumed = false,
+        )
         assertIdle(currentScene = SceneA)
 
-        onDragStarted(horizontalDraggableHandler, overSlop = down(fractionOfScreen = 0.3f))
+        onDragStarted(
+            horizontalDraggableHandler,
+            overSlop = down(fractionOfScreen = 0.3f),
+            expectedConsumed = false,
+        )
         assertIdle(currentScene = SceneA)
     }
 
@@ -489,19 +504,19 @@
 
         // start accelaratedScroll and scroll over to B -> null
         val dragController2 = onDragStartedImmediately()
-        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
-        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = false)
+        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = false)
 
         // here onDragStopped is already triggered, but subsequent onDelta/onDragStopped calls may
         // still be called. Make sure that they don't crash or change the scene
-        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = false)
         dragController2.onDragStopped(velocity = 0f)
 
         advanceUntilIdle()
         assertIdle(SceneB)
 
         // These events can still come in after the animation has settled
-        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = false)
         dragController2.onDragStopped(velocity = 0f)
         assertIdle(SceneB)
     }
@@ -845,7 +860,7 @@
         assertThat(progress).isEqualTo(0.2f)
 
         // this should be ignored, we are scrolling now!
-        dragController.onDragStopped(-velocityThreshold)
+        dragController.onDragStopped(-velocityThreshold, expectedConsumed = false)
         assertTransition(currentScene = SceneA)
 
         nestedScroll.scroll(available = -offsetY10)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index ecafb17..b98400a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -49,6 +49,21 @@
 class MultiPointerDraggableTest {
     @get:Rule val rule = createComposeRule()
 
+    private class SimpleDragController(
+        val onDrag: () -> Unit,
+        val onStop: () -> Unit,
+    ) : DragController {
+        override fun onDrag(delta: Float): Float {
+            onDrag()
+            return delta
+        }
+
+        override fun onStop(velocity: Float, canChangeScene: Boolean): Float {
+            onStop()
+            return velocity
+        }
+    }
+
     @Test
     fun cancellingPointerCallsOnDragStopped() {
         val size = 200f
@@ -70,15 +85,10 @@
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    dragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    stopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
                         },
                     )
             )
@@ -142,15 +152,10 @@
                         startDragImmediately = { true },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    dragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    stopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
                         },
                     )
                     .pointerInput(Unit) {
@@ -218,15 +223,10 @@
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    dragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    stopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
                         },
                     )
             ) {
@@ -341,15 +341,10 @@
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    dragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    stopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
                         },
                     )
             ) {
@@ -447,15 +442,10 @@
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             verticalStarted = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    verticalDragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    verticalStopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { verticalDragged = true },
+                                onStop = { verticalStopped = true },
+                            )
                         },
                     )
                     .multiPointerDraggable(
@@ -464,15 +454,10 @@
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             horizontalStarted = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    horizontalDragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    horizontalStopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { horizontalDragged = true },
+                                onStop = { horizontalStopped = true },
+                            )
                         },
                     )
             )
@@ -567,11 +552,10 @@
                             },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {}
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {}
-                            }
+                            SimpleDragController(
+                                onDrag = { /* do nothing */ },
+                                onStop = { /* do nothing */ },
+                            )
                         },
                     )
             ) {}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index 693fcda..18839e6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.classifier.falsingManager
 import com.android.systemui.haptics.vibratorHelper
 import com.android.systemui.kosmos.testScope
@@ -52,6 +53,7 @@
     private val vibratorHelper = kosmos.vibratorHelper
     private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
     @Mock private lateinit var callback: QSLongPressEffect.Callback
+    @Mock private lateinit var controller: ActivityTransitionAnimator.Controller
 
     private val effectDuration = 400
     private val lowTickDuration = 12
@@ -218,8 +220,9 @@
             // GIVEN that the animation completes
             longPressEffect.handleAnimationComplete()
 
-            // THEN the effect ends in the idle state.
+            // THEN the effect ends in the idle state and the reversed callback is used.
             assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
+            verify(callback, times(1)).onEffectFinishedReversing()
         }
 
     @Test
@@ -348,6 +351,23 @@
         assertThat(clickState).isEqualTo(QSLongPressEffect.State.IDLE)
     }
 
+    @Test
+    fun onLongClickTransitionCancelled_whileInLongClickState_reversesEffect() =
+        testWhileInState(QSLongPressEffect.State.LONG_CLICKED) {
+            // GIVEN a transition controller delegate
+            val delegate = longPressEffect.createTransitionControllerDelegate(controller)
+
+            // WHEN the activity launch animation is cancelled
+            val newOccludedState = false
+            delegate.onTransitionAnimationCancelled(newOccludedState)
+
+            // THEN the effect reverses and ends in RUNNING_BACKWARDS_FROM_CANCEL
+            assertThat(longPressEffect.state)
+                .isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL)
+            verify(callback, times(1)).onReverseAnimator(false)
+            verify(controller).onTransitionAnimationCancelled(newOccludedState)
+        }
+
     private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
         with(kosmos) {
             testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index a3959d2..42cd5ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.StatusBarManager
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
@@ -25,9 +26,13 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
 import com.android.systemui.Flags
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneTransitionInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.flags.BrokenWithSceneContainer
@@ -122,15 +127,22 @@
     private val fromGlanceableHubTransitionInteractor by lazy {
         kosmos.fromGlanceableHubTransitionInteractor
     }
+    private val communalSceneTransitionInteractor by lazy {
+        kosmos.communalSceneTransitionInteractor
+    }
 
     private val powerInteractor by lazy { kosmos.powerInteractor }
     private val communalInteractor by lazy { kosmos.communalInteractor }
+    private val communalSceneInteractor by lazy { kosmos.communalSceneInteractor }
 
     companion object {
         @JvmStatic
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+            return FlagsParameterization.allCombinationsOf(
+                    FLAG_COMMUNAL_SCENE_KTF_REFACTOR,
+                )
+                .andSceneContainer()
         }
     }
 
@@ -163,6 +175,7 @@
         fromOccludedTransitionInteractor.start()
         fromAlternateBouncerTransitionInteractor.start()
         fromGlanceableHubTransitionInteractor.start()
+        communalSceneTransitionInteractor.start()
     }
 
     @Test
@@ -636,6 +649,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun dozingToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
@@ -770,6 +784,7 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun goneToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to GONE
@@ -799,6 +814,29 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun goneToGlanceableHub_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GONE
+            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+
+            // WHEN the glanceable hub is shown
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    to = KeyguardState.GLANCEABLE_HUB,
+                    from = KeyguardState.GONE,
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    animatorAssertion = { it.isNull() }
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     @DisableSceneContainer
     fun alternateBouncerToPrimaryBouncer() =
         testScope.runTest {
@@ -941,6 +979,11 @@
     @Test
     fun alternateBouncerToGlanceableHub() =
         testScope.runTest {
+            // GIVEN the device is idle on the glanceable hub
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
             bouncerRepository.setAlternateVisible(true)
             runTransitionAndSetWakefulness(
@@ -951,19 +994,11 @@
             // GIVEN the primary bouncer isn't showing and device not sleeping
             bouncerRepository.setPrimaryShow(false)
 
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
             // WHEN the alternateBouncer stops showing
             bouncerRepository.setAlternateVisible(false)
             advanceTimeBy(200L)
 
-            // THEN a transition to LOCKSCREEN should occur
+            // THEN a transition to GLANCEABLE_HUB should occur
             assertThat(transitionRepository)
                 .startedTransition(
                     ownerName = FromAlternateBouncerTransitionInteractor::class.simpleName,
@@ -1063,17 +1098,16 @@
     @DisableSceneContainer
     fun primaryBouncerToGlanceableHub() =
         testScope.runTest {
+            // GIVEN the device is idle on the glanceable hub
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
             bouncerRepository.setPrimaryShow(true)
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
-
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
+            runTransitionAndSetWakefulness(
+                KeyguardState.GLANCEABLE_HUB,
+                KeyguardState.PRIMARY_BOUNCER
+            )
 
             // WHEN the primaryBouncer stops showing
             bouncerRepository.setPrimaryShow(false)
@@ -1095,27 +1129,26 @@
     @DisableSceneContainer
     fun primaryBouncerToGlanceableHubWhileDreaming() =
         testScope.runTest {
+            // GIVEN the device is idle on the glanceable hub
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
             bouncerRepository.setPrimaryShow(true)
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
+            runTransitionAndSetWakefulness(
+                KeyguardState.GLANCEABLE_HUB,
+                KeyguardState.PRIMARY_BOUNCER
+            )
 
             // GIVEN that we are dreaming and occluded
             keyguardRepository.setDreaming(true)
             keyguardRepository.setKeyguardOccluded(true)
 
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
             // WHEN the primaryBouncer stops showing
             bouncerRepository.setPrimaryShow(false)
             runCurrent()
 
-            // THEN a transition to LOCKSCREEN should occur
+            // THEN a transition to GLANCEABLE_HUB should occur
             assertThat(transitionRepository)
                 .startedTransition(
                     ownerName = FromPrimaryBouncerTransitionInteractor::class.simpleName,
@@ -1219,6 +1252,7 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun occludedToGlanceableHub() =
         testScope.runTest {
             // GIVEN a device on lockscreen
@@ -1256,6 +1290,7 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun occludedToGlanceableHubWhenInitiallyOnHub() =
         testScope.runTest {
             // GIVEN a device on lockscreen and communal is available
@@ -1293,6 +1328,37 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun occludedToGlanceableHub_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a device on lockscreen and communal is available
+            keyguardRepository.setKeyguardShowing(true)
+            kosmos.setCommunalAvailable(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to OCCLUDED from GLANCEABLE_HUB
+            runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            // WHEN occlusion ends
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            // THEN a transition to GLANCEABLE_HUB should occur
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.OCCLUDED,
+                    to = KeyguardState.GLANCEABLE_HUB,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun occludedToAlternateBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to OCCLUDED
@@ -1511,6 +1577,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun dreamingToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to DREAMING
@@ -1550,6 +1617,47 @@
         }
 
     @Test
+    @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun dreamingToGlanceableHub_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to DREAMING
+            keyguardRepository.setDreaming(true)
+            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
+            runCurrent()
+
+            // WHEN a transition to the glanceable hub starts
+            val currentScene = CommunalScenes.Blank
+            val targetScene = CommunalScenes.Communal
+
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = currentScene,
+                        toScene = targetScene,
+                        currentScene = flowOf(targetScene),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            communalSceneInteractor.setTransitionState(transitionState)
+            progress.value = .1f
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.DREAMING,
+                    to = KeyguardState.GLANCEABLE_HUB,
+                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     @BrokenWithSceneContainer(339465026)
     fun lockscreenToOccluded() =
         testScope.runTest {
@@ -1679,6 +1787,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun lockscreenToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
@@ -1737,6 +1846,48 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun lockscreenToGlanceableHub_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to LOCKSCREEN
+            runTransitionAndSetWakefulness(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
+            runCurrent()
+
+            // WHEN a glanceable hub transition starts
+            val currentScene = CommunalScenes.Blank
+            val targetScene = CommunalScenes.Communal
+
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = currentScene,
+                        toScene = targetScene,
+                        currentScene = flowOf(targetScene),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            communalSceneInteractor.setTransitionState(transitionState)
+            progress.value = .1f
+            runCurrent()
+
+            // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.GLANCEABLE_HUB,
+                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToLockscreen() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1792,6 +1943,48 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToLockscreen_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN a transition away from glanceable hub starts
+            val currentScene = CommunalScenes.Communal
+            val targetScene = CommunalScenes.Blank
+
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = currentScene,
+                        toScene = targetScene,
+                        currentScene = flowOf(targetScene),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            communalSceneInteractor.setTransitionState(transitionState)
+            progress.value = .1f
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.LOCKSCREEN,
+                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToDozing() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1814,6 +2007,31 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToDozing_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN the device begins to sleep
+            powerInteractor.setAsleepForTest()
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.DOZING,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
     fun glanceableHubToPrimaryBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -1858,6 +2076,7 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToOccluded() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1888,7 +2107,33 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToOccluded_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN the keyguard is occluded
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.OCCLUDED,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToGone() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1911,6 +2156,32 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToGone_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN keyguard goes away
+            keyguardRepository.setKeyguardGoingAway(true)
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.GONE,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToDreaming() =
         testScope.runTest {
             // GIVEN that we are dreaming and not dozing
@@ -1939,7 +2210,7 @@
                         isUserInputOngoing = flowOf(false),
                     )
                 )
-            communalInteractor.setTransitionState(transitionState)
+            communalSceneInteractor.setTransitionState(transitionState)
             runCurrent()
 
             assertThat(transitionRepository)
@@ -1953,6 +2224,52 @@
             coroutineContext.cancelChildren()
         }
 
+    @Test
+    @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToDreaming_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN that we are dreaming and not dozing
+            keyguardRepository.setDreaming(true)
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN a transition away from glanceable hub starts
+            val currentScene = CommunalScenes.Communal
+            val targetScene = CommunalScenes.Blank
+
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = currentScene,
+                        toScene = targetScene,
+                        currentScene = flowOf(targetScene),
+                        progress = flowOf(0f, 0.1f),
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            communalSceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.DREAMING,
+                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
     private suspend fun TestScope.runTransitionAndSetWakefulness(
         from: KeyguardState,
         to: KeyguardState
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 484e758..4ef1f93 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -124,6 +124,8 @@
     public static final long SYSUI_STATE_SHORTCUT_HELPER_SHOWING = 1L << 32;
     // Touchpad gestures are disabled
     public static final long SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED = 1L << 33;
+    // PiP animation is running
+    public static final long SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING = 1L << 34;
     // Communal hub is showing
     public static final long SYSUI_STATE_COMMUNAL_HUB_SHOWING = 1L << 35;
 
@@ -175,6 +177,7 @@
             SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY,
             SYSUI_STATE_SHORTCUT_HELPER_SHOWING,
             SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED,
+            SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING,
             SYSUI_STATE_COMMUNAL_HUB_SHOWING,
     })
     public @interface SystemUiStateFlags {}
@@ -280,6 +283,9 @@
         if ((flags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) != 0) {
             str.add("touchpad_gestures_disabled");
         }
+        if ((flags & SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING) != 0) {
+            str.add("disable_gesture_pip_animating");
+        }
         if ((flags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
             str.add("communal_hub_showing");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index e13161f..dbddc23 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -121,9 +121,25 @@
 
     private val _editModeOpen = MutableStateFlow(false)
 
-    /** Whether edit mode is currently open. */
+    /**
+     * Whether edit mode is currently open. This will be true from onCreate to onDestroy in
+     * [EditWidgetsActivity] and thus does not correspond to whether or not the activity is visible.
+     *
+     * Note that since this is called in onDestroy, it's not guaranteed to ever be set to false when
+     * edit mode is closed, such as in the case that a user exits edit mode manually with a back
+     * gesture or navigation gesture.
+     */
     val editModeOpen: StateFlow<Boolean> = _editModeOpen.asStateFlow()
 
+    private val _editActivityShowing = MutableStateFlow(false)
+
+    /**
+     * Whether the edit mode activity is currently showing. This is true from onStart to onStop in
+     * [EditWidgetsActivity] so may be false even when the user is in edit mode, such as when a
+     * widget's individual configuration activity has launched.
+     */
+    val editActivityShowing: StateFlow<Boolean> = _editActivityShowing.asStateFlow()
+
     /** Whether communal features are enabled. */
     val isCommunalEnabled: StateFlow<Boolean> = communalSettingsInteractor.isCommunalEnabled
 
@@ -316,6 +332,10 @@
         _editModeOpen.value = isOpen
     }
 
+    fun setEditActivityShowing(isOpen: Boolean) {
+        _editActivityShowing.value = isOpen
+    }
+
     /** Show the widget editor Activity. */
     fun showWidgetEditor(
         preselectedKey: String? = null,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index aa9cbd0..45cfe36 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -52,7 +52,7 @@
     @Application private val applicationScope: CoroutineScope,
     private val communalSceneRepository: CommunalSceneRepository,
 ) {
-    val _isLaunchingWidget = MutableStateFlow(false)
+    private val _isLaunchingWidget = MutableStateFlow(false)
 
     /** Whether a widget launch is currently in progress. */
     val isLaunchingWidget: StateFlow<Boolean> = _isLaunchingWidget.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 0353d2c..830f543 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -24,7 +24,6 @@
 import android.util.Log
 import androidx.activity.result.ActivityResultLauncher
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.Flags.enableWidgetPickerSizeFilter
 import com.android.systemui.communal.data.model.CommunalWidgetCategories
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
@@ -176,16 +175,14 @@
 
         return Intent(Intent.ACTION_PICK).apply {
             setPackage(packageName)
-            if (enableWidgetPickerSizeFilter()) {
-                putExtra(
-                    EXTRA_DESIRED_WIDGET_WIDTH,
-                    resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
-                )
-                putExtra(
-                    EXTRA_DESIRED_WIDGET_HEIGHT,
-                    resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height)
-                )
-            }
+            putExtra(
+                EXTRA_DESIRED_WIDGET_WIDTH,
+                resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
+            )
+            putExtra(
+                EXTRA_DESIRED_WIDGET_HEIGHT,
+                resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height)
+            )
             putExtra(
                 AppWidgetManager.EXTRA_CATEGORY_FILTER,
                 CommunalWidgetCategories.defaultCategories
@@ -217,6 +214,14 @@
     /** Sets whether edit mode is currently open */
     fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen)
 
+    /**
+     * Sets whether the edit mode activity is currently showing.
+     *
+     * See [CommunalInteractor.editActivityShowing] for more info.
+     */
+    fun setEditActivityShowing(showing: Boolean) =
+        communalInteractor.setEditActivityShowing(showing)
+
     /** Called when exiting the edit mode, before transitioning back to the communal scene. */
     fun cleanupEditModeState() {
         communalSceneInteractor.setEditModeState(null)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 46f802f..08fe42e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -96,8 +96,7 @@
                                 run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
                             }
                         }
-                    }
-                        ?: run { Log.w(TAG, "No data in result.") }
+                    } ?: run { Log.w(TAG, "No data in result.") }
                 }
                 else ->
                     Log.w(
@@ -195,6 +194,8 @@
     override fun onStart() {
         super.onStart()
 
+        communalViewModel.setEditActivityShowing(true)
+
         if (shouldOpenWidgetPickerOnStart) {
             onOpenWidgetPicker()
             shouldOpenWidgetPickerOnStart = false
@@ -206,6 +207,7 @@
 
     override fun onStop() {
         super.onStop()
+        communalViewModel.setEditActivityShowing(false)
 
         logger.i("Stopping the communal widget editor activity")
         uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_GONE)
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index c44eb471..491c73d 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -16,9 +16,15 @@
 
 package com.android.systemui.haptics.qs
 
+import android.content.ComponentName
 import android.os.VibrationEffect
 import android.service.quicksettings.Tile
+import android.view.View
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.DelegateTransitionAnimatorController
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QSTile
@@ -58,6 +64,7 @@
     /** The [QSTile] and [Expandable] used to perform a long-click and click actions */
     var qsTile: QSTile? = null
     var expandable: Expandable? = null
+        private set
 
     /** Haptic effects */
     private val durations =
@@ -125,8 +132,10 @@
     }
 
     fun handleAnimationStart() {
-        vibrate(longPressHint)
-        setState(State.RUNNING_FORWARD)
+        if (state == State.TIMEOUT_WAIT) {
+            vibrate(longPressHint)
+            setState(State.RUNNING_FORWARD)
+        }
     }
 
     /** This function is called both when an animator completes or gets cancelled */
@@ -147,7 +156,10 @@
                 setState(getStateForClick())
                 qsTile?.click(expandable)
             }
-            State.RUNNING_BACKWARDS_FROM_CANCEL -> setState(State.IDLE)
+            State.RUNNING_BACKWARDS_FROM_CANCEL -> {
+                callback?.onEffectFinishedReversing()
+                setState(State.IDLE)
+            }
             else -> {}
         }
     }
@@ -222,13 +234,58 @@
 
     fun resetState() = setState(State.IDLE)
 
+    fun createExpandableFromView(view: View) {
+        expandable =
+            object : Expandable {
+                override fun activityTransitionController(
+                    launchCujType: Int?,
+                    cookie: ActivityTransitionAnimator.TransitionCookie?,
+                    component: ComponentName?,
+                    returnCujType: Int?,
+                ): ActivityTransitionAnimator.Controller? {
+                    val delegatedController =
+                        ActivityTransitionAnimator.Controller.fromView(
+                            view,
+                            launchCujType,
+                            cookie,
+                            component,
+                            returnCujType,
+                        )
+                    return delegatedController?.let { createTransitionControllerDelegate(it) }
+                }
+
+                override fun dialogTransitionController(
+                    cuj: DialogCuj?,
+                ): DialogTransitionAnimator.Controller? =
+                    DialogTransitionAnimator.Controller.fromView(view, cuj)
+            }
+    }
+
+    @VisibleForTesting
+    fun createTransitionControllerDelegate(
+        controller: ActivityTransitionAnimator.Controller
+    ): DelegateTransitionAnimatorController {
+        val delegated =
+            object : DelegateTransitionAnimatorController(controller) {
+                override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+                    if (state == State.LONG_CLICKED) {
+                        setState(State.RUNNING_BACKWARDS_FROM_CANCEL)
+                        callback?.onReverseAnimator(false)
+                    }
+                    delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
+                }
+            }
+        return delegated
+    }
+
     enum class State {
         IDLE, /* The effect is idle waiting for touch input */
         TIMEOUT_WAIT, /* The effect is waiting for a tap timeout period */
         RUNNING_FORWARD, /* The effect is running normally */
         /* The effect was interrupted by an ACTION_UP and is now running backwards */
         RUNNING_BACKWARDS_FROM_UP,
-        /* The effect was interrupted by an ACTION_CANCEL and is now running backwards */
+        /* The effect was cancelled by an ACTION_CANCEL or a shade collapse and is now running
+        backwards */
         RUNNING_BACKWARDS_FROM_CANCEL,
         CLICKED, /* The effect has ended with a click */
         LONG_CLICKED, /* The effect has ended with a long-click */
@@ -247,7 +304,7 @@
         fun onStartAnimator()
 
         /** Reverse the effect animator */
-        fun onReverseAnimator()
+        fun onReverseAnimator(playHaptics: Boolean = true)
 
         /** Cancel the effect animator */
         fun onCancelAnimator()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 59ec87a..e5ccc4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -119,7 +119,9 @@
                                 // needed. Also, don't react to wake and unlock events, as we'll be
                                 // receiving a call to #dismissAod() shortly when the authentication
                                 // completes.
-                                !maybeStartTransitionToOccludedOrInsecureCamera() &&
+                                !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+                                    startTransitionTo(state, ownerReason = reason)
+                                } &&
                                     !isWakeAndUnlock(biometricUnlockState.mode) &&
                                     !primaryBouncerShowing
                             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 8f50b03..8ef138e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -20,8 +20,11 @@
 import android.annotation.SuppressLint
 import android.app.DreamManager
 import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -166,7 +169,7 @@
                         }
                     } else if (occluded) {
                         startTransitionTo(KeyguardState.OCCLUDED)
-                    } else if (isIdleOnCommunal) {
+                    } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
                         if (SceneContainerFlag.isEnabled) {
                             // TODO(b/336576536): Check if adaptation for scene framework is needed
                         } else {
@@ -183,7 +186,7 @@
                         if (SceneContainerFlag.isEnabled) {
                             // TODO(b/336576536): Check if adaptation for scene framework is needed
                         } else {
-                            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+                            transitionToGlanceableHub()
                         }
                     } else {
                         startTransitionTo(KeyguardState.LOCKSCREEN)
@@ -218,7 +221,9 @@
                         canWakeDirectlyToGone,
                         primaryBouncerShowing) ->
                     if (
-                        !maybeStartTransitionToOccludedOrInsecureCamera() &&
+                        !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+                            startTransitionTo(state, ownerReason = reason)
+                        } &&
                             // Handled by dismissFromDozing().
                             !isWakeAndUnlock(biometricUnlockState.mode)
                     ) {
@@ -242,7 +247,7 @@
                                     ownerReason = "waking from dozing"
                                 )
                             }
-                        } else if (isIdleOnCommunal) {
+                        } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
                             if (SceneContainerFlag.isEnabled) {
                                 // TODO(b/336576536): Check if adaptation for scene framework is
                                 // needed
@@ -264,10 +269,7 @@
                                 // TODO(b/336576536): Check if adaptation for scene framework is
                                 // needed
                             } else {
-                                startTransitionTo(
-                                    KeyguardState.GLANCEABLE_HUB,
-                                    ownerReason = "waking from dozing"
-                                )
+                                transitionToGlanceableHub()
                             }
                         } else {
                             startTransitionTo(
@@ -280,6 +282,18 @@
         }
     }
 
+    private suspend fun transitionToGlanceableHub() {
+        if (communalSceneKtfRefactor()) {
+            communalSceneInteractor.changeScene(
+                CommunalScenes.Communal,
+                // Immediately show the hub when transitioning from dozing to hub.
+                CommunalTransitionKeys.Immediately,
+            )
+        } else {
+            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+        }
+    }
+
     /** Dismisses keyguard from the DOZING state. */
     fun dismissFromDozing() {
         scope.launch { startTransitionTo(KeyguardState.GONE) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 453401d..4c3a75e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -19,6 +19,7 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -81,7 +82,9 @@
         listenForDreamingToLockscreenOrGone()
         listenForDreamingToAodOrDozing()
         listenForTransitionToCamera(scope, keyguardInteractor)
-        listenForDreamingToGlanceableHub()
+        if (!communalSceneKtfRefactor()) {
+            listenForDreamingToGlanceableHub()
+        }
         listenForDreamingToPrimaryBouncer()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 1a7012a..d811950 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -19,7 +19,11 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -30,6 +34,7 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.noneOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -50,6 +55,7 @@
     private val glanceableHubTransitions: GlanceableHubTransitions,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     keyguardInteractor: KeyguardInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     override val transitionRepository: KeyguardTransitionRepository,
     override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
     transitionInteractor: KeyguardTransitionInteractor,
@@ -72,7 +78,9 @@
         if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
             return
         }
-        listenForHubToLockscreenOrDreaming()
+        if (!communalSceneKtfRefactor()) {
+            listenForHubToLockscreenOrDreaming()
+        }
         listenForHubToDozing()
         listenForHubToPrimaryBouncer()
         listenForHubToAlternateBouncer()
@@ -120,7 +128,10 @@
         scope.launch("$TAG#listenForHubToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing }
-                .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) }
+                .collect {
+                    // Bouncer shows on top of the hub, so do not change scenes here.
+                    startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+                }
         }
     }
 
@@ -130,7 +141,10 @@
                 .filterRelevantKeyguardStateAnd { alternateBouncerShowing ->
                     alternateBouncerShowing
                 }
-                .collect { pair -> startTransitionTo(KeyguardState.ALTERNATE_BOUNCER) }
+                .collect { pair ->
+                    // Bouncer shows on top of the hub, so do not change scenes here.
+                    startTransitionTo(KeyguardState.ALTERNATE_BOUNCER)
+                }
         }
     }
 
@@ -139,10 +153,18 @@
             powerInteractor.isAsleep
                 .filterRelevantKeyguardStateAnd { isAsleep -> isAsleep }
                 .collect {
-                    startTransitionTo(
-                        toState = KeyguardState.DOZING,
-                        modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
-                    )
+                    if (communalSceneKtfRefactor()) {
+                        communalSceneInteractor.changeScene(
+                            newScene = CommunalScenes.Blank,
+                            transitionKey = CommunalTransitionKeys.Immediately,
+                            keyguardState = KeyguardState.DOZING,
+                        )
+                    } else {
+                        startTransitionTo(
+                            toState = KeyguardState.DOZING,
+                            modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
+                        )
+                    }
                 }
         }
     }
@@ -152,7 +174,44 @@
             scope.launch {
                 keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop
                     .filterRelevantKeyguardStateAnd { onTop -> onTop }
-                    .collect { maybeStartTransitionToOccludedOrInsecureCamera() }
+                    .collect {
+                        maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+                            if (communalSceneKtfRefactor()) {
+                                communalSceneInteractor.changeScene(
+                                    newScene = CommunalScenes.Blank,
+                                    transitionKey = CommunalTransitionKeys.SimpleFade,
+                                    keyguardState = state,
+                                )
+                                null
+                            } else {
+                                startTransitionTo(state, ownerReason = reason)
+                            }
+                        }
+                    }
+            }
+        } else if (communalSceneKtfRefactor()) {
+            scope.launch {
+                allOf(
+                        keyguardInteractor.isKeyguardOccluded,
+                        noneOf(
+                            // Dream is a special-case of occluded, so filter out the dreaming
+                            // case here.
+                            keyguardInteractor.isDreaming,
+                            // When launching activities from widgets on the hub, we have a
+                            // custom occlusion animation.
+                            communalSceneInteractor.isLaunchingWidget,
+                        ),
+                    )
+                    .filterRelevantKeyguardStateAnd { isOccludedAndNotDreamingNorLaunchingWidget ->
+                        isOccludedAndNotDreamingNorLaunchingWidget
+                    }
+                    .collect { _ ->
+                        communalSceneInteractor.changeScene(
+                            newScene = CommunalScenes.Blank,
+                            transitionKey = CommunalTransitionKeys.SimpleFade,
+                            keyguardState = KeyguardState.OCCLUDED,
+                        )
+                    }
             }
         } else {
             scope.launch {
@@ -160,9 +219,7 @@
                     .filterRelevantKeyguardStateAnd { isOccludedAndNotDreaming ->
                         isOccludedAndNotDreaming
                     }
-                    .collect { isOccludedAndNotDreaming ->
-                        startTransitionTo(KeyguardState.OCCLUDED)
-                    }
+                    .collect { _ -> startTransitionTo(KeyguardState.OCCLUDED) }
             }
         }
     }
@@ -170,10 +227,33 @@
     private fun listenForHubToGone() {
         // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
-        scope.launch {
-            keyguardInteractor.isKeyguardGoingAway
-                .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
-                .collect { startTransitionTo(KeyguardState.GONE) }
+        if (communalSceneKtfRefactor()) {
+            scope.launch {
+                allOf(
+                        keyguardInteractor.isKeyguardGoingAway,
+                        // TODO(b/327225415): Handle edit mode opening here to avoid going to GONE
+                        // state until after edit mode is ready to be shown.
+                        noneOf(
+                            // When launching activities from widgets on the hub, we wait to change
+                            // scenes until the activity launch is complete.
+                            communalSceneInteractor.isLaunchingWidget,
+                        ),
+                    )
+                    .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
+                    .collect {
+                        communalSceneInteractor.changeScene(
+                            newScene = CommunalScenes.Blank,
+                            transitionKey = CommunalTransitionKeys.SimpleFade,
+                            keyguardState = KeyguardState.GONE
+                        )
+                    }
+            }
+        } else {
+            scope.launch {
+                keyguardInteractor.isKeyguardGoingAway
+                    .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
+                    .collect { startTransitionTo(KeyguardState.GONE) }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 5c7adf0..16c014f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -20,6 +20,7 @@
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -90,7 +91,9 @@
         listenForLockscreenToPrimaryBouncerDragging()
         listenForLockscreenToAlternateBouncer()
         listenForLockscreenTransitionToCamera()
-        listenForLockscreenToGlanceableHub()
+        if (!communalSceneKtfRefactor()) {
+            listenForLockscreenToGlanceableHub()
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index f3ca9df..2f32040 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -18,8 +18,12 @@
 
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.Flags.restartDreamOnUnocclude
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -49,6 +53,7 @@
     keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
     private val communalInteractor: CommunalInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
 ) :
     TransitionInteractor(
@@ -140,7 +145,14 @@
         } else if (isIdleOnCommunal || showCommunalFromOccluded) {
             // TODO(b/336576536): Check if adaptation for scene framework is needed
             if (SceneContainerFlag.isEnabled) return
-            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+            if (communalSceneKtfRefactor()) {
+                communalSceneInteractor.changeScene(
+                    CommunalScenes.Communal,
+                    CommunalTransitionKeys.SimpleFade
+                )
+            } else {
+                startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+            }
         } else {
             startTransitionTo(KeyguardState.LOCKSCREEN)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 2429088..6c89ce0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -113,10 +113,9 @@
                         (isBouncerShowing, isAwake, isActiveDreamLockscreenHosted, isIdleOnCommunal)
                         ->
                         if (
-                            !maybeStartTransitionToOccludedOrInsecureCamera() &&
-                                !isBouncerShowing &&
-                                isAwake &&
-                                !isActiveDreamLockscreenHosted
+                            !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+                                startTransitionTo(state, ownerReason = reason)
+                            } && !isBouncerShowing && isAwake && !isActiveDreamLockscreenHosted
                         ) {
                             val toState =
                                 if (isIdleOnCommunal) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 89c7178..d06ee64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -122,9 +122,14 @@
      * SHOW_WHEN_LOCKED activity, or back to [KeyguardState.GONE], for some power button launch
      * gesture cases. If so, start the transition.
      *
+     * @param startTransition A callback which is triggered to start the transition to the desired
+     *   KeyguardState. Allows caller to hook into the transition start if needed.
+     *
      * Returns true if a transition was started, false otherwise.
      */
-    suspend fun maybeStartTransitionToOccludedOrInsecureCamera(): Boolean {
+    suspend fun maybeStartTransitionToOccludedOrInsecureCamera(
+        startTransition: suspend (state: KeyguardState, reason: String) -> UUID?
+    ): Boolean {
         // The refactor is required for the occlusion interactor to work.
         KeyguardWmStateRefactor.isUnexpectedlyInLegacyMode()
 
@@ -136,10 +141,7 @@
             if (!maybeHandleInsecurePowerGesture()) {
                 // Otherwise, the double tap gesture occurred while not GONE and not dismissable,
                 // which means we will launch the secure camera, which OCCLUDES the keyguard.
-                startTransitionTo(
-                    KeyguardState.OCCLUDED,
-                    ownerReason = "Power button gesture on lockscreen"
-                )
+                startTransition(KeyguardState.OCCLUDED, "Power button gesture on lockscreen")
             }
 
             return true
@@ -147,10 +149,7 @@
             // A SHOW_WHEN_LOCKED activity is on top of the task stack. Transition to OCCLUDED so
             // it's visible.
             // TODO(b/307976454) - Centralize transition to DREAMING here.
-            startTransitionTo(
-                KeyguardState.OCCLUDED,
-                ownerReason = "SHOW_WHEN_LOCKED activity on top"
-            )
+            startTransition(KeyguardState.OCCLUDED, "SHOW_WHEN_LOCKED activity on top")
 
             return true
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 6b3dfe1..dbfe818 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -107,7 +107,9 @@
         set(value) {
             if (field == value) return
             field = value
-            updateHeight()
+            if (longPressEffect?.state != QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL) {
+                updateHeight()
+            }
         }
 
     override var squishinessFraction: Float = 1f
@@ -381,14 +383,6 @@
     }
 
     private fun updateHeight() {
-        // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
-        //  launch animation.
-        if (!haveLongPressPropertiesBeenReset && longPressEffect != null) {
-            // The launch animation of a long-press effect did not reset the long-press effect so
-            // we must do it here
-            resetLongPressEffectProperties()
-            longPressEffect.resetState()
-        }
         val actualHeight =
             if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
                 heightOverride
@@ -417,17 +411,17 @@
     }
 
     override fun init(tile: QSTile) {
-        val expandable = Expandable.fromView(this)
         if (longPressEffect != null) {
             isHapticFeedbackEnabled = false
             longPressEffect.qsTile = tile
-            longPressEffect.expandable = expandable
+            longPressEffect.createExpandableFromView(this)
             initLongPressEffectCallback()
             init(
                 { _: View -> longPressEffect.onTileClick() },
                 null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
             )
         } else {
+            val expandable = Expandable.fromView(this)
             init(
                 { _: View? -> tile.click(expandable) },
                 { _: View? ->
@@ -475,10 +469,10 @@
                     }
                 }
 
-                override fun onReverseAnimator() {
+                override fun onReverseAnimator(playHaptics: Boolean) {
                     longPressEffectAnimator?.let {
                         val pausedProgress = it.animatedFraction
-                        longPressEffect?.playReverseHaptics(pausedProgress)
+                        if (playHaptics) longPressEffect?.playReverseHaptics(pausedProgress)
                         it.reverse()
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 4914409..54ae225 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -49,6 +49,7 @@
 import java.util.Random;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
 
 /**
  * An AsyncTask that saves an image to the media store in the background.
@@ -59,12 +60,73 @@
     private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s";
     private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
 
+    /**
+     * POD used in the AsyncTask which saves an image in the background.
+     */
+    static class SaveImageInBackgroundData {
+        public Bitmap image;
+        public Consumer<Uri> finisher;
+        public ActionsReadyListener mActionsReadyListener;
+        public QuickShareActionReadyListener mQuickShareActionsReadyListener;
+        public UserHandle owner;
+        public int displayId;
+
+        void clearImage() {
+            image = null;
+        }
+    }
+
+    /**
+     * Structure returned by the SaveImageInBackgroundTask
+     */
+    public static class SavedImageData {
+        public Uri uri;
+        public List<Notification.Action> smartActions;
+        public Notification.Action quickShareAction;
+        public UserHandle owner;
+        public String subject;  // Title for sharing
+        public Long imageTime; // Time at which screenshot was saved
+
+        /**
+         * Used to reset the return data on error
+         */
+        public void reset() {
+            uri = null;
+            smartActions = null;
+            quickShareAction = null;
+            subject = null;
+            imageTime = null;
+        }
+    }
+
+    /**
+     * Structure returned by the QueryQuickShareInBackgroundTask
+     */
+    static class QuickShareData {
+        public Notification.Action quickShareAction;
+
+        /**
+         * Used to reset the return data on error
+         */
+        public void reset() {
+            quickShareAction = null;
+        }
+    }
+
+    interface ActionsReadyListener {
+        void onActionsReady(SavedImageData imageData);
+    }
+
+    interface QuickShareActionReadyListener {
+        void onActionsReady(QuickShareData quickShareData);
+    }
+
     private final Context mContext;
     private FeatureFlags mFlags;
     private final ScreenshotSmartActions mScreenshotSmartActions;
-    private final ScreenshotController.SaveImageInBackgroundData mParams;
-    private final ScreenshotController.SavedImageData mImageData;
-    private final ScreenshotController.QuickShareData mQuickShareData;
+    private final SaveImageInBackgroundData mParams;
+    private final SavedImageData mImageData;
+    private final QuickShareData mQuickShareData;
 
     private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
     private String mScreenshotId;
@@ -77,15 +139,15 @@
             FeatureFlags flags,
             ImageExporter exporter,
             ScreenshotSmartActions screenshotSmartActions,
-            ScreenshotController.SaveImageInBackgroundData data,
+            SaveImageInBackgroundData data,
             ScreenshotNotificationSmartActionsProvider
                     screenshotNotificationSmartActionsProvider
     ) {
         mContext = context;
         mFlags = flags;
         mScreenshotSmartActions = screenshotSmartActions;
-        mImageData = new ScreenshotController.SavedImageData();
-        mQuickShareData = new ScreenshotController.QuickShareData();
+        mImageData = new SavedImageData();
+        mQuickShareData = new QuickShareData();
         mImageExporter = exporter;
 
         // Prepare all the output metadata
@@ -195,7 +257,7 @@
      * Update the listener run when the saving task completes. Used to avoid showing UI for the
      * first screenshot when a second one is taken.
      */
-    void setActionsReadyListener(ScreenshotController.ActionsReadyListener listener) {
+    void setActionsReadyListener(ActionsReadyListener listener) {
         mParams.mActionsReadyListener = listener;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 7739009..0a4635e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -35,10 +35,7 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.ExitTransitionCoordinator;
 import android.app.ICompatCameraControlCallback;
-import android.app.Notification;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -55,7 +52,6 @@
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.Pair;
 import android.view.Display;
 import android.view.ScrollCaptureResponse;
 import android.view.View;
@@ -67,7 +63,6 @@
 import android.widget.Toast;
 import android.window.WindowContext;
 
-import com.android.internal.app.ChooserActivity;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.policy.PhoneWindow;
 import com.android.settingslib.applications.InterestingConfigChanges;
@@ -89,7 +84,6 @@
 
 import kotlin.Unit;
 
-import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
@@ -104,67 +98,6 @@
 public class ScreenshotController implements ScreenshotHandler {
     private static final String TAG = logTag(ScreenshotController.class);
 
-    /**
-     * POD used in the AsyncTask which saves an image in the background.
-     */
-    static class SaveImageInBackgroundData {
-        public Bitmap image;
-        public Consumer<Uri> finisher;
-        public ScreenshotController.ActionsReadyListener mActionsReadyListener;
-        public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener;
-        public UserHandle owner;
-        public int displayId;
-
-        void clearImage() {
-            image = null;
-        }
-    }
-
-    /**
-     * Structure returned by the SaveImageInBackgroundTask
-     */
-    public static class SavedImageData {
-        public Uri uri;
-        public List<Notification.Action> smartActions;
-        public Notification.Action quickShareAction;
-        public UserHandle owner;
-        public String subject;  // Title for sharing
-        public Long imageTime; // Time at which screenshot was saved
-
-        /**
-         * Used to reset the return data on error
-         */
-        public void reset() {
-            uri = null;
-            smartActions = null;
-            quickShareAction = null;
-            subject = null;
-            imageTime = null;
-        }
-    }
-
-    /**
-     * Structure returned by the QueryQuickShareInBackgroundTask
-     */
-    static class QuickShareData {
-        public Notification.Action quickShareAction;
-
-        /**
-         * Used to reset the return data on error
-         */
-        public void reset() {
-            quickShareAction = null;
-        }
-    }
-
-    interface ActionsReadyListener {
-        void onActionsReady(ScreenshotController.SavedImageData imageData);
-    }
-
-    interface QuickShareActionReadyListener {
-        void onActionsReady(ScreenshotController.QuickShareData quickShareData);
-    }
-
     public interface TransitionDestination {
         /**
          * Allows the long screenshot activity to call back with a destination location (the bounds
@@ -213,7 +146,6 @@
     private final ScreenshotNotificationSmartActionsProvider
             mScreenshotNotificationSmartActionsProvider;
     private final TimeoutHandler mScreenshotHandler;
-    private final ActionIntentExecutor mActionIntentExecutor;
     private final UserManager mUserManager;
     private final AssistContentRequester mAssistContentRequester;
     private final ActionExecutor mActionExecutor;
@@ -259,7 +191,6 @@
             BroadcastDispatcher broadcastDispatcher,
             ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
             ScreenshotActionsController.Factory screenshotActionsControllerFactory,
-            ActionIntentExecutor actionIntentExecutor,
             ActionExecutor.Factory actionExecutorFactory,
             UserManager userManager,
             AssistContentRequester assistContentRequester,
@@ -289,7 +220,6 @@
         final Context displayContext = context.createDisplayContext(display);
         mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
         mFlags = flags;
-        mActionIntentExecutor = actionIntentExecutor;
         mUserManager = userManager;
         mMessageContainerController = messageContainerController;
         mAssistContentRequester = assistContentRequester;
@@ -765,33 +695,6 @@
         mScreenshotAnimation.start();
     }
 
-    /**
-     * Supplies the necessary bits for the shared element transition to share sheet.
-     * Note that once called, the action intent to share must be sent immediately after.
-     */
-    private Pair<ActivityOptions, ExitTransitionCoordinator> createWindowTransition() {
-        ExitTransitionCoordinator.ExitTransitionCallbacks callbacks =
-                new ExitTransitionCoordinator.ExitTransitionCallbacks() {
-                    @Override
-                    public boolean isReturnTransitionAllowed() {
-                        return false;
-                    }
-
-                    @Override
-                    public void hideSharedElements() {
-                        finishDismiss();
-                    }
-
-                    @Override
-                    public void onFinish() {
-                    }
-                };
-
-        return ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null,
-                Pair.create(mViewProxy.getScreenshotPreview(),
-                        ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
-    }
-
     /** Reset screenshot view and then call onCompleteRunnable */
     private void finishDismiss() {
         Log.d(TAG, "finishDismiss");
@@ -838,11 +741,11 @@
     private void saveScreenshotInWorkerThread(
             UserHandle owner,
             @NonNull Consumer<Uri> finisher,
-            @Nullable ActionsReadyListener actionsReadyListener,
-            @Nullable QuickShareActionReadyListener
+            @Nullable SaveImageInBackgroundTask.ActionsReadyListener actionsReadyListener,
+            @Nullable SaveImageInBackgroundTask.QuickShareActionReadyListener
                     quickShareActionsReadyListener) {
-        ScreenshotController.SaveImageInBackgroundData
-                data = new ScreenshotController.SaveImageInBackgroundData();
+        SaveImageInBackgroundTask.SaveImageInBackgroundData
+                data = new SaveImageInBackgroundTask.SaveImageInBackgroundData();
         data.image = mScreenBitmap;
         data.finisher = finisher;
         data.mActionsReadyListener = actionsReadyListener;
@@ -881,7 +784,7 @@
     /**
      * Logs success/failure of the screenshot saving task, and shows an error if it failed.
      */
-    private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) {
+    private void logSuccessOnActionsReady(SaveImageInBackgroundTask.SavedImageData imageData) {
         logScreenshotResultStatus(imageData.uri, imageData.owner);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index d090aea..b468d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -50,6 +50,9 @@
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -76,6 +79,7 @@
     private val communalInteractor: CommunalInteractor,
     private val communalViewModel: CommunalViewModel,
     private val keyguardInteractor: KeyguardInteractor,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val shadeInteractor: ShadeInteractor,
     private val powerManager: PowerManager,
     private val communalColors: CommunalColors,
@@ -148,6 +152,19 @@
     private var hubShowing = false
 
     /**
+     * True if we're transitioning to or from edit mode
+     *
+     * We block all touches and gestures when edit mode is open to prevent funky transition issues
+     * when entering and exiting edit mode because we delay exiting the hub scene when entering edit
+     * mode and enter the hub scene early when exiting edit mode to make for a smoother transition.
+     * Gestures during these transitions can result in broken and unexpected UI states.
+     *
+     * Tracks [CommunalInteractor.editActivityShowing] and the [KeyguardState.GONE] to
+     * [KeyguardState.GLANCEABLE_HUB] transition.
+     */
+    private var inEditModeTransition = false
+
+    /**
      * True if either the primary or alternate bouncer are open, meaning the hub should not receive
      * any touch input.
      */
@@ -323,6 +340,22 @@
         )
         collectFlow(
             containerView,
+            // When leaving edit mode, editActivityShowing is true until the edit mode activity
+            // finishes itself and the device locks, after which isInTransition will be true until
+            // we're fully on the hub.
+            anyOf(
+                communalInteractor.editActivityShowing,
+                keyguardTransitionInteractor.isInTransition(
+                    Edge.create(KeyguardState.GONE, KeyguardState.GLANCEABLE_HUB)
+                )
+            ),
+            {
+                inEditModeTransition = it
+                updateTouchHandlingState()
+            }
+        )
+        collectFlow(
+            containerView,
             combine(
                 shadeInteractor.isAnyFullyExpanded,
                 shadeInteractor.isUserInteracting,
@@ -359,8 +392,11 @@
      * Also clears gesture exclusion zones when the hub is occluded or gone.
      */
     private fun updateTouchHandlingState() {
+        // Only listen to gestures when we're settled in the hub keyguard state and the shade
+        // bouncer are not showing on top.
         val shouldInterceptGestures =
-            hubShowing && !(shadeShowingAndConsumingTouches || anyBouncerShowing)
+            hubShowing &&
+                !(shadeShowingAndConsumingTouches || anyBouncerShowing || inEditModeTransition)
         if (shouldInterceptGestures) {
             lifecycleRegistry.currentState = Lifecycle.State.RESUMED
         } else {
@@ -412,10 +448,10 @@
             return false
         }
 
-        return communalContainerView?.let { handleTouchEventOnCommunalView(it, ev) } ?: false
+        return communalContainerView?.let { handleTouchEventOnCommunalView(ev) } ?: false
     }
 
-    private fun handleTouchEventOnCommunalView(view: View, ev: MotionEvent): Boolean {
+    private fun handleTouchEventOnCommunalView(ev: MotionEvent): Boolean {
         val isDown = ev.actionMasked == MotionEvent.ACTION_DOWN
         val isUp = ev.actionMasked == MotionEvent.ACTION_UP
         val isMove = ev.actionMasked == MotionEvent.ACTION_MOVE
@@ -431,7 +467,7 @@
             if (isUp || isCancel) {
                 isTrackingHubTouch = false
             }
-            return dispatchTouchEvent(view, ev)
+            return dispatchTouchEvent(ev)
         }
 
         return false
@@ -441,7 +477,14 @@
      * Dispatches the touch event to the communal container and sends a user activity event to reset
      * the screen timeout.
      */
-    private fun dispatchTouchEvent(view: View, ev: MotionEvent): Boolean {
+    private fun dispatchTouchEvent(ev: MotionEvent): Boolean {
+        if (inEditModeTransition) {
+            // Consume but ignore touches while we're transitioning to or from edit mode so that the
+            // user can't trigger another transition, such as by swiping the hub away, tapping a
+            // widget, or opening the shade/bouncer. Doing any of these while transitioning can
+            // result in broken states.
+            return true
+        }
         try {
             var handled = false
             communalContainerWrapper?.dispatchTouchEvent(ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c912616..b9d24ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -51,6 +51,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Pair;
 import android.util.SparseArray;
 import android.view.KeyEvent;
@@ -200,6 +201,7 @@
      * event.
      */
     private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
+    private final Context mContext;
     private final DisplayTracker mDisplayTracker;
     private final @Nullable CommandRegistry mRegistry;
     private final @Nullable DumpHandler mDumpHandler;
@@ -571,6 +573,7 @@
             DumpHandler dumpHandler,
             Lazy<PowerInteractor> powerInteractor
     ) {
+        mContext = context;
         mDisplayTracker = displayTracker;
         mRegistry = registry;
         mDumpHandler = dumpHandler;
@@ -1209,7 +1212,12 @@
             boolean showImeSwitcher) {
         if (displayId == INVALID_DISPLAY) return;
 
-        if (mLastUpdatedImeDisplayId != displayId
+        boolean isConcurrentMultiUserModeEnabled = UserManager.isVisibleBackgroundUsersEnabled()
+                && mContext.getResources().getBoolean(android.R.bool.config_perDisplayFocusEnabled)
+                && android.view.inputmethod.Flags.concurrentInputMethods();
+
+        if (!isConcurrentMultiUserModeEnabled
+                && mLastUpdatedImeDisplayId != displayId
                 && mLastUpdatedImeDisplayId != INVALID_DISPLAY) {
             // Set previous NavBar's IME window status as invisible when IME
             // window switched to another display for single-session IME case.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 789a6f4..e08dbb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -308,6 +308,9 @@
         HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
         if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
             headsUpEntry.mRemoteInputActive = remoteInputActive;
+            if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) {
+                headsUpEntry.mRemoteInputActivatedAtLeastOnce = true;
+            }
             if (remoteInputActive) {
                 headsUpEntry.cancelAutoRemovalCallbacks("setRemoteInputActive(true)");
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index a0eb989..6517135 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
 import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.util.ListenerSet;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.settings.GlobalSettings;
@@ -726,6 +727,7 @@
      * of AvalancheController that take it as param.
      */
     public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+        public boolean mRemoteInputActivatedAtLeastOnce;
         public boolean mRemoteInputActive;
         public boolean mUserActionMayIndirectlyRemove;
 
@@ -835,6 +837,15 @@
          */
         public boolean isSticky() {
             if (mEntry == null) return false;
+
+            if (ExpandHeadsUpOnInlineReply.isEnabled()) {
+                // we don't consider pinned and expanded huns as sticky after the remote input
+                // has been activated for them
+                if (!mRemoteInputActive && mRemoteInputActivatedAtLeastOnce) {
+                    return false;
+                }
+            }
+
             return (mEntry.isRowPinned() && mExpanded)
                     || mRemoteInputActive
                     || hasFullScreenIntent(mEntry);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index a714351..14cd202 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -20,8 +20,9 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -66,6 +67,7 @@
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.sysui.ShellInterface;
@@ -249,7 +251,25 @@
                 pip.showPictureInPictureMenu();
             }
         });
+        pip.registerPipTransitionCallback(
+                new PipTransitionController.PipTransitionCallback() {
+                    @Override
+                    public void onPipTransitionStarted(int direction, Rect pipBounds) {
+                        mSysUiState.setFlag(SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING, true)
+                                .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+                    }
 
+                    @Override
+                    public void onPipTransitionFinished(int direction) {
+                        mSysUiState.setFlag(SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING, false)
+                                .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+                    }
+
+                    @Override
+                    public void onPipTransitionCanceled(int direction) {
+                        // No op.
+                    }
+                }, mSysUiMainExecutor);
         mSysUiState.addCallback(sysUiStateFlag -> {
             mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0;
             pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
index 25dd9fe..24e8b18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
@@ -27,7 +27,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.screenshot.ScreenshotController.SaveImageInBackgroundData
 import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
@@ -44,7 +43,7 @@
     private val imageExporter = mock<ImageExporter>()
     private val smartActions = mock<ScreenshotSmartActions>()
     private val smartActionsProvider = mock<ScreenshotNotificationSmartActionsProvider>()
-    private val saveImageData = SaveImageInBackgroundData()
+    private val saveImageData = SaveImageInBackgroundTask.SaveImageInBackgroundData()
     private val testScreenshotId: String = "testScreenshotId"
     private val testBitmap = mock<Bitmap>()
     private val testUser = UserHandle.getUserHandleForUid(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 86c9ab7..967df39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -48,7 +48,12 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -67,13 +72,13 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyFloat
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
 import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @ExperimentalCoroutinesApi
 @RunWith(AndroidTestingRunner::class)
@@ -87,11 +92,11 @@
             testDispatcher = UnconfinedTestDispatcher()
         }
 
-    @Mock private lateinit var communalViewModel: CommunalViewModel
-    @Mock private lateinit var powerManager: PowerManager
-    @Mock private lateinit var touchMonitor: TouchMonitor
-    @Mock private lateinit var communalColors: CommunalColors
-    @Mock private lateinit var communalContent: CommunalContent
+    private var communalViewModel = mock<CommunalViewModel>()
+    private var powerManager = mock<PowerManager>()
+    private var touchMonitor = mock<TouchMonitor>()
+    private var communalColors = mock<CommunalColors>()
+    private var communalContent = mock<CommunalContent>()
     private lateinit var ambientTouchComponentFactory: AmbientTouchComponent.Factory
 
     private lateinit var parentView: FrameLayout
@@ -103,8 +108,6 @@
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
         communalRepository = kosmos.fakeCommunalSceneRepository
 
         ambientTouchComponentFactory =
@@ -124,6 +127,7 @@
                     communalInteractor,
                     communalViewModel,
                     keyguardInteractor,
+                    kosmos.keyguardTransitionInteractor,
                     shadeInteractor,
                     powerManager,
                     communalColors,
@@ -167,6 +171,7 @@
                         communalInteractor,
                         communalViewModel,
                         keyguardInteractor,
+                        kosmos.keyguardTransitionInteractor,
                         shadeInteractor,
                         powerManager,
                         communalColors,
@@ -192,6 +197,7 @@
                     communalInteractor,
                     communalViewModel,
                     keyguardInteractor,
+                    kosmos.keyguardTransitionInteractor,
                     shadeInteractor,
                     powerManager,
                     communalColors,
@@ -212,6 +218,7 @@
                     communalInteractor,
                     communalViewModel,
                     keyguardInteractor,
+                    kosmos.keyguardTransitionInteractor,
                     shadeInteractor,
                     powerManager,
                     communalColors,
@@ -235,12 +242,15 @@
     }
 
     @Test
-    fun lifecycle_resumedAfterCommunalShows() {
-        // Communal is open.
-        goToScene(CommunalScenes.Communal)
+    fun lifecycle_resumedAfterCommunalShows() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is open.
+                goToScene(CommunalScenes.Communal)
 
-        assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
-    }
+                assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
+            }
+        }
 
     @Test
     fun lifecycle_startedAfterCommunalCloses() =
@@ -289,6 +299,43 @@
         }
 
     @Test
+    fun lifecycle_startedWhenEditActivityShowing() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is open.
+                goToScene(CommunalScenes.Communal)
+
+                // Edit activity is showing.
+                communalInteractor.setEditActivityShowing(true)
+                testableLooper.processAllMessages()
+
+                assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
+            }
+        }
+
+    @Test
+    fun lifecycle_startedWhenEditModeTransitionStarted() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is open.
+                goToScene(CommunalScenes.Communal)
+
+                // Leaving edit mode to return to the hub.
+                fakeKeyguardTransitionRepository.sendTransitionStep(
+                    TransitionStep(
+                        from = KeyguardState.GONE,
+                        to = KeyguardState.GLANCEABLE_HUB,
+                        value = 1.0f,
+                        transitionState = TransitionState.RUNNING
+                    )
+                )
+                testableLooper.processAllMessages()
+
+                assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
+            }
+        }
+
+    @Test
     fun lifecycle_createdAfterDisposeView() {
         // Container view disposed.
         underTest.disposeView()
@@ -486,10 +533,10 @@
             testScope.runTest {
                 // Communal is closed.
                 goToScene(CommunalScenes.Blank)
-                `when`(
+                whenever(
                         notificationStackScrollLayoutController.isBelowLastNotification(
-                            anyFloat(),
-                            anyFloat()
+                            any(),
+                            any()
                         )
                     )
                     .thenReturn(false)
@@ -497,6 +544,62 @@
             }
         }
 
+    @Test
+    fun onTouchEvent_hubOpen_touchesDispatched() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is open.
+                goToScene(CommunalScenes.Communal)
+
+                // Touch event is sent to the container view.
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+                verify(containerView).onTouchEvent(any())
+            }
+        }
+
+    @Test
+    fun onTouchEvent_editActivityShowing_touchesConsumedButNotDispatched() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is open.
+                goToScene(CommunalScenes.Communal)
+
+                // Transitioning to or from edit mode.
+                communalInteractor.setEditActivityShowing(true)
+                testableLooper.processAllMessages()
+
+                // onTouchEvent returns true to consume the touch, but it is not sent to the
+                // container view.
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+                verify(containerView, never()).onTouchEvent(any())
+            }
+        }
+
+    @Test
+    fun onTouchEvent_editModeTransitionStarted_touchesConsumedButNotDispatched() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is open.
+                goToScene(CommunalScenes.Communal)
+
+                // Leaving edit mode to return to the hub.
+                fakeKeyguardTransitionRepository.sendTransitionStep(
+                    TransitionStep(
+                        from = KeyguardState.GONE,
+                        to = KeyguardState.GLANCEABLE_HUB,
+                        value = 1.0f,
+                        transitionState = TransitionState.RUNNING
+                    )
+                )
+                testableLooper.processAllMessages()
+
+                // onTouchEvent returns true to consume the touch, but it is not sent to the
+                // container view.
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
+                verify(containerView, never()).onTouchEvent(any())
+            }
+        }
+
     private fun initAndAttachContainerView() {
         val mockInsets =
             mock<WindowInsets> {
@@ -515,8 +618,21 @@
         testableLooper.processAllMessages()
     }
 
-    private fun goToScene(scene: SceneKey) {
+    private suspend fun goToScene(scene: SceneKey) {
         communalRepository.changeScene(scene)
+        if (scene == CommunalScenes.Communal) {
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GLANCEABLE_HUB,
+                kosmos.testScope
+            )
+        } else {
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GLANCEABLE_HUB,
+                to = KeyguardState.LOCKSCREEN,
+                kosmos.testScope
+            )
+        }
         testableLooper.processAllMessages()
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
index 079852a..494f08b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
@@ -38,5 +39,6 @@
             transitionInteractor = keyguardTransitionInteractor,
             powerInteractor = powerInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+            communalSceneInteractor = communalSceneInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
index c216945..7827655 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -37,5 +38,6 @@
             powerInteractor = powerInteractor,
             communalInteractor = communalInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+            communalSceneInteractor = communalSceneInteractor,
         )
     }
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index ad8f5e1..668852b 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -31,12 +31,14 @@
 import android.metrics.LogMaker;
 import android.os.UserManager;
 import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
 import android.service.autofill.InternalSanitizer;
 import android.service.autofill.SaveInfo;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.View;
 import android.view.WindowManager;
@@ -374,4 +376,50 @@
     private interface ViewNodeFilter {
         boolean matches(ViewNode node);
     }
+
+    public static class SaveInfoStats {
+        public int saveInfoCount;
+        public int saveDataTypeCount;
+
+        public SaveInfoStats(int saveInfoCount, int saveDataTypeCount) {
+            this.saveInfoCount = saveInfoCount;
+            this.saveDataTypeCount = saveDataTypeCount;
+        }
+    }
+
+    /**
+     * Get statistic information of save info given a sparse array of fill responses.
+     *
+     * Specifically the statistic includes
+     *   1. how many save info the current session has.
+     *   2. How many distinct save data types current session has.
+     *
+     * @return SaveInfoStats returns the above two number in a SaveInfoStats object
+     */
+    public static SaveInfoStats getSaveInfoStatsFromFillResponses(
+            SparseArray<FillResponse> fillResponses) {
+        if (fillResponses == null) {
+            if (sVerbose) {
+                Slog.v(TAG, "getSaveInfoStatsFromFillResponses(): fillResponse sparse array is "
+                        + "null");
+            }
+            return new SaveInfoStats(-1, -1);
+        }
+        int numSaveInfos = 0;
+        int numSaveDataTypes = 0;
+        ArraySet<Integer> saveDataTypeSeen = new ArraySet<>();
+        final int numResponses = fillResponses.size();
+        for (int responseNum = 0; responseNum < numResponses; responseNum++) {
+            final FillResponse response = fillResponses.valueAt(responseNum);
+            if (response != null && response.getSaveInfo() != null) {
+                numSaveInfos += 1;
+                int saveDataType = response.getSaveInfo().getType();
+                if (!saveDataTypeSeen.contains(saveDataType)) {
+                    saveDataTypeSeen.add(saveDataType);
+                    numSaveDataTypes += 1;
+                }
+            }
+        }
+        return new SaveInfoStats(numSaveInfos, numSaveDataTypes);
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index a5ec2ba..49ca297 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -854,7 +854,6 @@
                 mCallingAppUid,
                 event.mIsCredentialRequest,
                 event.mWebviewRequestedCredential,
-                event.mFilteredFillabaleViewCount,
                 event.mViewFillableTotalCount,
                 event.mViewFillFailureCount,
                 event.mFocusedId,
@@ -868,6 +867,7 @@
                 event.mFocusedVirtualAutofillId,
                 event.mFieldFirstLength,
                 event.mFieldLastLength,
+                event.mFilteredFillabaleViewCount,
                 event.mViewFailedPriorToRefillCount,
                 event.mViewFilledSuccessfullyOnRefillCount,
                 event.mViewFailedOnRefillCount,
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c6ddc16..21df7a5 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -70,6 +70,7 @@
 import static com.android.server.autofill.Helper.containsCharsInOrder;
 import static com.android.server.autofill.Helper.createSanitizers;
 import static com.android.server.autofill.Helper.getNumericValue;
+import static com.android.server.autofill.Helper.SaveInfoStats;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.toArray;
@@ -3203,11 +3204,6 @@
         return saveInfo == null ? 0 : saveInfo.getFlags();
     }
 
-    static class SaveInfoStats {
-        public int saveInfoCount;
-        public int saveDataTypeCount;
-    }
-
     /**
      * Get statistic information of save info in current session. Specifically
      *   1. how many save info the current session has.
@@ -3217,42 +3213,13 @@
      */
     @GuardedBy("mLock")
     private SaveInfoStats getSaveInfoStatsLocked() {
-        SaveInfoStats retSaveInfoStats = new SaveInfoStats();
-        retSaveInfoStats.saveInfoCount = -1;
-        retSaveInfoStats.saveDataTypeCount = -1;
-
         if (mContexts == null) {
             if (sVerbose) {
                 Slog.v(TAG, "getSaveInfoStatsLocked(): mContexts is null");
             }
-        } else if (mResponses == null) {
-            // Happens when the activity / session was finished before the service replied, or
-            // when the service cannot autofill it (and returned a null response).
-            if (sVerbose) {
-                Slog.v(TAG, "getSaveInfoStatsLocked(): mResponses is null");
-            }
-            return retSaveInfoStats;
-        } else {
-            int numSaveInfos = 0;
-            int numSaveDataTypes = 0;
-            ArraySet<Integer> saveDataTypeSeen = new ArraySet<>();
-            final int numResponses = mResponses.size();
-            for (int responseNum = 0; responseNum < numResponses; responseNum++) {
-                final FillResponse response = mResponses.valueAt(responseNum);
-                if (response != null && response.getSaveInfo() != null) {
-                    numSaveInfos += 1;
-                    int saveDataType = response.getSaveInfo().getType();
-                    if (!saveDataTypeSeen.contains(saveDataType)) {
-                        saveDataTypeSeen.add(saveDataType);
-                        numSaveDataTypes += 1;
-                    }
-                }
-            }
-            retSaveInfoStats.saveInfoCount = numSaveInfos;
-            retSaveInfoStats.saveDataTypeCount = numSaveDataTypes;
+            return new SaveInfoStats(-1, -1);
         }
-
-        return retSaveInfoStats;
+        return Helper.getSaveInfoStatsFromFillResponses(mResponses);
     }
 
     /**
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 3d53deb..4fc9d55 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -103,9 +103,10 @@
                     String packageName = getNextArgRequired();
                     String address = getNextArgRequired();
                     String deviceProfile = getNextArg();
+                    boolean selfManaged = getNextBooleanArg();
                     final MacAddress macAddress = MacAddress.fromString(address);
                     mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddress,
-                            deviceProfile, deviceProfile, /* associatedDevice */ null, false,
+                            deviceProfile, deviceProfile, /* associatedDevice */ null, selfManaged,
                             /* callback */ null, /* resultReceiver */ null);
                 }
                 break;
@@ -462,6 +463,17 @@
         }
     }
 
+    private boolean getNextBooleanArg() {
+        String arg = getNextArg();
+        if (arg == null || "false".equalsIgnoreCase(arg)) {
+            return false;
+        } else if ("true".equalsIgnoreCase(arg)) {
+            return Boolean.parseBoolean(arg);
+        } else {
+            throw new IllegalArgumentException("Expected a boolean argument but was: " + arg);
+        }
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -470,7 +482,7 @@
         pw.println("      Print this help text.");
         pw.println("  list USER_ID");
         pw.println("      List all Associations for a user.");
-        pw.println("  associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE]");
+        pw.println("  associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE] [SELF_MANAGED]");
         pw.println("      Create a new Association.");
         pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
         pw.println("      Remove an existing Association.");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 69ee8fc..cf0befa 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16961,16 +16961,24 @@
 
         int userId = UserHandle.getCallingUserId();
 
-        if (UserManager.isVisibleBackgroundUsersEnabled() && userId != getCurrentUserId()) {
-            // The check is added mainly for auto devices. On auto devices, it is possible that
-            // multiple users are visible simultaneously using visible background users.
-            // In such cases, it is desired that only the current user (not the visible background
-            // user) can change the locale and other persistent settings of the device.
-            Slog.w(TAG, "Only current user is allowed to update persistent configuration if "
-                    + "visible background users are enabled. Current User" + getCurrentUserId()
-                    + ". Calling User: " + userId);
-            throw new SecurityException("Only current user is allowed to update persistent "
-                    + "configuration.");
+        if (UserManager.isVisibleBackgroundUsersEnabled()) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                if (userId != getCurrentUserId()) {
+                    // The check is added mainly for auto devices. On auto devices, it is
+                    // possible that multiple users are visible simultaneously using visible
+                    // background users. In such cases, it is desired that only the current user
+                    // (not the visible background user) can change the locale and other persistent
+                    // settings of the device.
+                    Slog.w(TAG, "Only current user is allowed to update persistent configuration "
+                            + "if visible background users are enabled. Current User"
+                            + getCurrentUserId() + ". Calling User: " + userId);
+                    throw new SecurityException("Only current user is allowed to update persistent "
+                            + "configuration.");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
         }
 
         mActivityTaskManager.updatePersistentConfiguration(values, userId);
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 8e8a037..8ec835b 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -21,6 +21,7 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 import static android.hardware.biometrics.BiometricManager.BIOMETRIC_NO_AUTHENTICATION;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE;
 
@@ -41,6 +42,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricStateListener;
 import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -54,8 +56,12 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.biometrics.SensorPropertiesInternal;
 import android.hardware.camera2.CameraManager;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
 import android.hardware.security.keymint.HardwareAuthenticatorType;
 import android.net.Uri;
 import android.os.Binder;
@@ -234,6 +240,8 @@
         private static final boolean DEFAULT_APP_ENABLED = true;
         private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;
         private static final boolean DEFAULT_MANDATORY_BIOMETRICS_STATUS = false;
+        private static final boolean DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS =
+                true;
 
         // Some devices that shipped before S already have face-specific settings. Instead of
         // migrating, which is complicated, let's just keep using the existing settings.
@@ -256,6 +264,8 @@
                 Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_APP_ENABLED);
         private final Uri MANDATORY_BIOMETRICS_ENABLED =
                 Settings.Secure.getUriFor(Settings.Secure.MANDATORY_BIOMETRICS);
+        private final Uri MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED = Settings.Secure.getUriFor(
+                Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED);
 
         private final ContentResolver mContentResolver;
         private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
@@ -264,6 +274,12 @@
         private final Map<Integer, Boolean> mBiometricEnabledForApps = new HashMap<>();
         private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
         private final Map<Integer, Boolean> mMandatoryBiometricsEnabled = new HashMap<>();
+        private final Map<Integer, Boolean> mMandatoryBiometricsRequirementsSatisfied =
+                new HashMap<>();
+        private final Map<Integer, Boolean> mFingerprintEnrolledForUser =
+                new HashMap<>();
+        private final Map<Integer, Boolean> mFaceEnrolledForUser =
+                new HashMap<>();
 
         /**
          * Creates a content observer.
@@ -288,7 +304,13 @@
             mMandatoryBiometricsEnabled.put(context.getUserId(), Settings.Secure.getIntForUser(
                     mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS,
                     DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0, context.getUserId()) != 0);
+            mMandatoryBiometricsRequirementsSatisfied.put(context.getUserId(),
+                    Settings.Secure.getIntForUser(mContentResolver,
+                            Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+                            DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS ? 1 : 0,
+                            context.getUserId()) != 0);
 
+            addBiometricListenersForMandatoryBiometrics(context);
             updateContentObserver();
         }
 
@@ -322,6 +344,10 @@
                     false /* notifyForDescendants */,
                     this /* observer */,
                     UserHandle.USER_ALL);
+            mContentResolver.registerContentObserver(MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+                    false /* notifyForDescendants */,
+                    this /* observer */,
+                    UserHandle.USER_ALL);
         }
 
         @Override
@@ -370,6 +396,13 @@
                         Settings.Secure.MANDATORY_BIOMETRICS,
                         DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0 /* default */,
                         userId) != 0);
+            } else if (MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED.equals(uri)) {
+                mMandatoryBiometricsRequirementsSatisfied.put(userId, Settings.Secure.getIntForUser(
+                        mContentResolver,
+                        Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+                        DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS
+                                ? 1 : 0 /* default */,
+                        userId) != 0);
             }
         }
 
@@ -411,9 +444,15 @@
             }
         }
 
-        public boolean getMandatoryBiometricsEnabledForUser(int userId) {
+        public boolean getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(int userId) {
             return mMandatoryBiometricsEnabled.getOrDefault(userId,
-                    DEFAULT_MANDATORY_BIOMETRICS_STATUS);
+                    DEFAULT_MANDATORY_BIOMETRICS_STATUS)
+                    && mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
+                    DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS)
+                    && mBiometricEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED)
+                    && getEnabledForApps(userId)
+                    && (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
+                    || mFaceEnrolledForUser.getOrDefault(userId, false /* default */));
         }
 
         void notifyEnabledOnKeyguardCallbacks(int userId) {
@@ -424,6 +463,79 @@
                         userId);
             }
         }
+
+        private void addBiometricListenersForMandatoryBiometrics(Context context) {
+            final FingerprintManager fingerprintManager = context.getSystemService(
+                    FingerprintManager.class);
+            final FaceManager faceManager = context.getSystemService(FaceManager.class);
+            if (fingerprintManager != null) {
+                fingerprintManager.addAuthenticatorsRegisteredCallback(
+                        new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+                            @Override
+                            public void onAllAuthenticatorsRegistered(
+                                    List<FingerprintSensorPropertiesInternal> list) {
+                                if (list == null || list.isEmpty()) {
+                                    Slog.d(TAG, "No fingerprint authenticators registered.");
+                                    return;
+                                }
+                                final FingerprintSensorPropertiesInternal
+                                        fingerprintSensorProperties = list.get(0);
+                                if (fingerprintSensorProperties.sensorStrength
+                                        == STRENGTH_STRONG) {
+                                    fingerprintManager.registerBiometricStateListener(
+                                            new BiometricStateListener() {
+                                                @Override
+                                                public void onEnrollmentsChanged(
+                                                        int userId,
+                                                        int sensorId,
+                                                        boolean hasEnrollments
+                                                ) {
+                                                    if (sensorId == fingerprintSensorProperties
+                                                            .sensorId) {
+                                                        mFingerprintEnrolledForUser.put(userId,
+                                                                hasEnrollments);
+                                                    }
+                                                }
+                                            });
+                                }
+                            }
+                        });
+            }
+            if (faceManager != null) {
+                faceManager.addAuthenticatorsRegisteredCallback(
+                        new IFaceAuthenticatorsRegisteredCallback.Stub() {
+                            @Override
+                            public void onAllAuthenticatorsRegistered(
+                                    List<FaceSensorPropertiesInternal> list) {
+                                if (list == null || list.isEmpty()) {
+                                    Slog.d(TAG, "No face authenticators registered.");
+                                    return;
+                                }
+                                final FaceSensorPropertiesInternal
+                                        faceSensorPropertiesInternal = list.get(0);
+                                if (faceSensorPropertiesInternal.sensorStrength
+                                        == STRENGTH_STRONG) {
+                                    faceManager.registerBiometricStateListener(
+                                            new BiometricStateListener() {
+                                                @Override
+                                                public void onEnrollmentsChanged(
+                                                        int userId,
+                                                        int sensorId,
+                                                        boolean hasEnrollments
+                                                ) {
+                                                    if (sensorId
+                                                            == faceSensorPropertiesInternal
+                                                            .sensorId) {
+                                                        mFaceEnrolledForUser.put(userId,
+                                                                hasEnrollments);
+                                                    }
+                                                }
+                                            });
+                                }
+                            }
+                        });
+            }
+        }
     }
 
     final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index b9e6563..0bd22f3 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -112,8 +112,8 @@
                 == BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
 
         if (dropCredentialFallback(promptInfo.getAuthenticators(),
-                settingObserver.getMandatoryBiometricsEnabledForUser(userId),
-                trustManager)) {
+                settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+                        userId), trustManager)) {
             promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
             promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
         }
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 73aa14b..78f7187 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -684,7 +684,8 @@
                 if (clipboard == null) {
                     return null;
                 }
-                showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
+                showAccessNotificationLocked(
+                        pkg, intendingUid, intendingUserId, clipboard, deviceId);
                 notifyTextClassifierLocked(clipboard, pkg, intendingUid);
                 if (clipboard.primaryClip != null) {
                     scheduleAutoClear(userId, intendingUid, intendingDeviceId);
@@ -1438,7 +1439,7 @@
      */
     @GuardedBy("mLock")
     private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,
-            Clipboard clipboard) {
+            Clipboard clipboard, int accessDeviceId) {
         if (clipboard.primaryClip == null) {
             return;
         }
@@ -1477,7 +1478,7 @@
             return;
         }
 
-        final ArraySet<Context> toastContexts = getToastContexts(clipboard);
+        final ArraySet<Context> toastContexts = getToastContexts(clipboard, accessDeviceId);
         Binder.withCleanCallingIdentity(() -> {
             try {
                 CharSequence callingAppLabel = mPm.getApplicationLabel(
@@ -1516,40 +1517,55 @@
      * If the clipboard is for a VirtualDevice, we attempt to return the single DisplayContext for
      * the focused VirtualDisplay for that device, but might need to return the contexts for
      * multiple displays if the VirtualDevice has several but none of them were focused.
+     *
+     * If the clipboard is NOT for a VirtualDevice, but it's being accessed from a VirtualDevice,
+     * this means that the clipboard is shared between the default and that device. In this case we
+     * need to show a toast in both places.
      */
-    private ArraySet<Context> getToastContexts(Clipboard clipboard) throws IllegalStateException {
+    private ArraySet<Context> getToastContexts(Clipboard clipboard, int accessDeviceId)
+            throws IllegalStateException {
         ArraySet<Context> contexts = new ArraySet<>();
-
-        if (mVdmInternal != null && clipboard.deviceId != DEVICE_ID_DEFAULT) {
-            DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
-
-            int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
-            ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(clipboard.deviceId);
-
-            if (displayIds.contains(topFocusedDisplayId)) {
-                Display display = displayManager.getDisplay(topFocusedDisplayId);
-                if (display != null) {
-                    contexts.add(getContext().createDisplayContext(display));
-                    return contexts;
-                }
-            }
-
-            for (int i = 0; i < displayIds.size(); i++) {
-                Display display = displayManager.getDisplay(displayIds.valueAt(i));
-                if (display != null) {
-                    contexts.add(getContext().createDisplayContext(display));
-                }
-            }
-            if (!contexts.isEmpty()) {
-                return contexts;
-            }
-            Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
-                    + clipboard.deviceId);
-            // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
-            // the default display below.
+        if (clipboard.deviceId == DEVICE_ID_DEFAULT || accessDeviceId == DEVICE_ID_DEFAULT) {
+            // Always show the toast on the default display when the default clipboard is accessed -
+            // also when the clipboard is shared with a virtual device and accessed from there.
+            // Same when any clipboard is accessed from the default device.
+            contexts.add(getContext());
         }
 
-        contexts.add(getContext());
+        if ((accessDeviceId == DEVICE_ID_DEFAULT && clipboard.deviceId == DEVICE_ID_DEFAULT)
+                || mVdmInternal == null) {
+            // No virtual devices involved.
+            return contexts;
+        }
+
+        // At this point the clipboard is either accessed from a virtual device, or it is a virtual
+        // device clipboard, so show a toast on the relevant virtual display(s).
+        DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
+        ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(accessDeviceId);
+        int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
+
+        if (displayIds.contains(topFocusedDisplayId)) {
+            Display display = displayManager.getDisplay(topFocusedDisplayId);
+            if (display != null) {
+                contexts.add(getContext().createDisplayContext(display));
+                return contexts;
+            }
+        }
+
+        for (int i = 0; i < displayIds.size(); i++) {
+            Display display = displayManager.getDisplay(displayIds.valueAt(i));
+            if (display != null) {
+                contexts.add(getContext().createDisplayContext(display));
+            }
+        }
+        if (contexts.isEmpty()) {
+            Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
+                    + accessDeviceId);
+            // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
+            // the default display below.
+            contexts.add(getContext());
+        }
+
         return contexts;
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index c82e5be..cc9f630 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -27,6 +27,7 @@
 import android.os.IBinder;
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
 import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
@@ -100,6 +101,18 @@
     public abstract List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId);
 
     /**
+     * Returns the list of installed input methods that are enabled for the specified user.
+     *
+     * @param imiId                           IME ID to be queried about
+     * @param allowsImplicitlyEnabledSubtypes {@code true} to return the implicitly enabled subtypes
+     * @param userId                          the user ID to be queried about
+     * @return a list of {@link InputMethodSubtype} that are enabled for {@code userId}
+     */
+    @NonNull
+    public abstract List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
+            String imiId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId);
+
+    /**
      * Called by the Autofill Frameworks to request an {@link InlineSuggestionsRequest} from
      * the input method.
      *
@@ -312,6 +325,13 @@
                     return Collections.emptyList();
                 }
 
+                @NonNull
+                @Override
+                public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(String imiId,
+                        boolean allowsImplicitlyEnabledSubtypes, int userId) {
+                    return Collections.emptyList();
+                }
+
                 @Override
                 public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
                         InlineSuggestionsRequestInfo requestInfo,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a9e9dcf..e8c1598 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1483,17 +1483,15 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
+        if (!mUserManagerInternal.exists(userId)) {
+            return InputMethodInfoSafeList.empty();
+        }
         synchronized (ImfLock.class) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mCurrentUserId, null);
-            if (resolvedUserIds.length != 1) {
-                return InputMethodInfoSafeList.empty();
-            }
             final int callingUid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
                 return InputMethodInfoSafeList.create(getInputMethodListLocked(
-                        resolvedUserIds[0], directBootAwareness, callingUid));
+                        userId, directBootAwareness, callingUid));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1508,17 +1506,15 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
+        if (!mUserManagerInternal.exists(userId)) {
+            return InputMethodInfoSafeList.empty();
+        }
         synchronized (ImfLock.class) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mCurrentUserId, null);
-            if (resolvedUserIds.length != 1) {
-                return InputMethodInfoSafeList.empty();
-            }
             final int callingUid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
                 return InputMethodInfoSafeList.create(
-                        getEnabledInputMethodListLocked(resolvedUserIds[0], callingUid));
+                        getEnabledInputMethodListLocked(userId, callingUid));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1534,17 +1530,14 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
+        if (!mUserManagerInternal.exists(userId)) {
+            return Collections.emptyList();
+        }
         synchronized (ImfLock.class) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mCurrentUserId, null);
-            if (resolvedUserIds.length != 1) {
-                return Collections.emptyList();
-            }
             final int callingUid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                return getInputMethodListLocked(
-                        resolvedUserIds[0], directBootAwareness, callingUid);
+                return getInputMethodListLocked(userId, directBootAwareness, callingUid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1559,16 +1552,14 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
+        if (!mUserManagerInternal.exists(userId)) {
+            return Collections.emptyList();
+        }
         synchronized (ImfLock.class) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mCurrentUserId, null);
-            if (resolvedUserIds.length != 1) {
-                return Collections.emptyList();
-            }
             final int callingUid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                return getEnabledInputMethodListLocked(resolvedUserIds[0], callingUid);
+                return getEnabledInputMethodListLocked(userId, callingUid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -5874,6 +5865,17 @@
             }
         }
 
+        @NonNull
+        @Override
+        public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
+                String imiId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+            synchronized (ImfLock.class) {
+                return getEnabledInputMethodSubtypeListLocked(imiId,
+                        allowsImplicitlyEnabledSubtypes,
+                        userId, Process.SYSTEM_UID);
+            }
+        }
+
         @Override
         public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
                 InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb) {
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 3d0b079..741513c 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -616,9 +616,10 @@
             LocaleConfig resLocaleConfig = null;
             try {
                 resLocaleConfig = LocaleConfig.fromContextIgnoringOverride(
-                        mContext.createPackageContext(appPackageName, 0));
+                        mContext.createPackageContextAsUser(appPackageName, /* flags= */ 0,
+                                UserHandle.of(userId)));
             } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(TAG, "Unknown package name " + appPackageName);
+                Slog.e(TAG, "Unknown package name " + appPackageName + " for user " + userId);
                 return;
             }
             final File file = getXmlFileNameForUser(appPackageName, userId);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 7de1045..3f4a9bb 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -297,7 +297,10 @@
         }
 
         public boolean isExpired() {
-            return mTimestamp + ContextHubTransactionManager.RELIABLE_MESSAGE_TIMEOUT.toNanos()
+            return mTimestamp
+                            + ContextHubTransactionManager
+                                    .RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT
+                                    .toNanos()
                     < SystemClock.elapsedRealtimeNanos();
         }
     }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index e6d330f8..cd69eba 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -56,6 +56,9 @@
 
     public static final Duration RELIABLE_MESSAGE_TIMEOUT = Duration.ofSeconds(1);
 
+    public static final Duration RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT =
+            RELIABLE_MESSAGE_TIMEOUT.multipliedBy(3);
+
     private static final int MAX_PENDING_REQUESTS = 10000;
 
     private static final int RELIABLE_MESSAGE_MAX_NUM_RETRY = 3;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 803b125..621c090 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -702,7 +702,7 @@
         }
     }
 
-    private final class BinderService extends IMediaProjectionManager.Stub {
+    final class BinderService extends IMediaProjectionManager.Stub {
 
         BinderService(Context context) {
             super(PermissionEnforcer.fromContext(context));
@@ -891,6 +891,13 @@
         @Override
         public void requestConsentForInvalidProjection(@NonNull IMediaProjection projection) {
             requestConsentForInvalidProjection_enforcePermission();
+
+            if (android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()
+                    && mKeyguardManager.isKeyguardLocked()) {
+                Slog.v(TAG, "Reusing token: Won't request consent while the keyguard is locked");
+                return;
+            }
+
             synchronized (mLock) {
                 if (!isCurrentProjection(projection)) {
                     Slog.v(TAG, "Reusing token: Won't request consent again for a token that "
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index 5b91118..74f0d9c 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -831,66 +831,6 @@
     }
 
     @Override
-    public boolean getIpForwardingEnabled() throws IllegalStateException{
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#getIpForwardingEnabled not supported in V+");
-    }
-
-    @Override
-    public void setIpForwardingEnabled(boolean enable) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#setIpForwardingEnabled not supported in V+");
-    }
-
-    @Override
-    public void startTethering(String[] dhcpRange) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#startTethering not supported in V+");
-    }
-
-    @Override
-    public void stopTethering() {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#stopTethering not supported in V+");
-    }
-
-    @Override
-    public boolean isTetheringStarted() {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#isTetheringStarted not supported in V+");
-    }
-
-    @Override
-    public void tetherInterface(String iface) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#tetherInterface not supported in V+");
-    }
-
-    @Override
-    public void untetherInterface(String iface) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#untetherInterface not supported in V+");
-    }
-
-    @Override
-    public String[] listTetheredInterfaces() {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#listTetheredInterfaces not supported in V+");
-    }
-
-    @Override
-    public void enableNat(String internalInterface, String externalInterface) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#enableNat not supported in V+");
-    }
-
-    @Override
-    public void disableNat(String internalInterface, String externalInterface) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        throw new UnsupportedOperationException("NMS#disableNat not supported in V+");
-    }
-
-    @Override
     public void setInterfaceQuota(String iface, long quotaBytes) {
         PermissionUtils.enforceNetworkStackPermission(mContext);
 
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index fde23b7..9b64488 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.policy;
 
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.role.RoleManager;
 import android.content.ActivityNotFoundException;
@@ -248,31 +249,7 @@
                                 + " className=" + className + " shortcutChar=" + shortcutChar);
                         continue;
                     }
-                    ComponentName componentName = new ComponentName(packageName, className);
-                    try {
-                        mPackageManager.getActivityInfo(componentName,
-                                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                        | PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                    } catch (PackageManager.NameNotFoundException e) {
-                        String[] packages = mPackageManager.canonicalToCurrentPackageNames(
-                                new String[] { packageName });
-                        componentName = new ComponentName(packages[0], className);
-                        try {
-                            mPackageManager.getActivityInfo(componentName,
-                                    PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                            | PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                        } catch (PackageManager.NameNotFoundException e1) {
-                            Log.w(TAG, "Unable to add bookmark: " + packageName
-                                    + "/" + className + " not found.");
-                            continue;
-                        }
-                    }
-
-                    intent = new Intent(Intent.ACTION_MAIN);
-                    intent.addCategory(Intent.CATEGORY_LAUNCHER);
-                    intent.setComponent(componentName);
+                    intent = resolveComponentNameIntent(packageName, className);
                 } else if (categoryName != null) {
                     if (roleName != null) {
                         Log.w(TAG, "Cannot specify role bookmark when category is present for"
@@ -310,6 +287,32 @@
         }
     }
 
+    @Nullable
+    private Intent resolveComponentNameIntent(String packageName, String className) {
+        int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+        ComponentName componentName = new ComponentName(packageName, className);
+        try {
+            mPackageManager.getActivityInfo(componentName, flags);
+        } catch (PackageManager.NameNotFoundException e) {
+            String[] packages = mPackageManager.canonicalToCurrentPackageNames(
+                    new String[] { packageName });
+            componentName = new ComponentName(packages[0], className);
+            try {
+                mPackageManager.getActivityInfo(componentName, flags);
+            } catch (PackageManager.NameNotFoundException e1) {
+                Log.w(TAG, "Unable to add bookmark: " + packageName
+                        + "/" + className + " not found.");
+                return null;
+            }
+        }
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.setComponent(componentName);
+        return intent;
+    }
+
     void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
             throws RemoteException {
         IShortcutService service = mShortcutKeyServices.get(shortcutCode);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8419a60..df97313 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2402,7 +2402,7 @@
                     PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         }
 
-        mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
+        final var transitionListener = new AppTransitionListener(DEFAULT_DISPLAY) {
             @Override
             public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
                     long statusBarAnimationDuration) {
@@ -2436,7 +2436,8 @@
                     mLockAfterDreamingTransitionFinished = false;
                 }
             }
-        });
+        };
+        mWindowManagerInternal.registerAppTransitionListener(transitionListener);
 
         mKeyguardDrawnTimeout = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_keyguardDrawnTimeout);
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 1b7bf89..4052a64 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -16356,6 +16356,7 @@
         mBluetoothPowerStatsCollector.collectAndDump(pw);
         mCameraPowerStatsCollector.collectAndDump(pw);
         mGnssPowerStatsCollector.collectAndDump(pw);
+        mCustomEnergyConsumerPowerStatsCollector.collectAndDump(pw);
     }
 
     private final Runnable mWriteAsyncRunnable = () -> {
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
index 1191901..0273ba6 100644
--- a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
@@ -19,6 +19,7 @@
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -65,4 +66,12 @@
         }
         return success;
     }
+
+    @Override
+    public void collectAndDump(PrintWriter pw) {
+        ensureInitialized();
+        for (int i = 0; i < mCollectors.size(); i++) {
+            mCollectors.get(i).collectAndDump(pw);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
index cace941..ce11fa0 100644
--- a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
@@ -176,9 +176,12 @@
 
                 for (EnergyConsumerAttribution attribution : perUid) {
                     int uid = mUidResolver.mapUid(attribution.uid);
-                    long lastEnergy = mLastConsumerEnergyPerUid.get(uid);
-                    long deltaEnergy = attribution.energyUWs - lastEnergy;
+                    long lastEnergy = mLastConsumerEnergyPerUid.get(uid, ENERGY_UNSPECIFIED);
                     mLastConsumerEnergyPerUid.put(uid, attribution.energyUWs);
+                    if (lastEnergy == ENERGY_UNSPECIFIED) {
+                        continue;
+                    }
+                    long deltaEnergy = attribution.energyUWs - lastEnergy;
                     if (deltaEnergy <= 0) {
                         continue;
                     }
@@ -189,7 +192,8 @@
                     }
 
                     mLayout.setUidConsumedEnergy(uidStats, 0,
-                            mLayout.getUidConsumedEnergy(uidStats, 0) + deltaEnergy);
+                            mLayout.getUidConsumedEnergy(uidStats, 0)
+                                    + uJtoUc(deltaEnergy, averageVoltage));
                 }
             }
         }
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index d9f6c1f..f5b0005 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -196,12 +196,11 @@
         }
 
         IndentingPrintWriter out = new IndentingPrintWriter(pw);
-        out.print(getClass().getSimpleName());
         if (!isEnabled()) {
+            out.print(getClass().getSimpleName());
             out.println(": disabled");
             return;
         }
-        out.println();
 
         ArrayList<PowerStats> collected = new ArrayList<>();
         Consumer<PowerStats> consumer = collected::add;
@@ -215,11 +214,9 @@
             removeConsumer(consumer);
         }
 
-        out.increaseIndent();
         for (PowerStats stats : collected) {
             stats.dump(out);
         }
-        out.decreaseIndent();
     }
 
     private void awaitCompletion() {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 400919a..516fc65 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -53,7 +53,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.isFloating;
 import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
 import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
 import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -336,7 +335,6 @@
 import android.service.dreams.DreamActivity;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArraySet;
-import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.MergedConfiguration;
@@ -8648,7 +8646,14 @@
             resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
         }
 
-        applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
+        applySizeOverrideIfNeeded(
+                mDisplayContent,
+                info.applicationInfo,
+                newParentConfiguration,
+                resolvedConfig,
+                mOptOutEdgeToEdge,
+                hasFixedRotationTransform(),
+                getCompatDisplayInsets() != null);
         mResolveConfigHint.resetTmpOverrides();
 
         logAppCompatState();
@@ -8658,100 +8663,6 @@
         return Rect.copyOrNull(mResolveConfigHint.mParentAppBoundsOverride);
     }
 
-    /**
-     * If necessary, override configuration fields related to app bounds.
-     * This will happen when the app is targeting SDK earlier than 35.
-     * The insets and configuration has decoupled since SDK level 35, to make the system
-     * compatible to existing apps, override the configuration with legacy metrics. In legacy
-     * metrics, fields such as appBounds will exclude some of the system bar areas.
-     * The override contains all potentially affected fields in Configuration, including
-     * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
-     * All overrides to those fields should be in this method.
-     *
-     * TODO: Consider integrate this with computeConfigByResolveHint()
-     */
-    private void applySizeOverrideIfNeeded(Configuration newParentConfiguration,
-            int parentWindowingMode, Configuration inOutConfig) {
-        if (mDisplayContent == null) {
-            return;
-        }
-        final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
-        int rotation = newParentConfiguration.windowConfiguration.getRotation();
-        if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming()) {
-            rotation = mDisplayContent.getRotation();
-        }
-        if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig
-                || getCompatDisplayInsets() != null
-                || (isFloating(parentWindowingMode)
-                        // Check the requested windowing mode of activity as well in case it is
-                        // switching between PiP and fullscreen.
-                        && (inOutConfig.windowConfiguration.getWindowingMode()
-                                == WINDOWING_MODE_UNDEFINED
-                                || isFloating(inOutConfig.windowConfiguration.getWindowingMode())))
-                || rotation == ROTATION_UNDEFINED)) {
-            // If the insets configuration decoupled logic is not enabled for the app, or the app
-            // already has a compat override, or the context doesn't contain enough info to
-            // calculate the override, skip the override.
-            return;
-        }
-        // Make sure the orientation related fields will be updated by the override insets, because
-        // fixed rotation has assigned the fields from display's configuration.
-        if (hasFixedRotationTransform()) {
-            inOutConfig.windowConfiguration.setAppBounds(null);
-            inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-            inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
-            inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
-            inOutConfig.orientation = ORIENTATION_UNDEFINED;
-        }
-
-        // Override starts here.
-        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final int dw = rotated ? mDisplayContent.mBaseDisplayHeight
-                : mDisplayContent.mBaseDisplayWidth;
-        final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
-                : mDisplayContent.mBaseDisplayHeight;
-        final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
-                .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets;
-        // This should be the only place override the configuration for ActivityRecord. Override
-        // the value if not calculated yet.
-        Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-        if (outAppBounds == null || outAppBounds.isEmpty()) {
-            inOutConfig.windowConfiguration.setAppBounds(parentBounds);
-            outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-            outAppBounds.inset(nonDecorInsets);
-        }
-        float density = inOutConfig.densityDpi;
-        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
-            density = newParentConfiguration.densityDpi;
-        }
-        density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
-        if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
-            inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
-        }
-        if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-            inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
-        }
-        if (inOutConfig.smallestScreenWidthDp
-                == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
-                && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
-            // For the case of PIP transition and multi-window environment, the
-            // smallestScreenWidthDp is handled already. Override only if the app is in
-            // fullscreen.
-            final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
-            mDisplayContent.computeSizeRanges(info, rotated, dw, dh,
-                    mDisplayContent.getDisplayMetrics().density,
-                    inOutConfig, true /* overrideConfig */);
-        }
-
-        // It's possible that screen size will be considered in different orientation with or
-        // without considering the system bar insets. Override orientation as well.
-        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
-            inOutConfig.orientation =
-                    (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
-                            ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
-        }
-    }
-
     private void computeConfigByResolveHint(@NonNull Configuration resolvedConfig,
             @NonNull Configuration parentConfig) {
         task.computeConfigResourceOverrides(resolvedConfig, parentConfig, mResolveConfigHint);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 8421765..0f8d68b 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -166,15 +166,14 @@
                 return null;
             }
 
-            // Move focus to the top embedded window if possible
-            if (mWindowManagerService.moveFocusToAdjacentEmbeddedWindow(window)) {
-                window = wmService.getFocusedWindowLocked();
-                if (window == null) {
-                    Slog.e(TAG, "New focused window is null, returning null.");
-                    return null;
-                }
+            // Updating the window to the most recently used one among the embedded windows
+            // that are displayed adjacently, unless the IME is visible.
+            // When the IME is visible, the IME is displayed on top of embedded activities.
+            // In that case, the back event should still be delivered to focused activity in
+            // order to dismiss the IME.
+            if (!window.getDisplayContent().getImeContainer().isVisible()) {
+                window = mWindowManagerService.getMostRecentUsedEmbeddedWindowForBack(window);
             }
-
             if (!window.isDrawn()) {
                 ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
                         "Focused window didn't have a valid surface drawn.");
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index efd5202..3ebaf03 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -22,14 +22,23 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isFloating;
 import static android.app.WindowConfiguration.windowingModeToString;
 import static android.app.WindowConfigurationProto.WINDOWING_MODE;
 import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 
 import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
 import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
@@ -38,11 +47,14 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.app.WindowConfiguration;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.LocaleList;
+import android.util.DisplayMetrics;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -173,6 +185,110 @@
         mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
     }
 
+    /**
+     * If necessary, override configuration fields related to app bounds.
+     * This will happen when the app is targeting SDK earlier than 35.
+     * The insets and configuration has decoupled since SDK level 35, to make the system
+     * compatible to existing apps, override the configuration with legacy metrics. In legacy
+     * metrics, fields such as appBounds will exclude some of the system bar areas.
+     * The override contains all potentially affected fields in Configuration, including
+     * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
+     * All overrides to those fields should be in this method.
+     *
+     * TODO: Consider integrate this with computeConfigByResolveHint()
+     */
+    static void applySizeOverrideIfNeeded(DisplayContent displayContent, ApplicationInfo appInfo,
+            Configuration newParentConfiguration, Configuration inOutConfig,
+            boolean optsOutEdgeToEdge, boolean hasFixedRotationTransform,
+            boolean hasCompatDisplayInsets) {
+        if (displayContent == null) {
+            return;
+        }
+        final boolean useOverrideInsetsForConfig =
+                displayContent.mWmService.mFlags.mInsetsDecoupledConfiguration
+                        ? !appInfo.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
+                                && !appInfo.isChangeEnabled(
+                                        OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION)
+                        : appInfo.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
+        final int parentWindowingMode =
+                newParentConfiguration.windowConfiguration.getWindowingMode();
+        final boolean isFloating = isFloating(parentWindowingMode)
+                // Check the requested windowing mode of activity as well in case it is
+                // switching between PiP and fullscreen.
+                && (inOutConfig.windowConfiguration.getWindowingMode() == WINDOWING_MODE_UNDEFINED
+                        || isFloating(inOutConfig.windowConfiguration.getWindowingMode()));
+        int rotation = newParentConfiguration.windowConfiguration.getRotation();
+        if (rotation == ROTATION_UNDEFINED && !hasFixedRotationTransform) {
+            rotation = displayContent.getRotation();
+        }
+        if (!optsOutEdgeToEdge && (!useOverrideInsetsForConfig
+                || hasCompatDisplayInsets
+                || isFloating
+                || rotation == ROTATION_UNDEFINED)) {
+            // If the insets configuration decoupled logic is not enabled for the app, or the app
+            // already has a compat override, or the context doesn't contain enough info to
+            // calculate the override, skip the override.
+            return;
+        }
+        // Make sure the orientation related fields will be updated by the override insets, because
+        // fixed rotation has assigned the fields from display's configuration.
+        if (hasFixedRotationTransform) {
+            inOutConfig.windowConfiguration.setAppBounds(null);
+            inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+            inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+            inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+            inOutConfig.orientation = ORIENTATION_UNDEFINED;
+        }
+
+        // Override starts here.
+        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+        final int dw = rotated
+                ? displayContent.mBaseDisplayHeight
+                : displayContent.mBaseDisplayWidth;
+        final int dh = rotated
+                ? displayContent.mBaseDisplayWidth
+                : displayContent.mBaseDisplayHeight;
+        // This should be the only place override the configuration for ActivityRecord. Override
+        // the value if not calculated yet.
+        Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+        if (outAppBounds == null || outAppBounds.isEmpty()) {
+            inOutConfig.windowConfiguration.setAppBounds(
+                    newParentConfiguration.windowConfiguration.getBounds());
+            outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+            outAppBounds.inset(displayContent.getDisplayPolicy()
+                    .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets);
+        }
+        float density = inOutConfig.densityDpi;
+        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+            density = newParentConfiguration.densityDpi;
+        }
+        density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+        if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+            inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
+        }
+        if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+            inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
+        }
+        if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
+                && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+            // For the case of PIP transition and multi-window environment, the
+            // smallestScreenWidthDp is handled already. Override only if the app is in
+            // fullscreen.
+            final DisplayInfo info = new DisplayInfo(displayContent.getDisplayInfo());
+            displayContent.computeSizeRanges(info, rotated, dw, dh,
+                    displayContent.getDisplayMetrics().density,
+                    inOutConfig, true /* overrideConfig */);
+        }
+
+        // It's possible that screen size will be considered in different orientation with or
+        // without considering the system bar insets. Override orientation as well.
+        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
+            inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+                    ? ORIENTATION_PORTRAIT
+                    : ORIENTATION_LANDSCAPE;
+        }
+    }
+
     /** Returns {@code true} if requested override override configuration is not empty. */
     boolean hasRequestedOverrideConfiguration() {
         return mHasOverrideConfiguration;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9371149..fa60368 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3895,6 +3895,22 @@
     }
 
     /**
+     * Returns the focused window of the given Activity if the Activity is focused.
+     */
+    WindowState findFocusedWindow(ActivityRecord activityRecord) {
+        final ActivityRecord tmpApp = mFocusedApp;
+        mTmpWindow = null;
+        try {
+            mFocusedApp = activityRecord;
+            // mFindFocusedWindow will populate mTmpWindow with the new focused window when found.
+            activityRecord.forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);
+        } finally {
+            mFocusedApp = tmpApp;
+        }
+        return mTmpWindow;
+    }
+
+    /**
      * Update the focused window and make some adjustments if the focus has changed.
      *
      * @param mode Indicates the situation we are in. Possible modes are:
@@ -6911,6 +6927,10 @@
         /** Whether {@link #mAnimatingRecents} is going to be the top activity. */
         private boolean mRecentsWillBeTop;
 
+        FixedRotationTransitionListener() {
+            super(DisplayContent.this.mDisplayId);
+        }
+
         /**
          * If the recents activity has a fixed orientation which is different from the current top
          * activity, it will be rotated before being shown so we avoid a screen rotation animation
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 80362a4..c3339cd 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -588,7 +588,7 @@
                     gesturesPointerEventCallbacks);
             displayContent.registerPointerEventListener(mSystemGestures);
         }
-        mAppTransitionListener = new WindowManagerInternal.AppTransitionListener() {
+        mAppTransitionListener = new WindowManagerInternal.AppTransitionListener(displayId) {
 
             private Runnable mAppTransitionPending = () -> {
                 StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 63fe94c..e50a089 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -44,6 +44,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
 import com.android.server.UiThread;
+import com.android.window.flags.Flags;
 
 /**
  * Controls camera compatibility treatment that handles orientation mismatch between camera
@@ -69,6 +70,9 @@
     @NonNull
     private final ActivityRefresher mActivityRefresher;
 
+    @Nullable
+    private Task mCameraTask;
+
     @ScreenOrientation
     private int mLastReportedOrientation = SCREEN_ORIENTATION_UNSET;
 
@@ -104,7 +108,7 @@
      * guaranteed to match, the rotation can cause letterboxing.
      *
      * <p>If treatment isn't applicable returns {@link SCREEN_ORIENTATION_UNSPECIFIED}. See {@link
-     * #shouldComputeCameraCompatOrientation} for conditions enabling the treatment.
+     * #isTreatmentEnabledForDisplay} for conditions enabling the treatment.
      */
     @ScreenOrientation
     int getOrientation() {
@@ -136,9 +140,9 @@
         // are aligned when they compute orientation of the preview.
         // This means that even for a landscape-only activity and a device with landscape natural
         // orientation this would return SCREEN_ORIENTATION_PORTRAIT because an assumption that
-        // natural orientation = portrait window = portait camera is the main wrong assumption
+        // natural orientation = portrait window = portrait camera is the main wrong assumption
         // that apps make when they implement camera previews so landscape windows need be
-        // rotated in the orientation oposite to the natural one even if it's portrait.
+        // rotated in the orientation opposite to the natural one even if it's portrait.
         // TODO(b/261475895): Consider allowing more rotations for "sensor" and "user" versions
         // of the portrait and landscape orientation requests.
         final int orientation = (isPortraitActivity && isNaturalDisplayOrientationPortrait)
@@ -296,6 +300,7 @@
     @Override
     public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
             @NonNull String cameraId) {
+        mCameraTask = cameraActivity.getTask();
         // Checking whether an activity in fullscreen rather than the task as this camera
         // compat treatment doesn't cover activity embedding.
         if (cameraActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
@@ -305,7 +310,7 @@
         }
         // Checking that the whole app is in multi-window mode as we shouldn't show toast
         // for the activity embedding case.
-        if (cameraActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
+        if (mCameraTask != null && mCameraTask.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
                 && isTreatmentEnabledForActivity(
                 cameraActivity, /* mustBeFullscreen */ false)) {
             final PackageManager packageManager = mWmService.mContext.getPackageManager();
@@ -343,10 +348,15 @@
 
     @Override
     public boolean onCameraClosed(@NonNull String cameraId) {
-        // Top activity in the same task as the camera activity, or `null` if the task is
-        // closed.
-        final ActivityRecord topActivity = mDisplayContent.topRunningActivity(
-                /* considerKeyguardState= */ true);
+        final ActivityRecord topActivity;
+        if (Flags.cameraCompatFullscreenPickSameTaskActivity()) {
+            topActivity = mCameraTask != null ? mCameraTask.getTopActivity(
+                    /* includeFinishing= */ true, /* includeOverlays= */ false) : null;
+        } else {
+            topActivity = mDisplayContent.topRunningActivity(/* considerKeyguardState= */ true);
+        }
+
+        mCameraTask = null;
         if (topActivity == null) {
             return true;
         }
@@ -368,8 +378,6 @@
                 mDisplayContent.mDisplayId);
         // Checking whether an activity in fullscreen rather than the task as this camera compat
         // treatment doesn't cover activity embedding.
-        // TODO(b/350495350): Consider checking whether this activity is the camera activity, or
-        // whether the top activity has the same task as the one which opened camera.
         if (topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index c26684f..cc95518 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -39,7 +39,6 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.NonNull;
@@ -537,27 +536,11 @@
 
     @Override
     public boolean startMovingTask(IWindow window, float startX, float startY) {
-        if (DEBUG_TASK_POSITIONING) Slog.d(
-                TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        return false;
     }
 
     @Override
     public void finishMovingTask(IWindow window) {
-        if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishMovingTask");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mService.mTaskPositioningController.finishTaskPositioning(window);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
deleted file mode 100644
index 972dd2e..0000000
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
-import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
-import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
-
-import static java.util.concurrent.CompletableFuture.completedFuture;
-
-import android.annotation.NonNull;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.InputConfig;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.util.DisplayMetrics;
-import android.util.Slog;
-import android.view.BatchedInputEventReceiver;
-import android.view.InputApplicationHandle;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.InputWindowHandle;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.TaskResizingAlgorithm;
-import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
-import com.android.internal.protolog.ProtoLog;
-
-import java.util.concurrent.CompletableFuture;
-
-class TaskPositioner implements IBinder.DeathRecipient {
-    private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
-    private static final String TAG_LOCAL = "TaskPositioner";
-    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
-
-    private static Factory sFactory;
-
-    public static final float RESIZING_HINT_ALPHA = 0.5f;
-
-    public static final int RESIZING_HINT_DURATION_MS = 0;
-
-    private final WindowManagerService mService;
-    private InputEventReceiver mInputEventReceiver;
-    private DisplayContent mDisplayContent;
-    private Rect mTmpRect = new Rect();
-    private int mMinVisibleWidth;
-    private int mMinVisibleHeight;
-
-    @VisibleForTesting
-    Task mTask;
-    WindowState mWindow;
-    private boolean mResizing;
-    private boolean mPreserveOrientation;
-    private boolean mStartOrientationWasLandscape;
-    private final Rect mWindowOriginalBounds = new Rect();
-    private final Rect mWindowDragBounds = new Rect();
-    private final Point mMaxVisibleSize = new Point();
-    private float mStartDragX;
-    private float mStartDragY;
-    @CtrlType
-    private int mCtrlType = CTRL_NONE;
-    @VisibleForTesting
-    boolean mDragEnded;
-    IBinder mClientCallback;
-
-    InputChannel mClientChannel;
-    InputApplicationHandle mDragApplicationHandle;
-    InputWindowHandle mDragWindowHandle;
-
-    /** Use {@link #create(WindowManagerService)} instead. */
-    @VisibleForTesting
-    TaskPositioner(WindowManagerService service) {
-        mService = service;
-    }
-
-    private boolean onInputEvent(InputEvent event) {
-        // All returns need to be in the try block to make sure the finishInputEvent is
-        // called correctly.
-        if (!(event instanceof MotionEvent)
-                || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
-            return false;
-        }
-        final MotionEvent motionEvent = (MotionEvent) event;
-        if (mDragEnded) {
-            // The drag has ended but the clean-up message has not been processed by
-            // window manager. Drop events that occur after this until window manager
-            // has a chance to clean-up the input handle.
-            return true;
-        }
-
-        final float newX = motionEvent.getRawX();
-        final float newY = motionEvent.getRawY();
-
-        switch (motionEvent.getAction()) {
-            case MotionEvent.ACTION_DOWN: {
-                if (DEBUG_TASK_POSITIONING) {
-                    Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
-                }
-            }
-            break;
-
-            case MotionEvent.ACTION_MOVE: {
-                if (DEBUG_TASK_POSITIONING) {
-                    Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
-                }
-                synchronized (mService.mGlobalLock) {
-                    mDragEnded = notifyMoveLocked(newX, newY);
-                    mTask.getDimBounds(mTmpRect);
-                }
-                if (!mTmpRect.equals(mWindowDragBounds)) {
-                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
-                            "wm.TaskPositioner.resizeTask");
-                    mService.mAtmService.resizeTask(
-                            mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
-                    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-                }
-            }
-            break;
-
-            case MotionEvent.ACTION_UP: {
-                if (DEBUG_TASK_POSITIONING) {
-                    Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
-                }
-                mDragEnded = true;
-            }
-            break;
-
-            case MotionEvent.ACTION_CANCEL: {
-                if (DEBUG_TASK_POSITIONING) {
-                    Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
-                }
-                mDragEnded = true;
-            }
-            break;
-        }
-
-        if (mDragEnded) {
-            final boolean wasResizing = mResizing;
-            synchronized (mService.mGlobalLock) {
-                endDragLocked();
-                mTask.getDimBounds(mTmpRect);
-            }
-            if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
-                // We were using fullscreen surface during resizing. Request
-                // resizeTask() one last time to restore surface to window size.
-                mService.mAtmService.resizeTask(
-                        mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
-            }
-
-            // Post back to WM to handle clean-ups. We still need the input
-            // event handler for the last finishInputEvent()!
-            mService.mTaskPositioningController.finishTaskPositioning();
-        }
-        return true;
-    }
-
-    @VisibleForTesting
-    Rect getWindowDragBounds() {
-        return mWindowDragBounds;
-    }
-
-    /**
-     * @param displayContent The Display that the window being dragged is on.
-     * @param win The window which will be dragged.
-     */
-    CompletableFuture<Void> register(DisplayContent displayContent, @NonNull WindowState win) {
-        if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "Registering task positioner");
-        }
-
-        if (mClientChannel != null) {
-            Slog.e(TAG, "Task positioner already registered");
-            return completedFuture(null);
-        }
-
-        mDisplayContent = displayContent;
-        mClientChannel = mService.mInputManager.createInputChannel(TAG);
-
-        mInputEventReceiver = new BatchedInputEventReceiver.SimpleBatchedInputEventReceiver(
-                mClientChannel, mService.mAnimationHandler.getLooper(),
-                mService.mAnimator.getChoreographer(), this::onInputEvent);
-
-        mDragApplicationHandle = new InputApplicationHandle(new Binder(), TAG,
-                DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
-
-        mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
-                displayContent.getDisplayId());
-        mDragWindowHandle.name = TAG;
-        mDragWindowHandle.token = mClientChannel.getToken();
-        mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
-        mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-        mDragWindowHandle.ownerPid = WindowManagerService.MY_PID;
-        mDragWindowHandle.ownerUid = WindowManagerService.MY_UID;
-        mDragWindowHandle.scaleFactor = 1.0f;
-        // When dragging the window around, we do not want to steal focus for the window.
-        mDragWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
-
-        // The drag window cannot receive new touches.
-        mDragWindowHandle.touchableRegion.setEmpty();
-
-        // Pause rotations before a drag.
-        ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during re-position");
-        mDisplayContent.getDisplayRotation().pause();
-
-        // Notify InputMonitor to take mDragWindowHandle.
-        return mService.mTaskPositioningController.showInputSurface(win.getDisplayId())
-            .thenRun(() -> {
-                // The global lock is held by the callers of register but released before the async
-                // results are waited on. We must acquire the lock in this callback to ensure thread
-                // safety.
-                synchronized (mService.mGlobalLock) {
-                    final Rect displayBounds = mTmpRect;
-                    displayContent.getBounds(displayBounds);
-                    final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
-                    mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
-                    mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
-                    mMaxVisibleSize.set(displayBounds.width(), displayBounds.height());
-
-                    mDragEnded = false;
-
-                    try {
-                        mClientCallback = win.mClient.asBinder();
-                        mClientCallback.linkToDeath(this, 0 /* flags */);
-                    } catch (RemoteException e) {
-                        // The caller has died, so clean up TaskPositioningController.
-                        mService.mTaskPositioningController.finishTaskPositioning();
-                        return;
-                    }
-                    mWindow = win;
-                    mTask = win.getTask();
-                }
-            });
-    }
-
-    void unregister() {
-        if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "Unregistering task positioner");
-        }
-
-        if (mClientChannel == null) {
-            Slog.e(TAG, "Task positioner not registered");
-            return;
-        }
-
-        mService.mTaskPositioningController.hideInputSurface(mDisplayContent.getDisplayId());
-        mService.mInputManager.removeInputChannel(mClientChannel.getToken());
-
-        mInputEventReceiver.dispose();
-        mInputEventReceiver = null;
-        mClientChannel.dispose();
-        mClientChannel = null;
-
-        mDragWindowHandle = null;
-        mDragApplicationHandle = null;
-        mDragEnded = true;
-
-        // Notify InputMonitor to remove mDragWindowHandle.
-        mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
-
-        // Resume rotations after a drag.
-        ProtoLog.d(WM_DEBUG_ORIENTATION, "Resuming rotation after re-position");
-        mDisplayContent.getDisplayRotation().resume();
-        mDisplayContent = null;
-        if (mClientCallback != null) {
-            mClientCallback.unlinkToDeath(this, 0 /* flags */);
-        }
-        mWindow = null;
-    }
-
-    /**
-     * Starts moving or resizing the task. This method should be only called from
-     * {@link TaskPositioningController#startPositioningLocked} or unit tests.
-     */
-    void startDrag(boolean resize, boolean preserveOrientation, float startX, float startY) {
-        if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "startDrag: win=" + mWindow + ", resize=" + resize
-                    + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
-                    + startY + "}");
-        }
-        // Use the bounds of the task which accounts for
-        // multiple app windows. Don't use any bounds from win itself as it
-        // may not be the same size as the task.
-        final Rect startBounds = mTmpRect;
-        mTask.getBounds(startBounds);
-
-        mCtrlType = CTRL_NONE;
-        mStartDragX = startX;
-        mStartDragY = startY;
-        mPreserveOrientation = preserveOrientation;
-
-        if (resize) {
-            if (startX < startBounds.left) {
-                mCtrlType |= CTRL_LEFT;
-            }
-            if (startX > startBounds.right) {
-                mCtrlType |= CTRL_RIGHT;
-            }
-            if (startY < startBounds.top) {
-                mCtrlType |= CTRL_TOP;
-            }
-            if (startY > startBounds.bottom) {
-                mCtrlType |= CTRL_BOTTOM;
-            }
-            mResizing = mCtrlType != CTRL_NONE;
-        }
-
-        // In case of !isDockedInEffect we are using the union of all task bounds. These might be
-        // made up out of multiple windows which are only partially overlapping. When that happens,
-        // the orientation from the window of interest to the entire stack might diverge. However
-        // for now we treat them as the same.
-        mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
-        mWindowOriginalBounds.set(startBounds);
-
-        // Notify the app that resizing has started, even though we haven't received any new
-        // bounds yet. This will guarantee that the app starts the backdrop renderer before
-        // configuration changes which could cause an activity restart.
-        if (mResizing) {
-            notifyMoveLocked(startX, startY);
-
-            // The WindowPositionerEventReceiver callbacks are delivered on the same handler so this
-            // initial resize is always guaranteed to happen before subsequent drag resizes.
-            mService.mH.post(() -> {
-                mService.mAtmService.resizeTask(
-                        mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
-            });
-        }
-
-        // Make sure we always have valid drag bounds even if the drag ends before any move events
-        // have been handled.
-        mWindowDragBounds.set(startBounds);
-    }
-
-    private void endDragLocked() {
-        mResizing = false;
-        mTask.setDragResizing(false);
-    }
-
-    /** Returns true if the move operation should be ended. */
-    @VisibleForTesting
-    boolean notifyMoveLocked(float x, float y) {
-        if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
-        }
-
-        if (mCtrlType != CTRL_NONE) {
-            resizeDrag(x, y);
-            mTask.setDragResizing(true);
-            return false;
-        }
-
-        // This is a moving or scrolling operation.
-        // Only allow to move in stable area so the target window won't be covered by system bar.
-        // Though {@link Task#resolveOverrideConfiguration} should also avoid the case.
-        mDisplayContent.getStableRect(mTmpRect);
-        // The task may be put in a limited display area.
-        mTmpRect.intersect(mTask.getRootTask().getParent().getBounds());
-
-        int nX = (int) x;
-        int nY = (int) y;
-        if (!mTmpRect.contains(nX, nY)) {
-            // For a moving operation we allow the pointer to go out of the stack bounds, but
-            // use the clamped pointer position for the drag bounds computation.
-            nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
-            nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
-        }
-
-        updateWindowDragBounds(nX, nY, mTmpRect);
-        return false;
-    }
-
-    /**
-     * The user is drag - resizing the window.
-     *
-     * @param x The x coordinate of the current drag coordinate.
-     * @param y the y coordinate of the current drag coordinate.
-     */
-    @VisibleForTesting
-    void resizeDrag(float x, float y) {
-        updateDraggedBounds(TaskResizingAlgorithm.resizeDrag(x, y, mStartDragX, mStartDragY,
-                mWindowOriginalBounds, mCtrlType, mMinVisibleWidth, mMinVisibleHeight,
-                mMaxVisibleSize, mPreserveOrientation, mStartOrientationWasLandscape));
-    }
-
-    private void updateDraggedBounds(Rect newBounds) {
-        mWindowDragBounds.set(newBounds);
-
-        checkBoundsForOrientationViolations(mWindowDragBounds);
-    }
-
-    /**
-     * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
-     *
-     * @param bounds The bounds to be checked.
-     */
-    private void checkBoundsForOrientationViolations(Rect bounds) {
-        // When using debug check that we are not violating the given constraints.
-        if (DEBUG_ORIENTATION_VIOLATIONS) {
-            if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
-                Slog.e(TAG, "Orientation violation detected! should be "
-                        + (mStartOrientationWasLandscape ? "landscape" : "portrait")
-                        + " but is the other");
-            } else {
-                Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
-            }
-            if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
-                Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
-                        + ", " + bounds.width() + ") Height(min,is)=("
-                        + mMinVisibleHeight + ", " + bounds.height() + ")");
-            }
-            if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
-                Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
-                        + ", " + bounds.width() + ") Height(min,is)=("
-                        + mMaxVisibleSize.y + ", " + bounds.height() + ")");
-            }
-        }
-    }
-
-    private void updateWindowDragBounds(int x, int y, Rect rootTaskBounds) {
-        final int offsetX = Math.round(x - mStartDragX);
-        final int offsetY = Math.round(y - mStartDragY);
-        mWindowDragBounds.set(mWindowOriginalBounds);
-        // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
-        final int maxLeft = rootTaskBounds.right - mMinVisibleWidth;
-        final int minLeft = rootTaskBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
-
-        // Vertically, the top mMinVisibleHeight of the window should remain visible.
-        // (This assumes that the window caption bar is at the top of the window).
-        final int minTop = rootTaskBounds.top;
-        final int maxTop = rootTaskBounds.bottom - mMinVisibleHeight;
-
-        mWindowDragBounds.offsetTo(
-                Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
-                Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
-
-        if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
-                "updateWindowDragBounds: " + mWindowDragBounds);
-    }
-
-    public String toShortString() {
-        return TAG;
-    }
-
-    static void setFactory(Factory factory) {
-        sFactory = factory;
-    }
-
-    static TaskPositioner create(WindowManagerService service) {
-        if (sFactory == null) {
-            sFactory = new Factory() {};
-        }
-
-        return sFactory.create(service);
-    }
-
-    @Override
-    public void binderDied() {
-        mService.mTaskPositioningController.finishTaskPositioning();
-    }
-
-    interface Factory {
-        default TaskPositioner create(WindowManagerService service) {
-            return new TaskPositioner(service);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
deleted file mode 100644
index 6f548ab..0000000
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import static java.util.concurrent.CompletableFuture.completedFuture;
-
-import android.annotation.Nullable;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Slog;
-import android.view.Display;
-import android.view.IWindow;
-import android.view.InputWindowHandle;
-import android.view.SurfaceControl;
-
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Controller for task positioning by drag.
- */
-class TaskPositioningController {
-    private final WindowManagerService mService;
-    private SurfaceControl mInputSurface;
-    private DisplayContent mPositioningDisplay;
-
-    private @Nullable TaskPositioner mTaskPositioner;
-
-    private final Rect mTmpClipRect = new Rect();
-
-    boolean isPositioningLocked() {
-        return mTaskPositioner != null;
-    }
-
-    final SurfaceControl.Transaction mTransaction;
-
-    InputWindowHandle getDragWindowHandleLocked() {
-        return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null;
-    }
-
-    TaskPositioningController(WindowManagerService service) {
-        mService = service;
-        mTransaction = service.mTransactionFactory.get();
-    }
-
-    void hideInputSurface(int displayId) {
-        if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId
-                && mInputSurface != null) {
-            mTransaction.hide(mInputSurface).apply();
-        }
-    }
-
-    /**
-     * @return a future that completes after window info is sent.
-     */
-    CompletableFuture<Void> showInputSurface(int displayId) {
-        if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) {
-            return completedFuture(null);
-        }
-        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-        if (mInputSurface == null) {
-            mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
-                    .setContainerLayer()
-                    .setName("Drag and Drop Input Consumer")
-                    .setCallsite("TaskPositioningController.showInputSurface")
-                    .setParent(dc.getOverlayLayer())
-                    .build();
-        }
-
-        final InputWindowHandle h = getDragWindowHandleLocked();
-        if (h == null) {
-            Slog.w(TAG_WM, "Drag is in progress but there is no "
-                    + "drag window handle.");
-            return completedFuture(null);
-        }
-
-        final Display display = dc.getDisplay();
-        final Point p = new Point();
-        display.getRealSize(p);
-        mTmpClipRect.set(0, 0, p.x, p.y);
-
-        CompletableFuture<Void> result = new CompletableFuture<>();
-        mTransaction.show(mInputSurface)
-                .setInputWindowInfo(mInputSurface, h)
-                .setLayer(mInputSurface, Integer.MAX_VALUE)
-                .setPosition(mInputSurface, 0, 0)
-                .setCrop(mInputSurface, mTmpClipRect)
-                .addWindowInfosReportedListener(() -> result.complete(null))
-                .apply();
-        return result;
-    }
-
-    boolean startMovingTask(IWindow window, float startX, float startY) {
-        WindowState win = null;
-        CompletableFuture<Boolean> startPositioningLockedFuture;
-        synchronized (mService.mGlobalLock) {
-            win = mService.windowForClientLocked(null, window, false);
-            startPositioningLockedFuture =
-                startPositioningLocked(
-                    win, false /*resize*/, false /*preserveOrientation*/, startX, startY);
-        }
-
-        try {
-            if (!startPositioningLockedFuture.get()) {
-                return false;
-            }
-        } catch (Exception exception) {
-            Slog.e(TAG_WM, "Exception thrown while waiting for startPositionLocked future",
-                    exception);
-            return false;
-        }
-
-        synchronized (mService.mGlobalLock) {
-            mService.mAtmService.setFocusedTask(win.getTask().mTaskId);
-        }
-        return true;
-    }
-
-    void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
-        mService.mH.post(() -> {
-            Task task;
-            CompletableFuture<Boolean> startPositioningLockedFuture;
-            synchronized (mService.mGlobalLock) {
-                task = displayContent.findTaskForResizePoint(x, y);
-                if (task == null || !task.isResizeable()) {
-                    // The task is not resizable, so don't do anything when the user drags the
-                    // the resize handles.
-                    return;
-                }
-                startPositioningLockedFuture =
-                    startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
-                            task.preserveOrientationOnResize(), x, y);
-            }
-
-            try {
-                if (!startPositioningLockedFuture.get()) {
-                    return;
-                }
-            } catch (Exception exception) {
-                Slog.e(TAG_WM, "Exception thrown while waiting for startPositionLocked future",
-                        exception);
-                return;
-            }
-
-            synchronized (mService.mGlobalLock) {
-                mService.mAtmService.setFocusedTask(task.mTaskId);
-            }
-        });
-    }
-
-    private CompletableFuture<Boolean> startPositioningLocked(WindowState win, boolean resize,
-            boolean preserveOrientation, float startX, float startY) {
-        if (DEBUG_TASK_POSITIONING)
-            Slog.d(TAG_WM, "startPositioningLocked: "
-                    + "win=" + win + ", resize=" + resize + ", preserveOrientation="
-                    + preserveOrientation + ", {" + startX + ", " + startY + "}");
-
-        if (win == null || win.mActivityRecord == null) {
-            Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
-            return completedFuture(false);
-        }
-        if (win.mInputChannel == null) {
-            Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, "
-                    + " probably being removed");
-            return completedFuture(false);
-        }
-
-        final DisplayContent displayContent = win.getDisplayContent();
-        if (displayContent == null) {
-            Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
-            return completedFuture(false);
-        }
-        mPositioningDisplay = displayContent;
-
-        mTaskPositioner = TaskPositioner.create(mService);
-        return mTaskPositioner.register(displayContent, win).thenApply(unused -> {
-            // The global lock is held by the callers of startPositioningLocked but released before
-            // the async results are waited on. We must acquire the lock in this callback to ensure
-            // thread safety.
-            synchronized (mService.mGlobalLock) {
-                // We need to grab the touch focus so that the touch events during the
-                // resizing/scrolling are not sent to the app. 'win' is the main window
-                // of the app, it may not have focus since there might be other windows
-                // on top (eg. a dialog window).
-                WindowState transferTouchFromWin = win;
-                if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
-                        && displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) {
-                    transferTouchFromWin = displayContent.mCurrentFocus;
-                }
-                if (!mService.mInputManager.transferTouchGesture(
-                        transferTouchFromWin.mInputChannel.getToken(),
-                        mTaskPositioner.mClientChannel.getToken())) {
-                    Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
-                    cleanUpTaskPositioner();
-                    return false;
-                }
-
-                mTaskPositioner.startDrag(resize, preserveOrientation, startX, startY);
-                return true;
-            }
-        });
-    }
-
-    public void finishTaskPositioning(IWindow window) {
-        if (mTaskPositioner != null && mTaskPositioner.mClientCallback == window.asBinder()) {
-            finishTaskPositioning();
-        }
-    }
-
-    void finishTaskPositioning() {
-        // TaskPositioner attaches the InputEventReceiver to the animation thread. We need to
-        // dispose the receiver on the same thread to avoid race conditions.
-        mService.mAnimationHandler.post(() -> {
-            if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
-
-            synchronized (mService.mGlobalLock) {
-                cleanUpTaskPositioner();
-                mPositioningDisplay = null;
-            }
-        });
-    }
-
-    private void cleanUpTaskPositioner() {
-        final TaskPositioner positioner = mTaskPositioner;
-        if (positioner == null) {
-            return;
-        }
-
-        // We need to assign task positioner to null first to indicate that we're finishing task
-        // positioning.
-        mTaskPositioner = null;
-        positioner.unregister();
-    }
-}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f6a68d5..65bc9a22 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -745,6 +745,7 @@
         if (mController.isAnimating()) {
             dc.enableHighPerfTransition(true);
         }
+        mController.dispatchLegacyAppTransitionPending(dc.mDisplayId);
     }
 
     /**
@@ -1618,7 +1619,7 @@
         mController.mTransitionTracer.logAbortedTransition(this);
         // Syncengine abort will call through to onTransactionReady()
         mSyncEngine.abort(mSyncId);
-        mController.dispatchLegacyAppTransitionCancelled();
+        mController.dispatchLegacyAppTransitionCancelled(mTargetDisplays);
         invokeTransitionEndedListeners();
     }
 
@@ -1766,7 +1767,19 @@
         }
 
         for (int i = 0; i < mTargets.size(); ++i) {
-            final DisplayArea da = mTargets.get(i).mContainer.asDisplayArea();
+            final WindowContainer<?> wc = mTargets.get(i).mContainer;
+            final WallpaperWindowToken wp = wc.asWallpaperToken();
+            if (wp != null) {
+                // If on a rotation leash, the wallpaper token surface needs to be shown explicitly
+                // because shell only gets the leash and the wallpaper token surface is not allowed
+                // to be changed by non-transition logic until the transition is finished.
+                if (Flags.ensureWallpaperInTransitions() && wp.isVisibleRequested()
+                        && wp.getFixedRotationLeash() != null) {
+                    transaction.show(wp.mSurfaceControl);
+                }
+                continue;
+            }
+            final DisplayArea<?> da = wc.asDisplayArea();
             if (da == null) continue;
             if (da.isVisibleRequested()) {
                 mController.mValidateDisplayVis.remove(da);
@@ -2168,14 +2181,6 @@
                         && !wallpaperIsOwnTarget(wallpaper)) {
                     wallpaper.setVisibleRequested(false);
                 }
-                if (showWallpaper && Flags.ensureWallpaperInTransitions()
-                        && wallpaper.isVisibleRequested()
-                        && getLeashSurface(wallpaper, t) != wallpaper.getSurfaceControl()) {
-                    // If on a rotation leash, we need to explicitly show the wallpaper surface
-                    // because shell only gets the leash and we don't allow non-transition logic
-                    // to touch the surfaces until the transition is over.
-                    t.show(wallpaper.getSurfaceControl());
-                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index f4ff404..1df251c 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -42,6 +42,7 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.Display;
 import android.view.WindowManager;
 import android.window.ITransitionMetricsReporter;
 import android.window.ITransitionPlayer;
@@ -326,7 +327,6 @@
         mCollectingTransition.startCollecting(timeoutMs);
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start collecting in Transition: %s",
                 mCollectingTransition);
-        dispatchLegacyAppTransitionPending();
     }
 
     void registerTransitionPlayer(@Nullable ITransitionPlayer player,
@@ -1347,31 +1347,54 @@
         mLegacyListeners.remove(listener);
     }
 
-    void dispatchLegacyAppTransitionPending() {
+    private static boolean shouldDispatchLegacyListener(
+            WindowManagerInternal.AppTransitionListener listener, int displayId) {
+        // INVALID_DISPLAY means that it is a global listener.
+        return listener.mDisplayId == Display.INVALID_DISPLAY || listener.mDisplayId == displayId;
+    }
+
+    void dispatchLegacyAppTransitionPending(int displayId) {
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            mLegacyListeners.get(i).onAppTransitionPendingLocked();
+            final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+            if (shouldDispatchLegacyListener(listener, displayId)) {
+                listener.onAppTransitionPendingLocked();
+            }
         }
     }
 
     void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
+        final long now = SystemClock.uptimeMillis();
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            // TODO(shell-transitions): handle (un)occlude transition.
-            mLegacyListeners.get(i).onAppTransitionStartingLocked(
-                    SystemClock.uptimeMillis() + statusBarTransitionDelay,
-                    AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+            final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+            for (int j = 0; j < info.getRootCount(); ++j) {
+                final int displayId = info.getRoot(j).getDisplayId();
+                if (shouldDispatchLegacyListener(listener, displayId)) {
+                    listener.onAppTransitionStartingLocked(
+                            now + statusBarTransitionDelay,
+                            AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+                }
+            }
         }
     }
 
     void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token);
+            final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+            if (shouldDispatchLegacyListener(listener, ar.getDisplayId())) {
+                listener.onAppTransitionFinishedLocked(ar.token);
+            }
         }
     }
 
-    void dispatchLegacyAppTransitionCancelled() {
-        for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            mLegacyListeners.get(i).onAppTransitionCancelledLocked(
-                    false /* keyguardGoingAwayCancelled */);
+    void dispatchLegacyAppTransitionCancelled(ArrayList<DisplayContent> targetDisplays) {
+        for (int i = 0; i < targetDisplays.size(); ++i) {
+            final int displayId = targetDisplays.get(i).mDisplayId;
+            for (int j = 0; j < mLegacyListeners.size(); ++j) {
+                final var listener = mLegacyListeners.get(j);
+                if (shouldDispatchLegacyListener(listener, displayId)) {
+                    listener.onAppTransitionCancelledLocked(false /* keyguardGoingAwayCancelled */);
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 42b556f..6125360 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -47,7 +47,6 @@
     static final boolean DEBUG_LAYOUT_REPEATS = false;
     static final boolean DEBUG_WINDOW_TRACE = false;
     static final boolean DEBUG_TASK_MOVEMENT = false;
-    static final boolean DEBUG_TASK_POSITIONING = false;
     static final boolean DEBUG_ROOT_TASK = false;
     static final boolean DEBUG_DISPLAY = false;
     static final boolean DEBUG_POWER = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 2ea1cf8..132e1ee 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -244,6 +244,22 @@
     public static abstract class AppTransitionListener {
 
         /**
+         * The display this listener is interested in. If it is INVALID_DISPLAY, then which display
+         * should be notified depends on the dispatcher.
+         */
+        public final int mDisplayId;
+
+        /** Let transition controller decide which display should receive the callbacks. */
+        public AppTransitionListener() {
+            this(Display.INVALID_DISPLAY);
+        }
+
+        /** It will listen the transition on the given display. */
+        public AppTransitionListener(int displayId) {
+            mDisplayId = displayId;
+        }
+
+        /**
          * Called when an app transition is being setup and about to be executed.
          */
         public void onAppTransitionPendingLocked() {}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index acd8b3f..f65eea0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -54,7 +54,6 @@
 import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.flags.Flags.sensitiveContentAppProtection;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -97,6 +96,7 @@
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
 import static android.window.WindowProviderService.isWindowProviderService;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -1070,7 +1070,6 @@
     /** Whether or not a layout can cause a wake up when theater mode is enabled. */
     boolean mAllowTheaterModeWakeFromLayout;
 
-    final TaskPositioningController mTaskPositioningController;
     final DragDropController mDragDropController;
 
     /** For frozen screen animations. */
@@ -1428,7 +1427,6 @@
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
 
-        mTaskPositioningController = new TaskPositioningController(this);
         mDragDropController = new DragDropController(this, mH.getLooper());
 
         mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources());
@@ -9379,40 +9377,82 @@
     }
 
     /**
-     * Move focus to the adjacent embedded activity if the adjacent activity is more recently
-     * created or has a window more recently added.
+     * Returns the Activity that has the most recently created window in the adjacent activities
+     * if any.
      */
-    boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) {
-        final TaskFragment taskFragment = focusedWindow.getTaskFragment();
+    @NonNull
+    ActivityRecord getMostRecentActivityInAdjacent(@NonNull ActivityRecord focusedActivity) {
+        final TaskFragment taskFragment = focusedActivity.getTaskFragment();
         if (taskFragment == null) {
-            // Skip if not an Activity window.
-            return false;
+            // Return if activity no attached.
+            return focusedActivity;
         }
 
         if (!Flags.embeddedActivityBackNavFlag()) {
-            // Skip if flag is not enabled.
-            return false;
+            // Return if flag is not enabled.
+            return focusedActivity;
         }
 
-        if (!focusedWindow.mActivityRecord.isEmbedded()) {
-            // Skip if the focused activity is not embedded
-            return false;
+        if (!focusedActivity.isEmbedded()) {
+            // Return if the focused activity is not embedded.
+            return focusedActivity;
         }
 
         final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
         final ActivityRecord adjacentTopActivity =
                 adjacentTaskFragment != null ? adjacentTaskFragment.topRunningActivity() : null;
         if (adjacentTopActivity == null) {
-            return false;
+            // Return if no adjacent activity.
+            return focusedActivity;
         }
 
         if (adjacentTopActivity.getLastWindowCreateTime()
-                < focusedWindow.mActivityRecord.getLastWindowCreateTime()) {
-            // Skip if the current focus activity has more recently active window.
+                < focusedActivity.getLastWindowCreateTime()) {
+            // Return if the current focus activity has more recently active window.
+            return focusedActivity;
+        }
+
+        return adjacentTopActivity;
+    }
+
+    @NonNull
+    WindowState getMostRecentUsedEmbeddedWindowForBack(@NonNull WindowState focusedWindow) {
+        final ActivityRecord focusedActivity = focusedWindow.getActivityRecord();
+        if (focusedActivity == null) {
+            // Not an Activity.
+            return focusedWindow;
+        }
+
+        final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent(
+                focusedActivity);
+        if (mostRecentActivityInAdjacent == focusedActivity) {
+            // Already be the most recent window.
+            return focusedWindow;
+        }
+
+        // Looks for a candidate focused window on the adjacent Activity for the back event.
+        final WindowState candidate =
+                mostRecentActivityInAdjacent.getDisplayContent().findFocusedWindow(
+                        mostRecentActivityInAdjacent);
+        return candidate != null ? candidate : focusedWindow;
+    }
+
+    /**
+     * Move focus to the adjacent embedded activity if the adjacent activity is more recently
+     * created or has a window more recently added.
+     * <p>
+     * Returns {@code true} if the focused window is changed. Otherwise, returns {@code false}.
+     */
+    boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) {
+        final ActivityRecord activity = focusedWindow.getActivityRecord();
+        if (activity == null) {
             return false;
         }
 
-        moveFocusToActivity(adjacentTopActivity);
+        final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent(
+                activity);
+
+        moveFocusToActivity(mostRecentActivityInAdjacent);
         return !focusedWindow.isFocused();
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 12c5073..984caf1 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1674,6 +1674,22 @@
         // Otherwise if other places send wpc.getConfiguration() to client, the configuration may
         // be ignored due to the seq is older.
         resolvedConfig.seq = newParentConfig.seq;
+
+        if (mConfigActivityRecord != null) {
+            // Let the activity decide whether to apply the size override.
+            return;
+        }
+        final DisplayContent displayContent = mAtm.mWindowManager != null
+                ? mAtm.mWindowManager.getDefaultDisplayContentLocked()
+                : null;
+        applySizeOverrideIfNeeded(
+                displayContent,
+                mInfo,
+                newParentConfig,
+                resolvedConfig,
+                false /* optsOutEdgeToEdge */,
+                false /* hasFixedRotationTransform */,
+                false /* hasCompatDisplayInsets */);
     }
 
     void dispatchConfiguration(@NonNull Configuration config) {
diff --git a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
index 4211764..3559e62 100644
--- a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
+++ b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
@@ -47,16 +47,13 @@
             Flags::enableDesktopWindowingWallpaperActivity, /* shouldOverrideByDevOption= */ true);
 
     private static final String TAG = "DesktopModeFlagsUtil";
-    private static final String SYSTEM_PROPERTY_OVERRIDE_KEY =
-            "sys.wmshell.desktopmode.dev_toggle_override";
-
     // Function called to obtain aconfig flag value.
     private final Supplier<Boolean> mFlagFunction;
     // Whether the flag state should be affected by developer option.
     private final boolean mShouldOverrideByDevOption;
 
     // Local cache for toggle override, which is initialized once on its first access. It needs to
-    // be refreshed only on reboots as overridden state takes effect on reboots.
+    // be refreshed only on reboots as overridden state is expected to take effect on reboots.
     private static ToggleOverride sCachedToggleOverride;
 
     DesktopModeFlagsUtil(Supplier<Boolean> flagFunction, boolean shouldOverrideByDevOption) {
@@ -67,9 +64,6 @@
     /**
      * Determines state of flag based on the actual flag and desktop mode developer option
      * overrides.
-     *
-     * Note: this method makes sure that a constant developer toggle overrides is read until
-     * reboot.
      */
     public boolean isEnabled(Context context) {
         if (!Flags.showDesktopWindowingDevOption()
@@ -102,49 +96,15 @@
     }
 
     /**
-     *  Returns {@link ToggleOverride} from a non-persistent system property if present. Otherwise
-     *  initializes the system property by reading Settings.Global.
+     *  Returns {@link ToggleOverride} from Settings.Global set by toggle.
      */
     private ToggleOverride getToggleOverrideFromSystem(Context context) {
-        // A non-persistent System Property is used to store override to ensure it remains
-        // constant till reboot.
-        String overrideProperty = System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null);
-        ToggleOverride overrideFromSystemProperties = convertToToggleOverride(overrideProperty);
-
-        // If valid system property, return it
-        if (overrideFromSystemProperties != null) {
-            return overrideFromSystemProperties;
-        }
-
-        // Fallback when System Property is not present (just after reboot) or not valid (user
-        // manually changed the value): Read from Settings.Global
         int settingValue = Settings.Global.getInt(
                 context.getContentResolver(),
                 Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
                 OVERRIDE_UNSET.getSetting()
         );
-        ToggleOverride overrideFromSettingsGlobal =
-                ToggleOverride.fromSetting(settingValue, OVERRIDE_UNSET);
-        // Initialize System Property
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(settingValue));
-        return overrideFromSettingsGlobal;
-    }
-
-    /**
-     * Converts {@code intString} into {@link ToggleOverride}. Return {@code null} if
-     * {@code intString} does not correspond to a {@link ToggleOverride}.
-     */
-    private static @Nullable ToggleOverride convertToToggleOverride(
-            @Nullable String intString
-    ) {
-        if (intString == null) return null;
-        try {
-            int intValue = Integer.parseInt(intString);
-            return ToggleOverride.fromSetting(intValue, null);
-        } catch (NumberFormatException e) {
-            Log.w(TAG, "Unknown toggleOverride int " + intString);
-            return null;
-        }
+        return ToggleOverride.fromSetting(settingValue, OVERRIDE_UNSET);
     }
 
     /** Override state of desktop mode developer option toggle. */
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index db4b171..9e8811f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -153,6 +153,7 @@
 import com.android.server.contextualsearch.ContextualSearchManagerService;
 import com.android.server.coverage.CoverageService;
 import com.android.server.cpu.CpuMonitorService;
+import com.android.server.crashrecovery.CrashRecoveryModule;
 import com.android.server.credentials.CredentialManagerService;
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -381,8 +382,6 @@
                     + "OnDevicePersonalizationSystemService$Lifecycle";
     private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS =
             "com.android.server.deviceconfig.DeviceConfigInit$Lifecycle";
-    private static final String CRASHRECOVERY_MODULE_LIFECYCLE_CLASS =
-            "com.android.server.crashrecovery.CrashRecoveryModule$Lifecycle";
 
 
     /*
@@ -2939,7 +2938,7 @@
 
         if (Flags.refactorCrashrecovery()) {
             t.traceBegin("StartCrashRecoveryModule");
-            mSystemServiceManager.startService(CRASHRECOVERY_MODULE_LIFECYCLE_CLASS);
+            mSystemServiceManager.startService(CrashRecoveryModule.Lifecycle.class);
             t.traceEnd();
         } else {
             if (Flags.recoverabilityDetection()) {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 3ed6ad7..acdbbde 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -398,13 +398,13 @@
                 if (randomNum >= traceFrequency) {
                     return;
                 }
-                // For a small percentage a traces, we collect the initialization behavior.
-                boolean traceInitialization = ThreadLocalRandom.current().nextInt(10) < 1;
-                int traceDelay = traceInitialization ? 0 : 1000;
-                String traceTag = traceInitialization ? "camera_init" : "camera";
+                final int traceDelay = 1000;
+                final int traceDuration = 5000;
+                final String traceTag = "camera";
                 BackgroundThread.get().getThreadHandler().postDelayed(() -> {
                     try {
-                        mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider");
+                        mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider",
+                                traceDuration);
                     } catch (RemoteException e) {
                         Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
                     }
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
index 99968d5..9da695a 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
@@ -19,10 +19,12 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -44,9 +46,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-import java.util.Collections;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamAccessibilityTest {
@@ -73,7 +72,8 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mDreamAccessibility = new DreamAccessibility(mContext, mView);
+        Runnable mDismissCallback = () -> {};
+        mDreamAccessibility = new DreamAccessibility(mContext, mView, mDismissCallback);
 
         when(mContext.getResources()).thenReturn(mResources);
         when(mResources.getString(R.string.dream_accessibility_action_click))
@@ -84,80 +84,55 @@
      */
     @Test
     public void testConfigureAccessibilityActions() {
-        when(mAccessibilityNodeInfo.getActionList()).thenReturn(new ArrayList<>());
+        when(mView.getAccessibilityDelegate()).thenReturn(null);
 
-        mDreamAccessibility.updateAccessibilityConfiguration(false);
+        mDreamAccessibility.updateAccessibilityConfiguration();
 
         verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
-        View.AccessibilityDelegate capturedDelegate =
-                mAccessibilityDelegateArgumentCaptor.getValue();
+        View.AccessibilityDelegate capturedDelegate = mAccessibilityDelegateArgumentCaptor
+                .getValue();
 
         capturedDelegate.onInitializeAccessibilityNodeInfo(mView, mAccessibilityNodeInfo);
 
         verify(mAccessibilityNodeInfo).addAction(argThat(action ->
-                action.getId() == AccessibilityNodeInfo.ACTION_CLICK
+                action.getId() == AccessibilityNodeInfo.ACTION_DISMISS
                         && TextUtils.equals(action.getLabel(), CUSTOM_ACTION)));
     }
 
     /**
-     * Test to verify the configuration of accessibility actions within a view delegate,
-     * specifically checking the removal of an existing click action and addition
-     * of a new custom action.
+     * Test to verify no accessibility configuration is added if one exist.
      */
     @Test
-    public void testConfigureAccessibilityActions_RemovesExistingClickAction() {
-        AccessibilityNodeInfo.AccessibilityAction existingAction =
-                new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK,
-                        EXISTING_ACTION);
-        when(mAccessibilityNodeInfo.getActionList())
-                .thenReturn(Collections.singletonList(existingAction));
+    public void testNotAddingDuplicateAccessibilityConfiguration() {
+        View.AccessibilityDelegate existingDelegate = mock(View.AccessibilityDelegate.class);
+        when(mView.getAccessibilityDelegate()).thenReturn(existingDelegate);
 
-        mDreamAccessibility.updateAccessibilityConfiguration(false);
+        mDreamAccessibility.updateAccessibilityConfiguration();
 
-        verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
-        View.AccessibilityDelegate capturedDelegate =
-                mAccessibilityDelegateArgumentCaptor.getValue();
-
-        capturedDelegate.onInitializeAccessibilityNodeInfo(mView, mAccessibilityNodeInfo);
-
-        verify(mAccessibilityNodeInfo).removeAction(existingAction);
-        verify(mAccessibilityNodeInfo).addAction(argThat(action ->
-                action.getId() == AccessibilityNodeInfo.ACTION_CLICK
-                        && TextUtils.equals(action.getLabel(), CUSTOM_ACTION)));
-
-    }
-
-    /**
-     * Test to verify the removal of a custom accessibility action within a view delegate.
-     */
-    @Test
-    public void testRemoveCustomAccessibilityAction() {
-
-        AccessibilityNodeInfo.AccessibilityAction existingAction =
-                new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK,
-                        EXISTING_ACTION);
-        when(mAccessibilityNodeInfo.getActionList())
-                .thenReturn(Collections.singletonList(existingAction));
-
-        mDreamAccessibility.updateAccessibilityConfiguration(false);
-        verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
-        View.AccessibilityDelegate capturedDelegate =
-                mAccessibilityDelegateArgumentCaptor.getValue();
-        when(mView.getAccessibilityDelegate()).thenReturn(capturedDelegate);
-        clearInvocations(mView);
-
-        mDreamAccessibility.updateAccessibilityConfiguration(true);
-        verify(mView).setAccessibilityDelegate(null);
-    }
-
-    /**
-     * Test to verify the removal of custom accessibility action is not called if delegate is not
-     * set by the dreamService.
-     */
-    @Test
-    public void testRemoveCustomAccessibility_DoesNotRemoveDelegateNotSetByDreamAccessibility() {
-        mDreamAccessibility.updateAccessibilityConfiguration(true);
         verify(mView, never()).setAccessibilityDelegate(any());
     }
+
+    /**
+     * Test to verify dismiss callback is called
+     */
+    @Test
+    public void testPerformAccessibilityAction() {
+        Runnable mockDismissCallback = mock(Runnable.class);
+        DreamAccessibility dreamAccessibility = new DreamAccessibility(mContext,
+                mView, mockDismissCallback);
+
+        dreamAccessibility.updateAccessibilityConfiguration();
+
+        verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
+        View.AccessibilityDelegate capturedDelegate = mAccessibilityDelegateArgumentCaptor
+                .getValue();
+
+        boolean result = capturedDelegate.performAccessibilityAction(mView,
+                AccessibilityNodeInfo.ACTION_DISMISS, null);
+
+        assertTrue(result);
+        verify(mockDismissCallback).run();
+    }
+
 }
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
index 644ae47..005ceee 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
@@ -130,7 +130,7 @@
         boolean inCpuSection = false;
         for (int i = 0; i < lines.length; i++) {
             if (!inCpuSection) {
-                if (lines[i].startsWith("CpuPowerStatsCollector")) {
+                if (lines[i].startsWith("cpu (1)")) {
                     inCpuSection = true;
                 }
             } else if (lines[i].startsWith(" ")) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
index 7bec13f6..1621d47d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
@@ -149,9 +149,9 @@
                 .isEqualTo(20000);
         assertThat(ps2.uidStats.size()).isEqualTo(2);
         assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID1), 0))
-                .isEqualTo(14000);
+                .isEqualTo(4000);
         assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID2), 0))
-                .isEqualTo(21000);
+                .isEqualTo(6000);
     }
 
     @Test
@@ -209,8 +209,8 @@
         assertThat(POWER_STATS_LAYOUT.getDevicePowerEstimate(deviceStats))
                 .isWithin(PRECISION).of(expectedPower * 0.75);
 
-        // UID1: estimated power = 14,000 uC = 0.00388 mAh
-        expectedPower = 0.00388;
+        // UID1: estimated power = 4,000 uC = 0.00111 mAh
+        expectedPower = 0.00111;
         ps2.getUidStats(uidStats, APP_UID1,
                 states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
         assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
@@ -221,8 +221,8 @@
         assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
                 .isWithin(PRECISION).of(expectedPower * 0.75);
 
-        // UID2: estimated power = 21,000 uC = 0.00583 mAh
-        expectedPower = 0.00583;
+        // UID2: estimated power = 6,000 uC = 0.00166 mAh
+        expectedPower = 0.00167;
         ps2.getUidStats(uidStats, APP_UID2,
                 states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
         assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
diff --git a/services/tests/servicestests/src/com/android/server/autofill/HelperTest.java b/services/tests/servicestests/src/com/android/server/autofill/HelperTest.java
new file mode 100644
index 0000000..f698bea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/autofill/HelperTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill;
+
+import static com.android.server.autofill.Helper.SaveInfoStats;
+import static com.android.server.autofill.Helper.getSaveInfoStatsFromFillResponses;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.service.autofill.FillResponse;
+import android.service.autofill.SaveInfo;
+import android.util.SparseArray;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class HelperTest {
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_nullFillResponses() {
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(null);
+
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(-1);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(-1);
+    }
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_emptyFillResponseSparseArray() {
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(new SparseArray<>());
+
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(0);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(0);
+    }
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_singleResponseWithoutSaveInfo() {
+        FillResponse.Builder fillResponseBuilder = new FillResponse.Builder();
+        // Add client state to satisfy the sanity check in FillResponseBuilder.build()
+        Bundle clientState = new Bundle();
+        fillResponseBuilder.setClientState(clientState);
+        FillResponse testFillResponse = fillResponseBuilder.build();
+
+        SparseArray<FillResponse> testFillResponses = new SparseArray<>();
+        testFillResponses.put(0, testFillResponse);
+
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(testFillResponses);
+
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(0);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(0);
+    }
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_singleResponseWithSaveInfo() {
+        FillResponse.Builder fillResponseBuilder = new FillResponse.Builder();
+        SaveInfo.Builder saveInfoBuilder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC);
+        fillResponseBuilder.setSaveInfo(saveInfoBuilder.build());
+        FillResponse testFillResponse = fillResponseBuilder.build();
+
+        SparseArray<FillResponse> testFillResponses = new SparseArray<>();
+        testFillResponses.put(0, testFillResponse);
+
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(testFillResponses);
+
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(1);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(1);
+    }
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_multipleResponseWithDifferentTypeSaveInfo() {
+        FillResponse.Builder fillResponseBuilder1 = new FillResponse.Builder();
+        SaveInfo.Builder saveInfoBuilder1 = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC);
+        fillResponseBuilder1.setSaveInfo(saveInfoBuilder1.build());
+        FillResponse testFillResponse1 = fillResponseBuilder1.build();
+
+        FillResponse.Builder fillResponseBuilder2 = new FillResponse.Builder();
+        SaveInfo.Builder saveInfoBuilder2 = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_ADDRESS);
+        fillResponseBuilder2.setSaveInfo(saveInfoBuilder2.build());
+        FillResponse testFillResponse2 = fillResponseBuilder2.build();
+
+        FillResponse.Builder fillResponseBuilder3 = new FillResponse.Builder();
+        SaveInfo.Builder saveInfoBuilder3 = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_ADDRESS);
+        fillResponseBuilder3.setSaveInfo(saveInfoBuilder3.build());
+        FillResponse testFillResponse3 = fillResponseBuilder3.build();
+
+        SparseArray<FillResponse> testFillResponses = new SparseArray<>();
+        testFillResponses.put(0, testFillResponse1);
+        testFillResponses.put(1, testFillResponse2);
+        testFillResponses.put(2, testFillResponse3);
+
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(testFillResponses);
+
+        // Save info count is 3. Since two save info share the same save data type, the distinct
+        // save data type count is 2.
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(3);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(2);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 0f38532..a4222ff 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -1518,7 +1518,8 @@
         mBiometricService.onStart();
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(false);
-        when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+        when(mBiometricService.mSettingObserver
+                .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
                 .thenReturn(true);
 
         setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
@@ -1540,7 +1541,8 @@
         mBiometricService.onStart();
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(false);
-        when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+        when(mBiometricService.mSettingObserver
+                .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
                 .thenReturn(true);
 
         setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
@@ -1564,7 +1566,8 @@
         mBiometricService.onStart();
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(false);
-        when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+        when(mBiometricService.mSettingObserver
+                .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
                 .thenReturn(true);
 
         setupAuthForOnly(TYPE_CREDENTIAL, Authenticators.DEVICE_CREDENTIAL);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index b831ef5..240da9f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -90,7 +90,8 @@
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt()))
                 .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
         when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
-        when(mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt())).thenReturn(true);
+        when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+                anyInt())).thenReturn(true);
         when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
         when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
         when(mFaceAuthenticator.getLockoutModeForUser(anyInt()))
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 316b5fa..689b241 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -39,6 +39,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -364,6 +365,39 @@
     @EnableFlags(android.companion.virtualdevice.flags
             .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
+    public void testReuseProjection_keyguardNotLocked_startConsentDialog()
+            throws NameNotFoundException {
+        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+        projection.start(mIMediaProjectionCallback);
+
+        doNothing().when(mContext).startActivityAsUser(any(), any());
+        doReturn(false).when(mKeyguardManager).isKeyguardLocked();
+
+        MediaProjectionManagerService.BinderService mediaProjectionBinderService =
+                mService.new BinderService(mContext);
+        mediaProjectionBinderService.requestConsentForInvalidProjection(projection);
+
+        verify(mContext).startActivityAsUser(any(), any());
+    }
+
+    @EnableFlags(android.companion.virtualdevice.flags
+            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+    @Test
+    public void testReuseProjection_keyguardLocked_noConsentDialog() throws NameNotFoundException {
+        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+        projection.start(mIMediaProjectionCallback);
+
+        doReturn(true).when(mKeyguardManager).isKeyguardLocked();
+        MediaProjectionManagerService.BinderService mediaProjectionBinderService =
+                mService.new BinderService(mContext);
+        mediaProjectionBinderService.requestConsentForInvalidProjection(projection);
+
+        verify(mContext, never()).startActivityAsUser(any(), any());
+    }
+
+    @EnableFlags(android.companion.virtualdevice.flags
+            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+    @Test
     public void testKeyguardLocked_stopsActiveProjection() throws Exception {
         MediaProjectionManagerService service =
                 new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 9dac23f..d7004e7 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -1746,10 +1746,6 @@
         assertTrue("Tested duration=" + duration4, duration4 < 2000);
 
         // Effect5: played normally after effect4, which may or may not have played.
-
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId5));
-        verifyCallbacksTriggered(vibrationId5, Vibration.Status.FINISHED);
-
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
                 fakeVibrator.getEffectSegments(vibrationId5));
     }
diff --git a/services/tests/wmtests/res/xml/bookmarks.xml b/services/tests/wmtests/res/xml/bookmarks.xml
index 88419e9..1549b2d 100644
--- a/services/tests/wmtests/res/xml/bookmarks.xml
+++ b/services/tests/wmtests/res/xml/bookmarks.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2024 The Android Open Source Project
+<!-- Copyright 2024 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.
@@ -14,6 +14,8 @@
      limitations under the License.
 -->
 <bookmarks>
+    <!-- the key combinations for the following shortcuts must be in sync
+         with the key combinations sent by the test in ModifierShortcutTests.java -->
     <bookmark
         role="android.app.role.BROWSER"
         shortcut="b" />
@@ -38,4 +40,37 @@
     <bookmark
         category="android.intent.category.APP_CALCULATOR"
         shortcut="u" />
+
+    <!-- The following shortcuts will not be invoked by tests but are here to
+         provide test coverage of parsing the different types of shortcut. -->
+    <bookmark
+        package="com.test"
+        class="com.test.BookmarkTest"
+        shortcut="a" />
+    <bookmark
+        package="com.test2"
+        class="com.test.BookmarkTest"
+        shortcut="d" />
+
+    <bookmark
+        role="android.app.role.BROWSER"
+        shortcut="b"
+        shift="true" />
+    <bookmark
+        category="android.intent.category.APP_CONTACTS"
+        shortcut="c"
+        shift="true" />
+    <bookmark
+        package="com.test"
+        class="com.test.BookmarkTest"
+        shortcut="a"
+        shift="true" />
+
+    <!-- It's intended that this package/class will NOT resolve so we test the resolution
+         failure case. -->
+    <bookmark
+        package="com.test3"
+        class="com.test.BookmarkTest"
+        shortcut="f" />
+
 </bookmarks>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index 8c375d4..5533ff9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 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.
@@ -19,15 +19,22 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.os.Handler;
@@ -58,27 +65,56 @@
     private Handler mHandler;
     private Context mContext;
     private Resources mResources;
+    private PackageManager mPackageManager;
 
     @Before
     public void setUp() {
         mHandler = new Handler(Looper.getMainLooper());
         mContext = spy(getInstrumentation().getTargetContext());
         mResources = spy(mContext.getResources());
+        mPackageManager = spy(mContext.getPackageManager());
 
         XmlResourceParser testBookmarks = mResources.getXml(
                 com.android.frameworks.wmtests.R.xml.bookmarks);
 
         when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mResources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks);
+        try {
+            // Keep packageName / className in sync with
+            // services/tests/wmtests/res/xml/bookmarks.xml
+            ActivityInfo testActivityInfo = new ActivityInfo();
+            testActivityInfo.applicationInfo = new ApplicationInfo();
+            testActivityInfo.packageName =
+                    testActivityInfo.applicationInfo.packageName = "com.test";
+
+            doReturn(testActivityInfo).when(mPackageManager).getActivityInfo(
+                    eq(new ComponentName("com.test", "com.test.BookmarkTest")), anyInt());
+            doThrow(new PackageManager.NameNotFoundException("com.test3")).when(mPackageManager)
+                    .getActivityInfo(eq(new ComponentName("com.test3", "com.test.BookmarkTest")),
+                        anyInt());
+        } catch (PackageManager.NameNotFoundException ignored) { }
+        doReturn(new String[] { "com.test" }).when(mPackageManager)
+                .canonicalToCurrentPackageNames(aryEq(new String[] { "com.test2" }));
+
 
         mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
     }
 
     @Test
     public void test_getApplicationLaunchKeyboardShortcuts() {
+        // Expected values here determined by the number of shortcuts defined in
+        // services/tests/wmtests/res/xml/bookmarks.xml
+
+        // Total valid shortcuts.
         KeyboardShortcutGroup group =
                 mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(-1);
-        assertEquals(8, group.getItems().size());
+        assertEquals(13, group.getItems().size());
+
+        // Total valid shift shortcuts.
+        assertEquals(3, group.getItems().stream()
+                .filter(s -> s.getModifiers() == (KeyEvent.META_SHIFT_ON | KeyEvent.META_META_ON))
+                .count());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
new file mode 100644
index 0000000..ddd6d56
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertEquals;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatAspectRatioOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatAspectRatioOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatAspectRatioOverridesTest extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
+            robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ false);
+
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserFullscreenOverride_falseFullscreenProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+
+            robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserFullscreenOverride_falseSettingsProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+            robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+        });
+    }
+
+
+    @Test
+    public void testShouldApplyUserFullscreenOverride_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+
+            robot.checkShouldApplyUserFullscreenOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testShouldEnableUserAspectRatioSettings_ignoreOrientation_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_trueProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* enabled */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_disabledIgnoreOrientationRequest() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ false);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_ignoreOrientation_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testShouldOverrideMinAspectRatio_overrideEnabled_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testShouldOverrideMinAspectRatio_propertyTrue_overrideEnabled_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ true);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testShouldOverrideMinAspectRatio_propertyTrue_overrideDisabled_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testShouldOverrideMinAspectRatio_overrideDisabled_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+        });
+    }
+
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testshouldOverrideMinAspectRatio_propertyFalse_noOverride_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<AspectRatioOverridesRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final AspectRatioOverridesRobotTest robot =
+                new AspectRatioOverridesRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class AspectRatioOverridesRobotTest extends AppCompatRobotBase {
+
+        AspectRatioOverridesRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        void checkShouldApplyUserFullscreenOverride(boolean expected) {
+            assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+                    .shouldApplyUserFullscreenOverride());
+        }
+
+        void checkShouldEnableUserAspectRatioSettings(boolean expected) {
+            assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+                    .shouldEnableUserAspectRatioSettings());
+        }
+
+        void checkShouldApplyUserMinAspectRatioOverride(boolean expected) {
+            assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+                    .shouldApplyUserMinAspectRatioOverride());
+        }
+
+        void checkShouldOverrideMinAspectRatio(boolean expected) {
+            assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+                    .shouldOverrideMinAspectRatio());
+        }
+
+        @NonNull
+        private AppCompatAspectRatioOverrides getTopActivityAppCompatAspectRatioOverrides() {
+            return activity().top().mAppCompatController.getAppCompatAspectRatioOverrides();
+        }
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index d8c7fb3..de99f54 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -25,6 +25,7 @@
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
@@ -36,6 +37,7 @@
 
 import androidx.annotation.NonNull;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
 import org.junit.Rule;
@@ -286,6 +288,88 @@
         });
     }
 
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ false);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_noOverride_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
     /**
      * Runs a test scenario providing a Robot.
      */
@@ -323,6 +407,11 @@
                     .shouldApplyFreeformTreatmentForCameraCompat(), expected);
         }
 
+        void checkShouldOverrideMinAspectRatioForCamera(boolean expected) {
+            Assert.assertEquals(getAppCompatCameraOverrides()
+                    .shouldOverrideMinAspectRatioForCamera(), expected);
+        }
+
         void checkIsCameraActive(boolean active) {
             Assert.assertEquals(getAppCompatCameraOverrides().isCameraActive(), active);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
index d568eec..361177f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
@@ -32,27 +32,27 @@
  */
 class AppCompatComponentPropRobot {
     @NonNull
-    private final WindowManagerService mWm;
+    private final PackageManager mPackageManager;
 
     AppCompatComponentPropRobot(@NonNull WindowManagerService wm) {
-        mWm = wm;
+        mPackageManager = wm.mContext.getPackageManager();
+        spyOn(mPackageManager);
     }
 
     void enable(@NonNull String propertyName) {
-        setPropertyValue(propertyName, /* enabled */ true);
+        setPropertyValue(propertyName, "", "", /* enabled */ true);
     }
 
     void disable(@NonNull String propertyName) {
-        setPropertyValue(propertyName, /* enabled */ false);
+        setPropertyValue(propertyName, "", "", /* enabled */ false);
     }
 
-    private void setPropertyValue(@NonNull String propertyName, boolean enabled) {
+    private void setPropertyValue(@NonNull String propertyName, @NonNull String packageName,
+            @NonNull String className, boolean enabled) {
         final PackageManager.Property property = new PackageManager.Property(propertyName,
-                /* value */ enabled, /* packageName */ "", /* className */ "");
-        final PackageManager pm = mWm.mContext.getPackageManager();
-        spyOn(pm);
+                /* value */ enabled, packageName, className);
         try {
-            doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
+            doReturn(property).when(mPackageManager).getProperty(eq(propertyName), anyString());
         } catch (PackageManager.NameNotFoundException e) {
             fail(e.getLocalizedMessage());
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index cb3cf6b..0a1b16b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -66,6 +66,4 @@
         doReturn(enabled).when(mAppCompatConfiguration)
                 .isCameraCompatSplitScreenAspectRatioEnabled();
     }
-
-
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 63c14b9..afa22bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -67,6 +67,7 @@
 import android.window.OnBackInvokedCallback;
 import android.window.OnBackInvokedCallbackInfo;
 import android.window.OnBackInvokedDispatcher;
+import android.window.TaskFragmentOrganizer;
 import android.window.TaskSnapshot;
 import android.window.WindowOnBackInvokedDispatcher;
 
@@ -670,25 +671,29 @@
     }
 
     @Test
-    public void testAdjacentFocusInActivityEmbedding() {
+    public void testBackOnMostRecentWindowInActivityEmbedding() {
         mSetFlagsRule.enableFlags(Flags.FLAG_EMBEDDED_ACTIVITY_BACK_NAV_FLAG);
         final Task task = createTask(mDefaultDisplay);
-        final TaskFragment primaryTf = createTaskFragmentWithActivity(task);
-        final TaskFragment secondaryTf = createTaskFragmentWithActivity(task);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TaskFragment primaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final TaskFragment secondaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
         final ActivityRecord primaryActivity = primaryTf.getTopMostActivity();
         final ActivityRecord secondaryActivity = secondaryTf.getTopMostActivity();
         primaryTf.setAdjacentTaskFragment(secondaryTf);
         secondaryTf.setAdjacentTaskFragment(primaryTf);
 
-        final WindowState windowState = mock(WindowState.class);
-        windowState.mActivityRecord = primaryActivity;
-        doReturn(windowState).when(mWm).getFocusedWindowLocked();
-        doReturn(primaryTf).when(windowState).getTaskFragment();
+        final WindowState primaryWindow = mock(WindowState.class);
+        final WindowState secondaryWindow = mock(WindowState.class);
+        doReturn(primaryActivity).when(primaryWindow).getActivityRecord();
+        doReturn(secondaryActivity).when(secondaryWindow).getActivityRecord();
         doReturn(1L).when(primaryActivity).getLastWindowCreateTime();
         doReturn(2L).when(secondaryActivity).getLastWindowCreateTime();
+        doReturn(mDisplayContent).when(primaryActivity).getDisplayContent();
+        doReturn(secondaryWindow).when(mDisplayContent).findFocusedWindow(eq(secondaryActivity));
 
-        startBackNavigation();
-        verify(mWm).moveFocusToActivity(eq(secondaryActivity));
+        final WindowState mostRecentUsedWindow =
+                mWm.getMostRecentUsedEmbeddedWindowForBack(primaryWindow);
+        assertThat(mostRecentUsedWindow).isEqualTo(secondaryWindow);
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index d318f00..44c7057b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -19,19 +19,12 @@
 import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
 import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
-import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
-import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -119,8 +112,6 @@
         mController = new LetterboxUiController(mWm, mActivity);
     }
 
-
-
     @Test
     public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() {
         final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
@@ -320,164 +311,6 @@
         return mainWindow;
     }
 
-    // shouldApplyUser...Override
-    @Test
-    public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
-                /* value */ true);
-
-        doReturn(false).when(mAppCompatConfiguration).isUserAppAspectRatioFullscreenEnabled();
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserFullscreenOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserFullscreenOverride_falseFullscreenProperty_returnsFalse()
-            throws Exception {
-        prepareActivityThatShouldApplyUserFullscreenOverride();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
-                /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserFullscreenOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserFullscreenOverride_falseSettingsProperty_returnsFalse()
-            throws Exception {
-        prepareActivityThatShouldApplyUserFullscreenOverride();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserFullscreenOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserFullscreenOverride_returnsTrue() {
-        prepareActivityThatShouldApplyUserFullscreenOverride();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserFullscreenOverride());
-    }
-
-    @Test
-    public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse()
-            throws Exception {
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldEnableUserAspectRatioSettings());
-    }
-
-    @Test
-    public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue()
-            throws Exception {
-
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldEnableUserAspectRatioSettings());
-    }
-
-    @Test
-    public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientation_returnsFalse()
-            throws Exception {
-        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldEnableUserAspectRatioSettings());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse()
-            throws Exception {
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldEnableUserAspectRatioSettings());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_trueProperty_returnsFalse()
-            throws Exception {
-        doReturn(false).when(mAppCompatConfiguration).isUserAppAspectRatioSettingsEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserMinAspectRatioOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_disabledIgnoreOrientationRequest() {
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-        mDisplayContent.setIgnoreOrientationRequest(false);
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserMinAspectRatioOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_returnsTrue() {
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserMinAspectRatioOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientation_returnsFalse() {
-        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserMinAspectRatioOverride());
-    }
-
-    private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
-            boolean orientationRequest) {
-        spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
-        doReturn(orientationRequest).when(
-                mAppCompatConfiguration).isUserAppAspectRatioSettingsEnabled();
-        mDisplayContent.setIgnoreOrientationRequest(true);
-        doReturn(USER_MIN_ASPECT_RATIO_3_2)
-                .when(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
-                .getUserMinAspectRatioOverrideCode();
-    }
-
-    private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
-        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ true);
-    }
-
-    private void prepareActivityThatShouldApplyUserFullscreenOverride() {
-        spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
-        doReturn(true).when(mAppCompatConfiguration).isUserAppAspectRatioFullscreenEnabled();
-        mDisplayContent.setIgnoreOrientationRequest(true);
-        doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN)
-                .when(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
-                .getUserMinAspectRatioOverrideCode();
-    }
-
     // shouldUseDisplayLandscapeNaturalOrientation
 
     @Test
@@ -595,156 +428,6 @@
     }
 
     @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_overrideEnabled_returnsTrue() {
-        mActivity = setUpActivityWithComponent();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_propertyTrue_overrideEnabled_returnsTrue()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_propertyTrue_overrideDisabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_overrideDisabled_returnsFalse() {
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_propertyFalse_noOverride_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
-        mActivity = setUpActivityWithComponent();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsTrue()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-        doReturn(false).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideDisabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_overrideDisabled_returnsFalse() {
-        mActivity = setUpActivityWithComponent();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_overrideEnabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_noOverride_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
     @EnableCompatChanges({FORCE_RESIZE_APP})
     public void testshouldOverrideForceResizeApp_overrideEnabled_returnsTrue() {
         mController = new LetterboxUiController(mWm, mActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
deleted file mode 100644
index d535677..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.internal.policy.TaskResizingAlgorithm.MIN_ASPECT;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.DisplayMetrics;
-import android.util.Log;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link TaskPositioner} class.
- *
- * Build/Install/Run:
- *  atest WmTests:TaskPositionerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskPositionerTests extends WindowTestsBase {
-
-    private static final boolean DEBUGGING = false;
-    private static final String TAG = "TaskPositionerTest";
-
-    private static final int MOUSE_DELTA_X = 5;
-    private static final int MOUSE_DELTA_Y = 5;
-
-    private int mMinVisibleWidth;
-    private int mMinVisibleHeight;
-    private TaskPositioner mPositioner;
-
-    @Before
-    public void setUp() {
-        TaskPositioner.setFactory(null);
-
-        final DisplayMetrics dm = mDisplayContent.getDisplayMetrics();
-
-        // This should be the same calculation as the TaskPositioner uses.
-        mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
-        mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
-        removeGlobalMinSizeRestriction();
-
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setCreateTask(true)
-                .build();
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "window");
-        mPositioner = new TaskPositioner(mWm);
-        mPositioner.register(mDisplayContent, win);
-
-        win.getRootTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
-    }
-
-    @After
-    public void tearDown() {
-        TaskPositioner.setFactory(null);
-    }
-
-    @Test
-    public void testOverrideFactory() {
-        final boolean[] created = new boolean[1];
-        created[0] = false;
-        TaskPositioner.setFactory(new TaskPositioner.Factory() {
-            @Override
-            public TaskPositioner create(WindowManagerService service) {
-                created[0] = true;
-                return null;
-            }
-        });
-
-        assertNull(TaskPositioner.create(mWm));
-        assertTrue(created[0]);
-    }
-
-    /** This tests that the window can move in all directions. */
-    @Test
-    public void testMoveWindow() {
-        final Rect displayBounds = mDisplayContent.getBounds();
-        final int windowSize = Math.min(displayBounds.width(), displayBounds.height()) / 2;
-        final int left = displayBounds.centerX() - windowSize / 2;
-        final int top = displayBounds.centerY() - windowSize / 2;
-        final Rect r = new Rect(left, top, left + windowSize, top + windowSize);
-        mPositioner.mTask.setBounds(r);
-        mPositioner.startDrag(false /* resizing */, false /* preserveOrientation */, left, top);
-
-        // Move upper left.
-        mPositioner.notifyMoveLocked(left - MOUSE_DELTA_X, top - MOUSE_DELTA_Y);
-        r.offset(-MOUSE_DELTA_X, -MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
-        // Move bottom right.
-        mPositioner.notifyMoveLocked(left, top);
-        r.offset(MOUSE_DELTA_X, MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that free resizing will allow to change the orientation as well
-     * as does some basic tests (e.g. dragging in Y only will keep X stable).
-     */
-    @Test
-    public void testBasicFreeWindowResizing() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        final int midY = (r.top + r.bottom) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        // Start a drag resize starting upper left.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
-        // Drag to a good landscape size.
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a good portrait size.
-        mPositioner.resizeDrag(400.0f, 0.0f);
-        assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the width.
-        mPositioner.resizeDrag(2000.0f, r.top);
-        assertBoundsEquals(
-                new Rect(r.right - mMinVisibleWidth, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the height.
-        mPositioner.resizeDrag(r.left, 2000.0f);
-        assertBoundsEquals(
-                new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Start a drag resize left and see that only the left coord changes..
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, midY);
-
-        // Drag to the left.
-        mPositioner.resizeDrag(0.0f, midY);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right.
-        mPositioner.resizeDrag(200.0f, midY);
-        assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top
-        mPositioner.resizeDrag(r.left, 0.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored.
-     */
-    @Test
-    public void testFreeWindowResizingTestAllEdges() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        final int midX = (r.left + r.right) / 2;
-        final int midY = (r.top + r.bottom) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        // Drag upper left.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag upper.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */, midX,
-                r.top - MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag upper right.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.right + MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(r.right + 100, 0.0f);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag right.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.right + MOUSE_DELTA_X, midY);
-        mPositioner.resizeDrag(r.right + 100, 0.0f);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag bottom right.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.right + MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag bottom.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */, midX,
-                r.bottom + MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag bottom left.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(0.0f, r.bottom + 100);
-        assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag left.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, midY);
-        mPositioner.resizeDrag(0.0f, r.bottom + 100);
-        assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-    }
-
-    /**
-     * This tests that a constrained landscape window will keep the aspect and do the
-     * right things upon resizing when dragged from the top left corner.
-     */
-    @Test
-    public void testLandscapePreservedWindowResizingDragTopLeft() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
-        // Drag to a good landscape size.
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a good portrait size.
-        mPositioner.resizeDrag(400.0f, 0.0f);
-        int width = Math.round((float) (r.bottom - MOUSE_DELTA_Y) * MIN_ASPECT);
-        assertBoundsEquals(new Rect(r.right - width, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the width.
-        mPositioner.resizeDrag(2000.0f, r.top);
-        final int w = mMinVisibleWidth;
-        final int h = Math.round(w / MIN_ASPECT);
-        assertBoundsEquals(new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the height.
-        mPositioner.resizeDrag(r.left, 2000.0f);
-        assertBoundsEquals(
-                new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained landscape window will keep the aspect and do the
-     * right things upon resizing when dragged from the left corner.
-     */
-    @Test
-    public void testLandscapePreservedWindowResizingDragLeft() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        final int midY = (r.top + r.bottom) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, midY);
-
-        // Drag to the left.
-        mPositioner.resizeDrag(0.0f, midY);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right.
-        mPositioner.resizeDrag(200.0f, midY);
-        assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag all the way to the right and see the height also shrinking.
-        mPositioner.resizeDrag(2000.0f, midY);
-        final int w = mMinVisibleWidth;
-        final int h = Math.round((float) w / MIN_ASPECT);
-        assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top.
-        mPositioner.resizeDrag(r.left, 0.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom.
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained landscape window will keep the aspect and do the
-     * right things upon resizing when dragged from the top corner.
-     */
-    @Test
-    public void testLandscapePreservedWindowResizingDragTop() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        final int midX = (r.left + r.right) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /*resizing*/, true /*preserveOrientation*/, midX,
-                r.top - MOUSE_DELTA_Y);
-
-        // Drag to the left (no change).
-        mPositioner.resizeDrag(0.0f, r.top);
-        assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right (no change).
-        mPositioner.resizeDrag(2000.0f, r.top);
-        assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top.
-        mPositioner.resizeDrag(300.0f, 0.0f);
-        int h = r.bottom - MOUSE_DELTA_Y;
-        int w = Math.max(r.right - r.left, Math.round(h * MIN_ASPECT));
-        assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom.
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        h = mMinVisibleHeight;
-        assertBoundsEquals(new Rect(r.left, r.bottom - h, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained portrait window will keep the aspect and do the
-     * right things upon resizing when dragged from the top left corner.
-     */
-    @Test
-    public void testPortraitPreservedWindowResizingDragTopLeft() {
-        final Rect r = new Rect(330, 100, 630, 600);
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /*resizing*/, true /*preserveOrientation*/,
-                r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
-        // Drag to a good landscape size.
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        int height = Math.round((float) (r.right - MOUSE_DELTA_X) * MIN_ASPECT);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.bottom - height, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a good portrait size.
-        mPositioner.resizeDrag(400.0f, 0.0f);
-        assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the height and the the width shrinking.
-        mPositioner.resizeDrag(r.left + MOUSE_DELTA_X, 2000.0f);
-        final int w = Math.max(mMinVisibleWidth, Math.round(mMinVisibleHeight / MIN_ASPECT));
-        final int h = Math.max(mMinVisibleHeight, Math.round(w * MIN_ASPECT));
-        assertBoundsEquals(
-                new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained portrait window will keep the aspect and do the
-     * right things upon resizing when dragged from the left corner.
-     */
-    @Test
-    public void testPortraitPreservedWindowResizingDragLeft() {
-        final Rect r = new Rect(330, 100, 630, 600);
-        final int midY = (r.top + r.bottom) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, midY);
-
-        // Drag to the left.
-        mPositioner.resizeDrag(0.0f, midY);
-        int w = r.right - MOUSE_DELTA_X;
-        int h = Math.round(w * MIN_ASPECT);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.top + h),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right.
-        mPositioner.resizeDrag(450.0f, midY);
-        assertBoundsEquals(new Rect(450 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag all the way to the right.
-        mPositioner.resizeDrag(2000.0f, midY);
-        w = mMinVisibleWidth;
-        h = Math.max(Math.round((float) w * MIN_ASPECT), r.height());
-        assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top.
-        mPositioner.resizeDrag(r.left, 0.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom.
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained portrait window will keep the aspect and do the
-     * right things upon resizing when dragged from the top corner.
-     */
-    @Test
-    public void testPortraitPreservedWindowResizingDragTop() {
-        final Rect r = new Rect(330, 100, 630, 600);
-        final int midX = (r.left + r.right) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */, midX,
-                r.top - MOUSE_DELTA_Y);
-
-        // Drag to the left (no change).
-        mPositioner.resizeDrag(0.0f, r.top);
-        assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right (no change).
-        mPositioner.resizeDrag(2000.0f, r.top);
-        assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top.
-        mPositioner.resizeDrag(300.0f, 0.0f);
-        int h = r.bottom - MOUSE_DELTA_Y;
-        int w = Math.min(r.width(), Math.round(h / MIN_ASPECT));
-        assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom.
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        h = Math.max(mMinVisibleHeight, Math.round(mMinVisibleWidth * MIN_ASPECT));
-        w = Math.round(h / MIN_ASPECT);
-        assertBoundsEquals(new Rect(r.left, r.bottom - h, r.left + w, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    private static void assertBoundsEquals(Rect expected, Rect actual) {
-        if (DEBUGGING) {
-            if (!expected.equals(actual)) {
-                Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString()
-                        + ") " + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    public void testFinishingMovingWhenBinderDied() {
-        spyOn(mWm.mTaskPositioningController);
-
-        mPositioner.startDrag(false, false, 0 /* startX */, 0 /* startY */);
-        verify(mWm.mTaskPositioningController, never()).finishTaskPositioning();
-        mPositioner.binderDied();
-        verify(mWm.mTaskPositioningController).finishTaskPositioning();
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
deleted file mode 100644
index bfc13d3..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-import android.view.InputChannel;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link TaskPositioningController} class.
- *
- * Build/Install/Run:
- *  atest WmTests:TaskPositioningControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskPositioningControllerTests extends WindowTestsBase {
-    private static final int TIMEOUT_MS = 1000;
-
-    private TaskPositioningController mTarget;
-    private WindowState mWindow;
-
-    @Before
-    public void setUp() throws Exception {
-        assertNotNull(mWm.mTaskPositioningController);
-        mTarget = mWm.mTaskPositioningController;
-
-        when(mWm.mInputManager.transferTouchGesture(any(), any())).thenReturn(true);
-
-        mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
-        mWindow.getTask().setResizeMode(RESIZE_MODE_RESIZEABLE);
-        mWindow.mInputChannel = new InputChannel();
-        mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
-        doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor();
-    }
-
-    @FlakyTest(bugId = 291067614)
-    @Test
-    public void testStartAndFinishPositioning() {
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-
-        assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
-
-        assertTrue(mTarget.isPositioningLocked());
-        assertNotNull(mTarget.getDragWindowHandleLocked());
-
-        mTarget.finishTaskPositioning();
-        // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-    }
-
-    @Test
-    public void testFinishPositioningWhenAppRequested() {
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-
-        assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
-
-        assertTrue(mTarget.isPositioningLocked());
-        assertNotNull(mTarget.getDragWindowHandleLocked());
-
-        mTarget.finishTaskPositioning(mWindow.mClient);
-        // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-    }
-
-    @Test
-    public void testHandleTapOutsideTask() {
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-
-        final DisplayContent content = mock(DisplayContent.class);
-        doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
-        assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
-
-        mTarget.handleTapOutsideTask(content, 0, 0);
-        // Wait until the looper processes handleTapOutsideTask.
-        assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
-
-        assertTrue(mTarget.isPositioningLocked());
-        assertNotNull(mTarget.getDragWindowHandleLocked());
-
-        mTarget.finishTaskPositioning();
-        // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-    }
-
-    @Test
-    public void testHandleTapOutsideNonResizableTask() {
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-
-        final DisplayContent content = mock(DisplayContent.class);
-        doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
-        assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
-
-        mWindow.getTask().setResizeMode(RESIZE_MODE_UNRESIZEABLE);
-
-        mTarget.handleTapOutsideTask(content, 0, 0);
-        // Wait until the looper processes handleTapOutsideTask.
-        assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
-
-        assertFalse(mTarget.isPositioningLocked());
-    }
-
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 7d01b79..720457e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1573,7 +1573,8 @@
         enteringAnimReports.clear();
         doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), anyBoolean());
         final boolean[] wasInFinishingTransition = { false };
-        controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() {
+        controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener(
+                mDisplayContent.mDisplayId) {
             @Override
             public void onAppTransitionFinishedLocked(IBinder token) {
                 final ActivityRecord r = ActivityRecord.forToken(token);
@@ -1582,6 +1583,14 @@
                 }
             }
         });
+        final boolean[] calledListenerOnOtherDisplay = { false };
+        controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener(
+                mDisplayContent.mDisplayId + 1234) {
+            @Override
+            public void onAppTransitionFinishedLocked(IBinder token) {
+                calledListenerOnOtherDisplay[0] = true;
+            }
+        });
         assertTrue(activity1.isVisible());
         doReturn(false).when(task1).isTranslucent(null);
         doReturn(false).when(task1).isTranslucentForTransition();
@@ -1592,6 +1601,7 @@
 
         controller.finishTransition(closeTransition);
         assertTrue(wasInFinishingTransition[0]);
+        assertFalse(calledListenerOnOtherDisplay[0]);
         assertNull(controller.mFinishingTransition);
 
         assertTrue(activity2.isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index e6648da..0cb22ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
 import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -86,6 +87,7 @@
 
         ApplicationInfo info = mock(ApplicationInfo.class);
         info.packageName = "test.package.name";
+        doReturn(true).when(info).isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED);
         mWpc = new WindowProcessController(
                 mAtm, info, null, 0, -1, null, mMockListener);
         mWpc.setThread(mock(IApplicationThread.class));
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
index e5f2f89..eda78cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
@@ -63,9 +63,6 @@
         resetCache();
     }
 
-    private static final String SYSTEM_PROPERTY_OVERRIDE_KEY =
-            "sys.wmshell.desktopmode.dev_toggle_override";
-
     @Test
     @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@@ -190,110 +187,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_noProperty_overrideOn_featureFlagOff_returnsTrueAndPropertyOn() {
-        System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
-        setOverride(OVERRIDE_ON.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
-        // Store System Property if not present
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_ON.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_noProperty_overrideUnset_featureFlagOn_returnsTrueAndPropertyUnset() {
-        System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
-        setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
-        // Store System Property if not present
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(
-                        DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
-    }
-
-    @Test
-    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_noProperty_overrideUnset_featureFlagOff_returnsFalseAndPropertyUnset() {
-        System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
-        setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
-        // Store System Property if not present
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(
-                        DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_propertyNotInt_overrideOff_featureFlagOn_returnsFalseAndPropertyOff() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc");
-        setOverride(OVERRIDE_OFF.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
-        // Store System Property if currently invalid
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_propertyInvalid_overrideOff_featureFlagOn_returnsFalseAndPropertyOff() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2");
-        setOverride(OVERRIDE_OFF.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
-        // Store System Property if currently invalid
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_propertyOff_overrideOn_featureFlagOn_returnsFalseAndnoPropertyUpdate() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(
-                OVERRIDE_OFF.getSetting()));
-        setOverride(OVERRIDE_ON.getSetting());
-
-        // Have a consistent override until reboot
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
-    }
-
-    @Test
-    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_propertyOn_overrideOff_featureFlagOff_returnsTrueAndnoPropertyUpdate() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(OVERRIDE_ON.getSetting()));
-        setOverride(OVERRIDE_OFF.getSetting());
-
-        // Have a consistent override until reboot
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_ON.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_propertyUnset_overrideOff_featureFlagOn_returnsTrueAndnoPropertyUpdate() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY,
-                String.valueOf(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
-        setOverride(OVERRIDE_OFF.getSetting());
-
-        // Have a consistent override until reboot
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(
-                        DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
-    }
-
-    @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
             FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY})
     public void isEnabled_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() {
@@ -452,8 +345,5 @@
                 "sCachedToggleOverride");
         cachedToggleOverride.setAccessible(true);
         cachedToggleOverride.set(null, null);
-
-        // Clear override cache stored in System property
-        System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
     }
 }
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index f367c38..06c2651 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -48,6 +48,7 @@
         "testables",
         "testng",
         "truth",
+        "ui-trace-collector",
     ],
     libs: [
         "android.test.mock",
diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml
index 4a99bd4..bc9322f 100644
--- a/tests/Input/AndroidTest.xml
+++ b/tests/Input/AndroidTest.xml
@@ -22,6 +22,10 @@
         <option name="shell-timeout" value="660s" />
         <option name="test-timeout" value="600s" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/>
+        <!-- DefaultUITraceListener args -->
+        <option name="instrumentation-arg" key="skip_test_success_metrics" value="true"/>
+        <option name="instrumentation-arg" key="per_class" value="true"/>
     </test>
     <object class="com.android.tradefed.testtype.suite.module.TestFailureModuleController"
             type="module_controller">
@@ -32,6 +36,8 @@
         <option name="pull-pattern-keys" value="input_.*" />
         <!-- Pull files created by tests, like the output of screenshot tests -->
         <option name="directory-keys" value="/sdcard/Download/InputTests" />
+        <!-- Pull perfetto traces from DefaultUITraceListener -->
+        <option name="pull-pattern-keys" value="perfetto_file_path*" />
         <option name="collect-on-run-ended-only" value="false" />
     </metrics_collector>
 </configuration>
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index b6672a0..fad94d4 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -698,7 +698,7 @@
             traceMonitor.start();
 
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
-                    "My test null string: %s", null);
+                    "My test null string: %s", (Object) null);
         } finally {
             traceMonitor.stop(mWriter);
         }