Merge "Serialize PowerStatsExporter sessions" into main
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index a4ca55e..2f515fe 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -201,7 +201,9 @@
      * @param exceptionHandler an optional {@link RemoteException} handler
      */
     @AnyThread
-    @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @RequiresPermission(allOf = {
+            Manifest.permission.INTERNAL_SYSTEM_WINDOW,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     static void removeImeSurface(int displayId,
             @Nullable Consumer<RemoteException> exceptionHandler) {
         final IInputMethodManager service = getService();
@@ -441,7 +443,9 @@
     }
 
     @AnyThread
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @RequiresPermission(allOf = {
+            Manifest.permission.WRITE_SECURE_SETTINGS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     static void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
         final IInputMethodManager service = getService();
         if (service == null) {
@@ -469,7 +473,9 @@
     }
 
     @AnyThread
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @RequiresPermission(allOf = {
+            Manifest.permission.WRITE_SECURE_SETTINGS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     static void onImeSwitchButtonClickFromSystem(int displayId) {
         final IInputMethodManager service = getService();
         if (service == null) {
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
index fa51957..23a1224 100644
--- a/core/java/android/window/TaskFragmentInfo.java
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -102,6 +102,8 @@
     @NonNull
     private final Point mMinimumDimensions = new Point();
 
+    private final boolean mIsTopNonFishingChild;
+
     /** @hide */
     public TaskFragmentInfo(
             @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
@@ -110,7 +112,7 @@
             @NonNull List<IBinder> inRequestedTaskFragmentActivities,
             @NonNull Point positionInParent, boolean isTaskClearedForReuse,
             boolean isTaskFragmentClearedForPip, boolean isClearedForReorderActivityToFront,
-            @NonNull Point minimumDimensions) {
+            @NonNull Point minimumDimensions, boolean isTopNonFinishingChild) {
         mFragmentToken = requireNonNull(fragmentToken);
         mToken = requireNonNull(token);
         mConfiguration.setTo(configuration);
@@ -123,6 +125,7 @@
         mIsTaskFragmentClearedForPip = isTaskFragmentClearedForPip;
         mIsClearedForReorderActivityToFront = isClearedForReorderActivityToFront;
         mMinimumDimensions.set(minimumDimensions);
+        mIsTopNonFishingChild = isTopNonFinishingChild;
     }
 
     @NonNull
@@ -212,6 +215,16 @@
     }
 
     /**
+     * Indicates that this TaskFragment is the top non-finishing child of its parent container
+     * among all Activities and TaskFragment siblings.
+     *
+     * @hide
+     */
+    public boolean isTopNonFinishingChild() {
+        return mIsTopNonFishingChild;
+    }
+
+    /**
      * Returns {@code true} if the parameters that are important for task fragment organizers are
      * equal between this {@link TaskFragmentInfo} and {@param that}.
      * Note that this method is usually called with
@@ -236,7 +249,8 @@
                 && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse
                 && mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip
                 && mIsClearedForReorderActivityToFront == that.mIsClearedForReorderActivityToFront
-                && mMinimumDimensions.equals(that.mMinimumDimensions);
+                && mMinimumDimensions.equals(that.mMinimumDimensions)
+                && mIsTopNonFishingChild == that.mIsTopNonFishingChild;
     }
 
     private TaskFragmentInfo(Parcel in) {
@@ -252,6 +266,7 @@
         mIsTaskFragmentClearedForPip = in.readBoolean();
         mIsClearedForReorderActivityToFront = in.readBoolean();
         mMinimumDimensions.readFromParcel(in);
+        mIsTopNonFishingChild = in.readBoolean();
     }
 
     /** @hide */
@@ -269,6 +284,7 @@
         dest.writeBoolean(mIsTaskFragmentClearedForPip);
         dest.writeBoolean(mIsClearedForReorderActivityToFront);
         mMinimumDimensions.writeToParcel(dest, flags);
+        dest.writeBoolean(mIsTopNonFishingChild);
     }
 
     @NonNull
@@ -299,6 +315,7 @@
                 + " isTaskFragmentClearedForPip=" + mIsTaskFragmentClearedForPip
                 + " mIsClearedForReorderActivityToFront=" + mIsClearedForReorderActivityToFront
                 + " minimumDimensions=" + mMinimumDimensions
+                + " isTopNonFinishingChild=" + mIsTopNonFishingChild
                 + "}";
     }
 
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 4230641..4c18bbf 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -51,13 +51,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "embedded_activity_back_nav_flag"
-    description: "Refines embedded activity back navigation behavior"
-    bug: "293642394"
-}
-
-flag {
-    namespace: "windowing_sdk"
     name: "cover_display_opt_in"
     is_exported: true
     description: "Properties to allow apps and activities to opt-in to cover display rendering"
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 3f7ba0a..b51678e 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -125,9 +125,9 @@
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
 
-    @EnforcePermission("WRITE_SECURE_SETTINGS")
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
-            + "android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    @EnforcePermission(allOf = {"WRITE_SECURE_SETTINGS", "INTERACT_ACROSS_USERS_FULL"})
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf = {android.Manifest."
+    + "permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})")
     void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId);
 
     @EnforcePermission("TEST_INPUT_METHOD")
@@ -143,9 +143,9 @@
      * @param displayId The ID of the display where the input method picker dialog should be shown.
      * @param userId    The ID of the user that triggered the click.
      */
-    @EnforcePermission("WRITE_SECURE_SETTINGS")
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
-            + "android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    @EnforcePermission(allOf = {"WRITE_SECURE_SETTINGS" ,"INTERACT_ACROSS_USERS_FULL"})
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf = {android.Manifest."
+    + "permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})")
     oneway void onImeSwitchButtonClickFromSystem(int displayId);
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
@@ -168,9 +168,9 @@
 
     oneway void reportPerceptibleAsync(in IBinder windowToken, boolean perceptible);
 
-    @EnforcePermission("INTERNAL_SYSTEM_WINDOW")
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
-            + "android.Manifest.permission.INTERNAL_SYSTEM_WINDOW)")
+    @EnforcePermission(allOf = {"INTERNAL_SYSTEM_WINDOW", "INTERACT_ACROSS_USERS_FULL"})
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf = {android.Manifest."
+    + "permission.INTERNAL_SYSTEM_WINDOW, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})")
     void removeImeSurface(int displayId);
 
     /** Remove the IME surface. Requires passing the currently focused window. */
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7c62615..638591f 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2292,7 +2292,7 @@
                                  criteria.mValue.mUsage);
                 jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
                                                     gAudioMixMatchCriterionAttrCstor,
-                                                    jMixMatchCriterion, criteria.mRule);
+                                                    jAudioAttributes, criteria.mRule);
                 break;
             case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
                 jAudioAttributes = env->NewObject(gAudioAttributesClass, gAudioAttributesCstor);
@@ -2300,7 +2300,7 @@
                                  criteria.mValue.mSource);
                 jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
                                                     gAudioMixMatchCriterionAttrCstor,
-                                                    jMixMatchCriterion, criteria.mRule);
+                                                    jAudioAttributes, criteria.mRule);
                 break;
         }
         env->CallBooleanMethod(jAudioMixMatchCriterionList, gArrayListMethods.add,
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ec865f6..e94db2d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6555,4 +6555,7 @@
     <string name="keyboard_shortcut_group_applications_maps">Maps</string>
     <!-- User visible title for the keyboard shortcut group containing system-wide application launch shortcuts. [CHAR-LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications">Applications</string>
+
+    <!-- Fingerprint loe notification string -->
+    <string name="fingerprint_loe_notification_msg">Your fingerprints can no longer be recognized. Set up Fingerprint Unlock again.</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6b58396..cbf3fe7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5584,4 +5584,7 @@
   <java-symbol type="string" name="keyboard_shortcut_group_applications_music" />
   <java-symbol type="string" name="keyboard_shortcut_group_applications_sms" />
   <java-symbol type="string" name="keyboard_shortcut_group_applications" />
+
+  <!-- Fingerprint loe notification string -->
+  <java-symbol type="string" name="fingerprint_loe_notification_msg" />
 </resources>
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
index b13bcd1..444ed51 100644
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java
@@ -25,7 +25,7 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index 3e45a79..46f22ce 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -26,7 +26,7 @@
 import android.Manifest.permission;
 import android.content.Context;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
index bc12e72..7413ede 100644
--- a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
+++ b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index d984d86..63eeaa1 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -26,7 +26,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index 267fc2b..024d614 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -29,7 +29,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import libcore.util.HexEncoding;
 
diff --git a/core/tests/coretests/src/android/net/sntp/Duration64Test.java b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
index b228596..b177e18 100644
--- a/core/tests/coretests/src/android/net/sntp/Duration64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
@@ -23,7 +23,7 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
index 200c80e..9f95132 100644
--- a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
@@ -23,7 +23,7 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 8e1fde0..409cde3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -119,7 +119,8 @@
 
     // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
     //  association. It's not set in WM Extensions nor Wm Jetpack library currently.
-    private static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY =
+    @VisibleForTesting
+    static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY =
             "androidx.window.extensions.embedding.shouldAssociateWithLaunchingActivity";
 
     @VisibleForTesting
@@ -2742,89 +2743,70 @@
         }
 
         final int taskId = getTaskId(launchActivity);
-        if (!overlayContainers.isEmpty()) {
-            for (final TaskFragmentContainer overlayContainer : overlayContainers) {
-                final boolean isTopNonFinishingOverlay = overlayContainer.equals(
-                        overlayContainer.getTaskContainer().getTopNonFinishingTaskFragmentContainer(
-                                true /* includePin */, true /* includeOverlay */));
-                if (taskId != overlayContainer.getTaskId()) {
-                    // If there's an overlay container with same tag in a different task,
-                    // dismiss the overlay container since the tag must be unique per process.
-                    if (overlayTag.equals(overlayContainer.getOverlayTag())) {
-                        Log.w(TAG, "The overlay container with tag:"
-                                + overlayContainer.getOverlayTag() + " is dismissed because"
-                                + " there's an existing overlay container with the same tag but"
-                                + " different task ID:" + overlayContainer.getTaskId() + ". "
-                                + "The new associated activity is " + launchActivity);
-                        mPresenter.cleanupContainer(wct, overlayContainer,
-                                false /* shouldFinishDependant */);
-                    }
-                    continue;
-                }
-                if (!overlayTag.equals(overlayContainer.getOverlayTag())) {
-                    // If there's an overlay container with different tag on top in the same
-                    // task, dismiss the existing overlay container.
-                    if (isTopNonFinishingOverlay) {
-                        mPresenter.cleanupContainer(wct, overlayContainer,
-                                false /* shouldFinishDependant */);
-                    }
-                    continue;
-                }
-                // The overlay container has the same tag and task ID with the new launching
-                // overlay container.
-                if (!isTopNonFinishingOverlay) {
-                    // Dismiss the invisible overlay container regardless of activity
-                    // association if it collides the tag of new launched overlay container .
-                    Log.w(TAG, "The invisible overlay container with tag:"
-                            + overlayContainer.getOverlayTag() + " is dismissed because"
-                            + " there's a launching overlay container with the same tag."
-                            + " The new associated activity is " + launchActivity);
-                    mPresenter.cleanupContainer(wct, overlayContainer,
-                            false /* shouldFinishDependant */);
-                    continue;
-                }
-                // Requesting an always-on-top overlay.
-                if (!associateLaunchingActivity) {
-                    if (overlayContainer.isOverlayWithActivityAssociation()) {
-                        // Dismiss the overlay container since it has associated with an activity.
-                        Log.w(TAG, "The overlay container with tag:"
-                                + overlayContainer.getOverlayTag() + " is dismissed because"
-                                + " there's an existing overlay container with the same tag but"
-                                + " different associated launching activity. The overlay container"
-                                + " doesn't associate with any activity.");
-                        mPresenter.cleanupContainer(wct, overlayContainer,
-                                false /* shouldFinishDependant */);
-                        continue;
-                    } else {
-                        // The existing overlay container doesn't associate an activity as well.
-                        // Just update the overlay and return.
-                        // Note that going to this condition means the tag, task ID matches a
-                        // visible always-on-top overlay, and won't dismiss any overlay any more.
-                        mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
-                                getMinDimensions(intent));
-                        return overlayContainer;
-                    }
-                }
-                if (launchActivity.getActivityToken()
-                        != overlayContainer.getAssociatedActivityToken()) {
-                    Log.w(TAG, "The overlay container with tag:"
-                            + overlayContainer.getOverlayTag() + " is dismissed because"
-                            + " there's an existing overlay container with the same tag but"
-                            + " different associated launching activity. The new associated"
-                            + " activity is " + launchActivity);
-                    // The associated activity must be the same, or it will be dismissed.
-                    mPresenter.cleanupContainer(wct, overlayContainer,
-                            false /* shouldFinishDependant */);
-                    continue;
-                }
-                // Reaching here means the launching activity launch an overlay container with the
-                // same task ID, tag, while there's a previously launching visible overlay
-                // container. We'll regard it as updating the existing overlay container.
+        // Overlay container policy:
+        // 1. Overlay tag must be unique per process.
+        //   a. For associated overlay, if a new launched overlay container has the same tag as
+        //      an existing one, the existing overlay will be dismissed regardless of its task
+        //      and window hierarchy.
+        //   b. For always-on-top overlay, if there's an overlay container has the same tag in the
+        //      launched task, the overlay container will be re-used, which means the
+        //      ActivityStackAttributes will be applied and the launched activity will be positioned
+        //      on top of the overlay container.
+        // 2. There must be at most one overlay that partially occludes a visible activity per task.
+        //   a. For associated overlay, only the top visible overlay container in the launched task
+        //      will be dismissed.
+        //   b. Always-on-top overlay is always visible. If there's an overlay with different tags
+        //      in the same task, the overlay will be dismissed in case an activity above
+        //      the overlay is dismissed and the overlay is shown unexpectedly.
+        for (final TaskFragmentContainer overlayContainer : overlayContainers) {
+            final boolean isTopNonFinishingOverlay = overlayContainer.isTopNonFinishingChild();
+            final boolean areInSameTask = taskId == overlayContainer.getTaskId();
+            final boolean haveSameTag = overlayTag.equals(overlayContainer.getOverlayTag());
+            if (!associateLaunchingActivity && overlayContainer.isAlwaysOnTopOverlay()
+                    && haveSameTag && areInSameTask) {
+                // Just launch the activity and update the existing always-on-top overlay
+                // if the requested overlay is an always-on-top overlay with the same tag
+                // as the existing one.
                 mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
                         getMinDimensions(intent));
                 return overlayContainer;
-
             }
+            if (haveSameTag) {
+                // For other tag match, we should clean up the existing overlay since the overlay
+                // tag must be unique per process.
+                Log.w(TAG, "The overlay container with tag:"
+                        + overlayContainer.getOverlayTag() + " is dismissed with "
+                        + " the launching activity=" + launchActivity
+                        + " because there's an existing overlay container with the same tag.");
+                mPresenter.cleanupContainer(wct, overlayContainer,
+                        false /* shouldFinishDependant */);
+            }
+            if (!areInSameTask) {
+                // Early return here because we won't clean-up or update overlay from different
+                // tasks except tag collision.
+                continue;
+            }
+            if (associateLaunchingActivity) {
+                // For associated overlay, we only dismiss the overlay if it's the top non-finishing
+                // child of its parent container.
+                if (isTopNonFinishingOverlay) {
+                    Log.w(TAG, "The on-top overlay container with tag:"
+                            + overlayContainer.getOverlayTag() + " is dismissed with "
+                            + " the launching activity=" + launchActivity
+                            + "because we only allow one overlay on top.");
+                    mPresenter.cleanupContainer(wct, overlayContainer,
+                            false /* shouldFinishDependant */);
+                }
+                continue;
+            }
+            // Otherwise, we should clean up the overlay in the task because we only allow one
+            // overlay when an always-on-top overlay is launched.
+            Log.w(TAG, "The overlay container with tag:"
+                    + overlayContainer.getOverlayTag() + " is dismissed with "
+                    + " the launching activity=" + launchActivity
+                    + "because an always-on-top overlay is launched.");
+            mPresenter.cleanupContainer(wct, overlayContainer,
+                    false /* shouldFinishDependant */);
         }
         // Launch the overlay container to the task with taskId.
         return createEmptyContainer(wct, intent, taskId, attrs, launchActivity, overlayTag,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 7173b0c..d0e2c99 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -340,6 +340,13 @@
         return mInfo != null && mInfo.isVisible();
     }
 
+    /**
+     * See {@link TaskFragmentInfo#isTopNonFinishingChild()}
+     */
+    boolean isTopNonFinishingChild() {
+        return mInfo != null && mInfo.isTopNonFinishingChild();
+    }
+
     /** Whether the TaskFragment is in an intermediate state waiting for the server update.*/
     boolean isInIntermediateState() {
         if (mInfo == null) {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index d649c6d..7dc78fd 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -163,12 +163,14 @@
     }
 
     /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
+    @NonNull
     static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
             @NonNull Activity activity) {
         return createMockTaskFragmentInfo(container, activity, true /* isVisible */);
     }
 
     /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
+    @NonNull
     static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
             @NonNull Activity activity, boolean isVisible) {
         return new TaskFragmentInfo(container.getTaskFragmentToken(),
@@ -182,7 +184,27 @@
                 false /* isTaskClearedForReuse */,
                 false /* isTaskFragmentClearedForPip */,
                 false /* isClearedForReorderActivityToFront */,
-                new Point());
+                new Point(),
+                false /* isTopChild */);
+    }
+
+    /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
+    @NonNull
+    static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
+            @NonNull Activity activity, boolean isVisible, boolean isOnTop) {
+        return new TaskFragmentInfo(container.getTaskFragmentToken(),
+                mock(WindowContainerToken.class),
+                new Configuration(),
+                1,
+                isVisible,
+                Collections.singletonList(activity.getActivityToken()),
+                new ArrayList<>(),
+                new Point(),
+                false /* isTaskClearedForReuse */,
+                false /* isTaskFragmentClearedForPip */,
+                false /* isClearedForReorderActivityToFront */,
+                new Point(),
+                isOnTop);
     }
 
     static ActivityInfo createActivityInfoWithMinDimensions() {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index ad41b18..8911d18 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -114,6 +114,7 @@
                 mock(WindowContainerToken.class), new Configuration(), 0 /* runningActivityCount */,
                 false /* isVisible */, new ArrayList<>(), new ArrayList<>(), new Point(),
                 false /* isTaskClearedForReuse */, false /* isTaskFragmentClearedForPip */,
-                false /* isClearedForReorderActivityToFront */, new Point());
+                false /* isClearedForReorderActivityToFront */, new Point(),
+                false /* isTopChild */);
     }
 }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 1c4c887..475475b 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -30,6 +30,7 @@
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
+import static androidx.window.extensions.embedding.SplitController.KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY;
 import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM;
 import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT;
 import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT;
@@ -94,6 +95,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -267,7 +269,7 @@
     }
 
     @Test
-    public void testCreateOrUpdateOverlay_visibleOverlaySameTagInTask_dismissOverlay() {
+    public void testCreateOrUpdateOverlay_topOverlayInTask_dismissOverlay() {
         createExistingOverlayContainers();
 
         final TaskFragmentContainer overlayContainer =
@@ -295,26 +297,6 @@
     }
 
     @Test
-    public void testCreateOrUpdateOverlay_sameTagTaskAndActivity_updateOverlay() {
-        createExistingOverlayContainers();
-
-        final Rect bounds = new Rect(0, 0, 100, 100);
-        mSplitController.setActivityStackAttributesCalculator(params ->
-                new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
-        final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
-                mOverlayContainer1.getOverlayTag());
-
-        assertWithMessage("overlayContainer1 must be updated since the new overlay container"
-                + " is launched with the same tag and task")
-                .that(mSplitController.getAllNonFinishingOverlayContainers())
-                .containsExactly(mOverlayContainer1, mOverlayContainer2);
-
-        assertThat(overlayContainer).isEqualTo(mOverlayContainer1);
-        verify(mSplitPresenter).resizeTaskFragment(eq(mTransaction),
-                eq(mOverlayContainer1.getTaskFragmentToken()), eq(bounds));
-    }
-
-    @Test
     public void testCreateOrUpdateOverlay_sameTagAndTaskButNotActivity_dismissOverlay() {
         createExistingOverlayContainers();
 
@@ -362,6 +344,43 @@
     }
 
     @Test
+    public void testCreateOrUpdateAlwaysOnTopOverlay_dismissMultipleOverlaysInTask() {
+        createExistingOverlayContainers();
+        // Create another overlay in task.
+        final TaskFragmentContainer overlayContainer3 =
+                createTestOverlayContainer(TASK_ID, "test3");
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(mOverlayContainer1, mOverlayContainer2, overlayContainer3);
+
+        final TaskFragmentContainer overlayContainer =
+                createOrUpdateAlwaysOnTopOverlay("test4");
+
+        assertWithMessage("overlayContainer1 and overlayContainer3 must be dismissed")
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(mOverlayContainer2, overlayContainer);
+    }
+
+    @Test
+    public void testCreateOrUpdateAlwaysOnTopOverlay_updateOverlay() {
+        createExistingOverlayContainers();
+        // Create another overlay in task.
+        final TaskFragmentContainer alwaysOnTopOverlay = createTestOverlayContainer(TASK_ID,
+                "test3", true /* isVisible */, false /* associateLaunchingActivity */);
+        final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder()
+                .setRelativeBounds(new Rect(0, 0, 100, 100)).build();
+        mSplitController.setActivityStackAttributesCalculator(params -> attrs);
+
+        Mockito.clearInvocations(mSplitPresenter);
+        final TaskFragmentContainer overlayContainer =
+                createOrUpdateAlwaysOnTopOverlay(alwaysOnTopOverlay.getOverlayTag());
+
+        assertWithMessage("overlayContainer1 and overlayContainer3 must be dismissed")
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(mOverlayContainer2, alwaysOnTopOverlay);
+        assertThat(overlayContainer).isEqualTo(alwaysOnTopOverlay);
+    }
+
+    @Test
     public void testCreateOrUpdateOverlay_launchFromSplit_returnNull() {
         final Activity primaryActivity = createMockActivity();
         final Activity secondaryActivity = createMockActivity();
@@ -381,13 +400,13 @@
     }
 
     private void createExistingOverlayContainers() {
-        createExistingOverlayContainers(true /* visible */);
+        createExistingOverlayContainers(true /* isOnTop */);
     }
 
-    private void createExistingOverlayContainers(boolean visible) {
-        mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", visible,
+    private void createExistingOverlayContainers(boolean isOnTop) {
+        mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", isOnTop,
                 true /* associatedLaunchingActivity */, mActivity);
-        mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2", visible);
+        mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2", isOnTop);
         List<TaskFragmentContainer> overlayContainers = mSplitController
                 .getAllNonFinishingOverlayContainers();
         assertThat(overlayContainers).containsExactly(mOverlayContainer1, mOverlayContainer2);
@@ -966,6 +985,16 @@
                 launchOptions, mIntent, activity);
     }
 
+    @Nullable
+    private TaskFragmentContainer createOrUpdateAlwaysOnTopOverlay(
+            @NonNull String tag) {
+        final Bundle launchOptions = new Bundle();
+        launchOptions.putBoolean(KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY, false);
+        launchOptions.putString(KEY_OVERLAY_TAG, tag);
+        return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction,
+                launchOptions, mIntent, createMockActivity());
+    }
+
     /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
     @NonNull
     private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) {
@@ -975,10 +1004,10 @@
     /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
     @NonNull
     private TaskFragmentContainer createMockTaskFragmentContainer(
-            @NonNull Activity activity, boolean isVisible) {
+            @NonNull Activity activity, boolean isOnTop) {
         final TaskFragmentContainer container = createTfContainer(mSplitController,
                 activity.getTaskId(), activity);
-        setupTaskFragmentInfo(container, activity, isVisible);
+        setupTaskFragmentInfo(container, activity, isOnTop);
         return container;
     }
 
@@ -990,8 +1019,8 @@
 
     @NonNull
     private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
-            boolean isVisible) {
-        return createTestOverlayContainer(taskId, tag, isVisible,
+            boolean isOnTop) {
+        return createTestOverlayContainer(taskId, tag, isOnTop,
                 true /* associateLaunchingActivity */);
     }
 
@@ -1002,11 +1031,9 @@
                 null /* launchingActivity */);
     }
 
-    // TODO(b/243518738): add more test coverage on overlay container without activity association
-    //  once we have use cases.
     @NonNull
     private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
-            boolean isVisible, boolean associateLaunchingActivity,
+            boolean isOnTop, boolean associateLaunchingActivity,
             @Nullable Activity launchingActivity) {
         final Activity activity = launchingActivity != null
                 ? launchingActivity : createMockActivity();
@@ -1017,14 +1044,15 @@
                         .setLaunchOptions(Bundle.EMPTY)
                         .setAssociatedActivity(associateLaunchingActivity ? activity : null)
                         .build();
-        setupTaskFragmentInfo(overlayContainer, createMockActivity(), isVisible);
+        setupTaskFragmentInfo(overlayContainer, createMockActivity(), isOnTop);
         return overlayContainer;
     }
 
     private void setupTaskFragmentInfo(@NonNull TaskFragmentContainer container,
                                        @NonNull Activity activity,
-                                       boolean isVisible) {
-        final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity, isVisible);
+                                       boolean isOnTop) {
+        final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity, isOnTop,
+                isOnTop);
         container.setInfo(mTransaction, info);
         mSplitPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), info);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index ea7e968..06c1e68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -104,6 +104,7 @@
             TaskStackListenerImpl taskStackListener,
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
+            PipTouchHandler pipTouchHandler,
             @ShellMainThread ShellExecutor mainExecutor) {
         if (!PipUtils.isPip2ExperimentEnabled()) {
             return Optional.empty();
@@ -112,7 +113,7 @@
                     context, shellInit, shellCommandHandler, shellController, displayController,
                     displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                     pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
-                    pipTransitionState, mainExecutor));
+                    pipTransitionState, pipTouchHandler, mainExecutor));
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 8aa0933..94fe286 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -88,6 +88,7 @@
     private final TaskStackListenerImpl mTaskStackListener;
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final PipTransitionState mPipTransitionState;
+    private final PipTouchHandler mPipTouchHandler;
     private final ShellExecutor mMainExecutor;
     private final PipImpl mImpl;
     private Consumer<Boolean> mOnIsInPipStateChangedListener;
@@ -130,6 +131,7 @@
             TaskStackListenerImpl taskStackListener,
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
+            PipTouchHandler pipTouchHandler,
             ShellExecutor mainExecutor) {
         mContext = context;
         mShellCommandHandler = shellCommandHandler;
@@ -144,6 +146,7 @@
         mShellTaskOrganizer = shellTaskOrganizer;
         mPipTransitionState = pipTransitionState;
         mPipTransitionState.addPipTransitionStateChangedListener(this);
+        mPipTouchHandler = pipTouchHandler;
         mMainExecutor = mainExecutor;
         mImpl = new PipImpl();
 
@@ -168,6 +171,7 @@
             TaskStackListenerImpl taskStackListener,
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
+            PipTouchHandler pipTouchHandler,
             ShellExecutor mainExecutor) {
         if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -177,7 +181,7 @@
         return new PipController(context, shellInit, shellCommandHandler, shellController,
                 displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                 pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
-                pipTransitionState, mainExecutor);
+                pipTransitionState, pipTouchHandler, mainExecutor);
     }
 
     public PipImpl getPipImpl() {
@@ -204,7 +208,9 @@
         mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
                 new ImeListener(mDisplayController, mPipDisplayLayoutState.getDisplayId()) {
                     @Override
-                    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
+                    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+                        mPipTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
+                    }
                 });
 
         // Allow other outside processes to bind to PiP controller using the key below.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index e1e072a..83253c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -134,6 +134,8 @@
     private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig =
             new PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_NO_BOUNCY);
 
+    @Nullable private Runnable mUpdateMovementBoundsRunnable;
+
     private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
         if (mPipBoundsState.getBounds().equals(newBounds)) {
             return;
@@ -141,6 +143,7 @@
 
         mMenuController.updateMenuLayout(newBounds);
         mPipBoundsState.setBounds(newBounds);
+        maybeUpdateMovementBounds();
     };
 
     /**
@@ -566,11 +569,20 @@
                             + " callers=\n%s", TAG, originalBounds, offset,
                     Debug.getCallers(5, "    "));
         }
+        if (offset == 0) {
+            return;
+        }
+
         cancelPhysicsAnimation();
-        /*
-        mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
-                mUpdateBoundsCallback);
-         */
+
+        Rect adjustedBounds = new Rect(originalBounds);
+        adjustedBounds.offset(0, offset);
+
+        setAnimatingToBounds(adjustedBounds);
+        Bundle extra = new Bundle();
+        extra.putBoolean(ANIMATING_BOUNDS_CHANGE, true);
+        extra.putInt(ANIMATING_BOUNDS_CHANGE_DURATION, SHIFT_DURATION);
+        mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
     }
 
     /**
@@ -585,11 +597,11 @@
     /** Set new fling configs whose min/max values respect the given movement bounds. */
     private void rebuildFlingConfigs() {
         mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
-                mPipBoundsAlgorithm.getMovementBounds(getBounds()).left,
-                mPipBoundsAlgorithm.getMovementBounds(getBounds()).right);
+                mPipBoundsState.getMovementBounds().left,
+                mPipBoundsState.getMovementBounds().right);
         mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
-                mPipBoundsAlgorithm.getMovementBounds(getBounds()).top,
-                mPipBoundsAlgorithm.getMovementBounds(getBounds()).bottom);
+                mPipBoundsState.getMovementBounds().top,
+                mPipBoundsState.getMovementBounds().bottom);
         final Rect insetBounds = mPipBoundsState.getDisplayLayout().stableInsets();
         mStashConfigX = new PhysicsAnimator.FlingConfig(
                 DEFAULT_FRICTION,
@@ -671,6 +683,16 @@
         cleanUpHighPerfSessionMaybe();
     }
 
+    void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) {
+        mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
+    }
+
+    private void maybeUpdateMovementBounds() {
+        if (mUpdateMovementBoundsRunnable != null)  {
+            mUpdateMovementBoundsRunnable.run();
+        }
+    }
+
     /**
      * Notifies the floating coordinator that we're moving, and sets the animating to bounds so
      * we return these bounds from
@@ -807,8 +829,14 @@
                 startTx, finishTx, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
                 destinationBounds, duration, 0f /* angle */);
         animator.setAnimationEndCallback(() -> {
-            mPipBoundsState.setBounds(destinationBounds);
-            // All motion operations have actually finished, so make bounds cache updates.
+            mUpdateBoundsCallback.accept(destinationBounds);
+
+            // In case an ongoing drag/fling was present before a deterministic resize transition
+            // kicked in, we need to update the update bounds properly before cleaning in-motion
+            // state.
+            mPipBoundsState.getMotionBoundsState().setBoundsInMotion(destinationBounds);
+            settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
+
             cleanUpHighPerfSessionMaybe();
             // Signal that we are done with resize transition
             mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */);
@@ -817,7 +845,7 @@
     }
 
     private void settlePipBoundsAfterPhysicsAnimation(boolean animatingAfter) {
-        if (!animatingAfter) {
+        if (!animatingAfter && mPipBoundsState.getMotionBoundsState().isInMotion()) {
             // The physics animation ended, though we may not necessarily be done animating, such as
             // when we're still dragging after moving out of the magnetic target. Only set the final
             // bounds state and clear motion bounds completely if the whole animation is over.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 5b0ca18..d28204a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -146,8 +146,8 @@
         mUpdateResizeBoundsCallback = (rect) -> {
             mUserResizeBounds.set(rect);
             // mMotionHelper.synchronizePinnedStackBounds();
-            mUpdateMovementBoundsRunnable.run();
             mPipBoundsState.setBounds(rect);
+            mUpdateMovementBoundsRunnable.run();
             resetState();
         };
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 53b80e8..f387e72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -199,6 +199,7 @@
         mMenuController.addListener(new PipMenuListener());
         mGesture = new DefaultPipTouchGesture();
         mMotionHelper = pipMotionHelper;
+        mMotionHelper.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
         mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
                 mMotionHelper, mainExecutor);
         mTouchState = new PipTouchState(ViewConfiguration.get(context),
@@ -317,6 +318,8 @@
         mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
         mPipResizeGestureHandler.onActivityUnpinned();
         mPipInputConsumer.unregisterInputConsumer();
+        mPipBoundsState.setHasUserMovedPip(false);
+        mPipBoundsState.setHasUserResizedPip(false);
     }
 
     void onPinnedStackAnimationEnded(
@@ -346,6 +349,22 @@
     void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
         mIsImeShowing = imeVisible;
         mImeHeight = imeHeight;
+
+        // Cache new movement bounds using the new potential IME height.
+        updateMovementBounds();
+
+        mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
+            int delta = mPipBoundsState.getMovementBounds().bottom
+                    - mPipBoundsState.getBounds().top;
+
+            boolean hasUserInteracted = (mPipBoundsState.hasUserMovedPip()
+                    || mPipBoundsState.hasUserResizedPip());
+            if ((imeVisible && delta < 0) || (!imeVisible && !hasUserInteracted)) {
+                // The policy is to ignore an IME disappearing if user has interacted with PiP.
+                // Otherwise, only offset due to an appearing IME if PiP occludes it.
+                mMotionHelper.animateToOffset(mPipBoundsState.getBounds(), delta);
+            }
+        });
     }
 
     void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
@@ -1077,6 +1096,7 @@
         switch (newState) {
             case PipTransitionState.ENTERED_PIP:
                 onActivityPinned();
+                updateMovementBounds();
                 mTouchState.setAllowInputEvents(true);
                 mTouchState.reset();
                 break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index 29272be..a132796f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -149,6 +149,12 @@
     @Nullable
     private SurfaceControl mSwipePipToHomeOverlay;
 
+    //
+    // Scheduling-related state
+    //
+    @Nullable
+    private Runnable mOnIdlePipTransitionStateRunnable;
+
     /**
      * An interface to track state updates as we progress through PiP transitions.
      */
@@ -197,6 +203,8 @@
             mState = state;
             dispatchPipTransitionStateChanged(prevState, mState, extra);
         }
+
+        maybeRunOnIdlePipTransitionStateCallback();
     }
 
     /**
@@ -231,6 +239,29 @@
     }
 
     /**
+     * Schedule a callback to run when in a valid idle PiP state.
+     *
+     * <p>We only allow for one callback to be scheduled to avoid cases with multiple transitions
+     * being scheduled. For instance, if user double taps and IME shows, this would
+     * schedule a bounds change transition for IME appearing. But if some other transition would
+     * want to animate PiP before the scheduled callback executes, we would rather want to replace
+     * the existing callback with a new one, to avoid multiple animations
+     * as soon as we are idle.</p>
+     */
+    public void setOnIdlePipTransitionStateRunnable(
+            @Nullable Runnable onIdlePipTransitionStateRunnable) {
+        mOnIdlePipTransitionStateRunnable = onIdlePipTransitionStateRunnable;
+        maybeRunOnIdlePipTransitionStateCallback();
+    }
+
+    private void maybeRunOnIdlePipTransitionStateCallback() {
+        if (mOnIdlePipTransitionStateRunnable != null && isPipStateIdle()) {
+            mOnIdlePipTransitionStateRunnable.run();
+            mOnIdlePipTransitionStateRunnable = null;
+        }
+    }
+
+    /**
      * Adds a {@link PipTransitionStateChangedListener} for future PiP transition state updates.
      */
     public void addPipTransitionStateChangedListener(PipTransitionStateChangedListener listener) {
@@ -318,6 +349,11 @@
         throw new IllegalStateException("Unknown state: " + state);
     }
 
+    public boolean isPipStateIdle() {
+        // This needs to be a valid in-PiP state that isn't a transient state.
+        return mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS;
+    }
+
     @Override
     public String toString() {
         return String.format("PipTransitionState(mState=%s, mInSwipePipToHomeTransition=%b)",
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 4217562..1024a55 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -578,6 +578,8 @@
     });
 
     private AudioAttributes() {
+        mBundle = null;
+        mFormattedTags = "";
     }
 
     /**
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 395f81d..0ffab4b 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -1166,10 +1166,11 @@
 
 
     /**
-     * Returns whether the device supports observer mode or not. When observe
-     * mode is enabled, the NFC hardware will listen for NFC readers, but not
-     * respond to them. When observe mode is disabled, the NFC hardware will
-     * resoond to the reader and proceed with the transaction.
+     * Returns whether the device supports observe mode or not. When observe mode is enabled, the
+     * NFC hardware will listen to NFC readers, but not respond to them. While enabled, observed
+     * polling frames will be sent to the APDU service (see {@link #setObserveModeEnabled(boolean)}.
+     * When observe mode is disabled (or if it's not supported), the NFC hardware will automatically
+     * respond to the reader and proceed with the transaction.
      * @return true if the mode is supported, false otherwise.
      */
     @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
@@ -1193,9 +1194,10 @@
      * and simply observe and notify the APDU service of polling loop frames. See
      * {@link #isObserveModeSupported()} for a description of observe mode. Only the package of the
      * currently preferred service (the service set as preferred by the current foreground
-     * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the
-     * current Default Wallet Role Holder {@link android.app.role.RoleManager#ROLE_WALLET}),
-     * otherwise a call to this method will fail and return false.
+     * application via {@link android.nfc.cardemulation.CardEmulation#setPreferredService(Activity,
+     * android.content.ComponentName)} or the current Default Wallet Role Holder
+     * {@link android.app.role.RoleManager#ROLE_WALLET}), otherwise a call to this method will fail
+     * and return false.
      *
      * @param enabled false disables observe mode to allow the transaction to proceed while true
      *                enables observe mode and does not allow transactions to proceed.
diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig
index 92fd9cb..15c8850 100644
--- a/services/core/java/com/android/server/biometrics/biometrics.aconfig
+++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig
@@ -14,3 +14,10 @@
   description: "This flag controls whether virtual HAL is used for testing instead of TestHal "
   bug: "294254230"
 }
+
+flag {
+  name: "notify_fingerprint_loe"
+  namespace: "biometrics_framework"
+  description: "This flag controls whether a notification should be sent to notify user when loss of enrollment happens"
+  bug: "351036558"
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
index 53e6bdb..27f9cc8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -151,6 +151,43 @@
     }
 
     /**
+     * Shows a fingerprint notification for loss of enrollment
+     */
+    public static void showFingerprintLoeNotification(@NonNull Context context) {
+        Slog.d(TAG, "Showing fingerprint LOE notification");
+
+        final String name =
+                context.getString(R.string.device_unlock_notification_name);
+        final String title = context.getString(R.string.fingerprint_dangling_notification_title);
+        final String content = context.getString(R.string.fingerprint_loe_notification_msg);
+
+        // Create "Set up" notification action button.
+        final Intent setupIntent =
+                new Intent(BiometricDanglingReceiver.ACTION_FINGERPRINT_RE_ENROLL_LAUNCH);
+        final PendingIntent setupPendingIntent = PendingIntent.getBroadcastAsUser(context, 0,
+                setupIntent, PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+        final String setupText =
+                context.getString(R.string.biometric_dangling_notification_action_set_up);
+        final Notification.Action setupAction = new Notification.Action.Builder(
+                null, setupText, setupPendingIntent).build();
+
+        // Create "Not now" notification action button.
+        final Intent notNowIntent =
+                new Intent(BiometricDanglingReceiver.ACTION_FINGERPRINT_RE_ENROLL_DISMISS);
+        final PendingIntent notNowPendingIntent = PendingIntent.getBroadcastAsUser(context, 0,
+                notNowIntent, PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+        final String notNowText = context.getString(
+                R.string.biometric_dangling_notification_action_not_now);
+        final Notification.Action notNowAction = new Notification.Action.Builder(
+                null, notNowText, notNowPendingIntent).build();
+
+        showNotificationHelper(context, name, title, content, setupPendingIntent, setupAction,
+                notNowAction, Notification.CATEGORY_SYSTEM, FINGERPRINT_RE_ENROLL_CHANNEL,
+                FINGERPRINT_RE_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET, false,
+                Notification.FLAG_NO_CLEAR);
+    }
+
+    /**
      * Shows a fingerprint bad calibration notification.
      */
     public static void showBadCalibrationNotification(@NonNull Context context) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
index 7fb27b6..63678aa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
@@ -57,6 +57,7 @@
     protected boolean mInvalidationInProgress;
     protected final Context mContext;
     protected final File mFile;
+    private boolean mIsInvalidBiometricState = false;
 
     private final Runnable mWriteStateRunnable = this::doWriteStateInternal;
 
@@ -102,7 +103,7 @@
             serializer.endDocument();
             destination.finishWrite(out);
         } catch (Throwable t) {
-            Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+            Slog.e(TAG, "Failed to write settings, restoring backup", t);
             destination.failWrite(out);
             throw new IllegalStateException("Failed to write to file: " + mFile.toString(), t);
         } finally {
@@ -192,6 +193,29 @@
         }
     }
 
+    /**
+     * Return true if the biometric file is correctly read. Otherwise return false.
+     */
+    public boolean isInvalidBiometricState() {
+        return mIsInvalidBiometricState;
+    }
+
+    /**
+     * Delete the file of the biometric state.
+     */
+    public void deleteBiometricFile() {
+        synchronized (this) {
+            if (!mFile.exists()) {
+                return;
+            }
+            if (mFile.delete()) {
+                Slog.i(TAG, mFile + " is deleted successfully");
+            } else {
+                Slog.i(TAG, "Failed to delete " + mFile);
+            }
+        }
+    }
+
     private boolean isUnique(String name) {
         for (T identifier : mBiometrics) {
             if (identifier.getName().equals(name)) {
@@ -218,7 +242,8 @@
         try {
             in = new FileInputStream(mFile);
         } catch (FileNotFoundException fnfe) {
-            Slog.i(TAG, "No fingerprint state");
+            Slog.i(TAG, "No fingerprint state", fnfe);
+            mIsInvalidBiometricState = true;
             return;
         }
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
index ebe4679..0b4f640 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
@@ -33,4 +33,14 @@
     CharSequence getUniqueName(Context context, int userId);
     void setInvalidationInProgress(Context context, int userId, boolean inProgress);
     boolean isInvalidationInProgress(Context context, int userId);
+
+    /**
+     * Return true if the biometric file is correctly read. Otherwise return false.
+     */
+    boolean hasValidBiometricUserState(Context context, int userId);
+
+    /**
+     * Delete the file of the biometric state.
+     */
+    void deleteStateForUser(int userId);
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 69ad152..3b6aeef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -25,6 +25,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 
@@ -62,7 +63,7 @@
     }
 
     private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
-    private final BiometricUtils<S> mBiometricUtils;
+    protected final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
     private final boolean mHasEnrollmentsBeforeStarting;
     private BaseClientMonitor mCurrentTask;
@@ -105,6 +106,11 @@
                     startCleanupUnknownHalTemplates();
                 }
             }
+
+            if (mBiometricUtils.hasValidBiometricUserState(getContext(), getTargetUserId())
+                    && Flags.notifyFingerprintLoe()) {
+                handleInvalidBiometricState();
+            }
         }
     };
 
@@ -248,4 +254,8 @@
     public ArrayList<UserTemplate> getUnknownHALTemplates() {
         return mUnknownHALTemplates;
     }
+
+    protected void handleInvalidBiometricState() {}
+
+    protected abstract int getModality();
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
index c574478..79285cb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
@@ -124,6 +124,22 @@
         return getStateForUser(context, userId).isInvalidationInProgress();
     }
 
+    @Override
+    public boolean hasValidBiometricUserState(Context context, int userId) {
+        return getStateForUser(context, userId).isInvalidBiometricState();
+    }
+
+    @Override
+    public void deleteStateForUser(int userId) {
+        synchronized (this) {
+            FaceUserState state = mUserStates.get(userId);
+            if (state != null) {
+                state.deleteBiometricFile();
+                mUserStates.delete(userId);
+            }
+        }
+    }
+
     private FaceUserState getStateForUser(Context ctx, int userId) {
         synchronized (this) {
             FaceUserState state = mUserStates.get(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index e75c6ab..964bf6c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.face.Face;
 import android.os.IBinder;
@@ -77,4 +78,9 @@
         FaceUtils.getInstance(getSensorId()).addBiometricForUser(
                 getContext(), getTargetUserId(), (Face) identifier);
     }
+
+    @Override
+    protected int getModality() {
+        return BiometricsProtoEnums.MODALITY_FACE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index 0062d31..b8c06c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -140,6 +140,22 @@
         return getStateForUser(context, userId).isInvalidationInProgress();
     }
 
+    @Override
+    public boolean hasValidBiometricUserState(Context context, int userId) {
+        return getStateForUser(context, userId).isInvalidBiometricState();
+    }
+
+    @Override
+    public void deleteStateForUser(int userId) {
+        synchronized (this) {
+            FingerprintUserState state = mUserStates.get(userId);
+            if (state != null) {
+                state.deleteBiometricFile();
+                mUserStates.delete(userId);
+            }
+        }
+    }
+
     private FingerprintUserState getStateForUser(Context ctx, int userId) {
         synchronized (this) {
             FingerprintUserState state = mUserStates.get(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 5edc2ca..1fc5179 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -22,9 +22,11 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
+import android.util.Slog;
 
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.InternalCleanupClient;
 import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -42,6 +44,8 @@
 public class FingerprintInternalCleanupClient
         extends InternalCleanupClient<Fingerprint, AidlSession> {
 
+    private static final String TAG = "FingerprintInternalCleanupClient";
+
     public FingerprintInternalCleanupClient(@NonNull Context context,
             @NonNull Supplier<AidlSession> lazyDaemon,
             int userId, @NonNull String owner, int sensorId,
@@ -80,4 +84,16 @@
         FingerprintUtils.getInstance(getSensorId()).addBiometricForUser(
                 getContext(), getTargetUserId(), (Fingerprint) identifier);
     }
+
+    @Override
+    public void handleInvalidBiometricState() {
+        Slog.d(TAG, "Invalid fingerprint user state: delete the state.");
+        mBiometricUtils.deleteStateForUser(getTargetUserId());
+        BiometricNotificationUtils.showFingerprintLoeNotification(getContext());
+    }
+
+    @Override
+    protected int getModality() {
+        return BiometricsProtoEnums.MODALITY_FINGERPRINT;
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index 7f7ae10..58e3452 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -71,7 +71,20 @@
     @Retention(SOURCE)
     @Target({METHOD})
     @interface PermissionVerified {
+        /**
+         * The name of the permission that is verified, if precisely one permission is required.
+         * If more than one permission is required, specify either {@link #allOf()} instead.
+         *
+         * <p>If specified, {@link #allOf()} must both be {@code null}.</p>
+         */
         String value() default "";
+
+        /**
+         * Specifies a list of permission names that are all required.
+         *
+         * <p>If specified, {@link #value()} must both be {@code null}.</p>
+         */
+        String[] allOf() default {};
     }
 
     @BinderThread
@@ -132,13 +145,17 @@
 
         void showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode);
 
-        @PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+        @PermissionVerified(allOf = {
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                Manifest.permission.WRITE_SECURE_SETTINGS})
         void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId);
 
         @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
         boolean isInputMethodPickerShownForTest();
 
-        @PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+        @PermissionVerified(allOf = {
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                Manifest.permission.WRITE_SECURE_SETTINGS})
         void onImeSwitchButtonClickFromSystem(int displayId);
 
         InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId);
@@ -153,7 +170,9 @@
 
         void reportPerceptibleAsync(IBinder windowToken, boolean perceptible);
 
-        @PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+        @PermissionVerified(allOf = {
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                Manifest.permission.INTERNAL_SYSTEM_WINDOW})
         void removeImeSurface(int displayId);
 
         void removeImeSurfaceFromWindowAsync(IBinder windowToken);
@@ -330,13 +349,14 @@
         mCallback.showInputMethodPickerFromClient(client, auxiliarySubtypeMode);
     }
 
-    @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @EnforcePermission(allOf = {
+            Manifest.permission.WRITE_SECURE_SETTINGS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     @Override
     public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
         super.showInputMethodPickerFromSystem_enforcePermission();
 
         mCallback.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
-
     }
 
     @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
@@ -347,7 +367,9 @@
         return mCallback.isInputMethodPickerShownForTest();
     }
 
-    @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @EnforcePermission(allOf = {
+            Manifest.permission.WRITE_SECURE_SETTINGS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     @Override
     public void onImeSwitchButtonClickFromSystem(int displayId) {
         super.onImeSwitchButtonClickFromSystem_enforcePermission();
@@ -382,7 +404,9 @@
         mCallback.reportPerceptibleAsync(windowToken, perceptible);
     }
 
-    @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @EnforcePermission(allOf = {
+            Manifest.permission.INTERNAL_SYSTEM_WINDOW,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     @Override
     public void removeImeSurface(int displayId) {
         super.removeImeSurface_enforcePermission();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8d652ef..5e7d391 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4002,7 +4002,9 @@
         }
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.WRITE_SECURE_SETTINGS})
     @Override
     public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
         // Always call subtype picker, because subtype picker is a superset of input method
@@ -4096,7 +4098,9 @@
         }
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.WRITE_SECURE_SETTINGS})
     @Override
     public void onImeSwitchButtonClickFromSystem(int displayId) {
         synchronized (ImfLock.class) {
@@ -4438,7 +4442,9 @@
         });
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.INTERNAL_SYSTEM_WINDOW})
     @Override
     public void removeImeSurface(int displayId) {
         mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index f603ff3..c940a9c 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -252,7 +252,9 @@
         offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode));
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.WRITE_SECURE_SETTINGS})
     @Override
     public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
         mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
@@ -264,7 +266,9 @@
         return mInner.isInputMethodPickerShownForTest();
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.WRITE_SECURE_SETTINGS})
     @Override
     public void onImeSwitchButtonClickFromSystem(int displayId) {
         mInner.onImeSwitchButtonClickFromSystem(displayId);
@@ -298,7 +302,9 @@
         mInner.reportPerceptibleAsync(windowToken, perceptible);
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.INTERNAL_SYSTEM_WINDOW})
     @Override
     public void removeImeSurface(int displayId) {
         mInner.removeImeSurface(displayId);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index c83b280..ed0dc3b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2819,7 +2819,21 @@
                 mClearedTaskForReuse,
                 mClearedTaskFragmentForPip,
                 mClearedForReorderActivityToFront,
-                calculateMinDimension());
+                calculateMinDimension(),
+                isTopNonFinishingChild());
+    }
+
+    private boolean isTopNonFinishingChild() {
+        final WindowContainer<?> parent = getParent();
+        if (parent == null) {
+            // Either the TaskFragment is not attached or is going to destroy. Return false.
+            return false;
+        }
+        final ActivityRecord topNonFishingActivity = parent.getActivity(ar -> !ar.finishing);
+        // If the parent's top non-finishing activity is this TaskFragment's, it means
+        // this TaskFragment is the top non-finishing container of its parent.
+        return topNonFishingActivity != null && topNonFishingActivity
+                .equals(getTopNonFinishingActivity());
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 358adc3..af3ed28 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2185,8 +2185,7 @@
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             final WallpaperWindowToken wallpaper = mParticipants.valueAt(i).asWallpaperToken();
             if (wallpaper != null) {
-                if (!wallpaper.isVisible() && (wallpaper.isVisibleRequested()
-                        || (Flags.ensureWallpaperInTransitions() && showWallpaper))) {
+                if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) {
                     wallpaper.commitVisibility(showWallpaper);
                 } else if (Flags.ensureWallpaperInTransitions() && wallpaper.isVisible()
                         && !showWallpaper && !wallpaper.getDisplayContent().isKeyguardLocked()
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4db62478..9884df9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -118,12 +118,12 @@
 import static com.android.server.policy.PhoneWindowManager.TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
-import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
-import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
+import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
 import static com.android.server.wm.SensitiveContentPackages.PackageInfo;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
@@ -357,7 +357,6 @@
 import com.android.server.power.ShutdownThread;
 import com.android.server.utils.PriorityDump;
 import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
-import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -9390,11 +9389,6 @@
             return focusedActivity;
         }
 
-        if (!Flags.embeddedActivityBackNavFlag()) {
-            // Return if flag is not enabled.
-            return focusedActivity;
-        }
-
         if (!focusedActivity.isEmbedded()) {
             // Return if the focused activity is not embedded.
             return focusedActivity;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 3789531..36a7b3d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -1296,6 +1296,11 @@
             mFingerprints.add((Fingerprint) identifier);
         }
 
+        @Override
+        protected int getModality() {
+            return 0;
+        }
+
         public List<Fingerprint> getFingerprints() {
             return mFingerprints;
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
index c9482ce..a34e796 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -30,12 +31,16 @@
 import android.hardware.fingerprint.Fingerprint;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.TestableContext;
 
 import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -69,6 +74,10 @@
     public final TestableContext mContext = new TestableContext(
             InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Mock
     ISession mSession;
     @Mock
@@ -168,6 +177,21 @@
         assertThat(mClient.getUnknownHALTemplates()).isEmpty();
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NOTIFY_FINGERPRINT_LOE)
+    public void invalidBiometricUserState() throws Exception {
+        mClient =  createClient();
+
+        final List<Fingerprint> list = new ArrayList<>();
+        doReturn(true).when(mFingerprintUtils)
+                .hasValidBiometricUserState(mContext, 2);
+        doReturn(list).when(mFingerprintUtils).getBiometricsForUser(mContext, 2);
+
+        mClient.start(mCallback);
+        mClient.onEnumerationResult(null, 0);
+        verify(mFingerprintUtils).deleteStateForUser(2);
+    }
+
     protected FingerprintInternalCleanupClient createClient() {
         final Map<Integer, Long> authenticatorIds = new HashMap<>();
         return new FingerprintInternalCleanupClient(mContext, () -> mAidlSession, 2 /* userId */,
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 afa22bc..a159ce3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -72,7 +72,6 @@
 import android.window.WindowOnBackInvokedDispatcher;
 
 import com.android.server.LocalServices;
-import com.android.window.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -672,7 +671,6 @@
 
     @Test
     public void testBackOnMostRecentWindowInActivityEmbedding() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_EMBEDDED_ACTIVITY_BACK_NAV_FLAG);
         final Task task = createTask(mDefaultDisplay);
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
         final TaskFragment primaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 3c247a0..6be1af2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -995,16 +995,14 @@
         // The focus should change.
         assertEquals(winLeftTop, mDisplayContent.mCurrentFocus);
 
-        if (Flags.embeddedActivityBackNavFlag()) {
-            // Move focus if the adjacent activity is more recently active.
-            doReturn(1L).when(appLeftTop).getLastWindowCreateTime();
-            doReturn(2L).when(appRightTop).getLastWindowCreateTime();
-            assertTrue(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop));
+        // Move focus if the adjacent activity is more recently active.
+        doReturn(1L).when(appLeftTop).getLastWindowCreateTime();
+        doReturn(2L).when(appRightTop).getLastWindowCreateTime();
+        assertTrue(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop));
 
-            // Do not move the focus if the adjacent activity is less recently active.
-            doReturn(3L).when(appLeftTop).getLastWindowCreateTime();
-            assertFalse(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop));
-        }
+        // Do not move the focus if the adjacent activity is less recently active.
+        doReturn(3L).when(appLeftTop).getLastWindowCreateTime();
+        assertFalse(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop));
     }
 
     @Test