Merge "Enable desktop mode protologs by default" into udc-qpr-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9bd2970..9f90425 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2958,6 +2958,10 @@
     method @Deprecated public boolean isBound();
   }
 
+  public class NotificationRankingUpdate implements android.os.Parcelable {
+    method public final boolean isFdNotNullAndClosed();
+  }
+
 }
 
 package android.service.quickaccesswallet {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 66c24ff..de5bcc7 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2476,19 +2476,38 @@
     }
 
     /**
-     * Reset all wallpaper to the factory default.
+     * Reset all wallpaper to the factory default. As opposed to {@link #clear()}, if the device
+     * is configured to have a live wallpaper by default, apply it.
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#SET_WALLPAPER}.
      */
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
     public void clearWallpaper() {
+        if (isLockscreenLiveWallpaperEnabled()) {
+            clearWallpaper(FLAG_LOCK | FLAG_SYSTEM, mContext.getUserId());
+            return;
+        }
         clearWallpaper(FLAG_LOCK, mContext.getUserId());
         clearWallpaper(FLAG_SYSTEM, mContext.getUserId());
     }
 
     /**
-     * Clear the wallpaper for a specific user.  The caller must hold the
+     * Clear the wallpaper for a specific user.
+     * <ul>
+     *     <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper.
+     *     The home screen wallpaper will become visible on the lock screen. </li>
+     *
+     *     <li> When called with {@code which=}{@link #FLAG_SYSTEM}, revert the home screen
+     *     wallpaper to default. The lockscreen wallpaper will be unchanged: if the previous
+     *     wallpaper was shared between home and lock screen, it will become lock screen only. </li>
+     *
+     *     <li> When called with {@code which=}({@link #FLAG_LOCK} | {@link #FLAG_SYSTEM}), put the
+     *     default wallpaper on both home and lock screen, removing any user defined wallpaper.</li>
+     * </ul>
+     * </p>
+     *
+     * The caller must hold the
      * INTERACT_ACROSS_USERS_FULL permission to clear another user's
      * wallpaper, and must hold the SET_WALLPAPER permission in all
      * circumstances.
@@ -2793,8 +2812,9 @@
 
     /**
      * Remove any currently set system wallpaper, reverting to the system's built-in
-     * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
-     * is broadcast.
+     * wallpaper. As opposed to {@link #clearWallpaper()}, this method always set a static wallpaper
+     * with the default image, even if the device is configured to have a live wallpaper by default.
+     * On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#SET_WALLPAPER}.
@@ -2809,9 +2829,14 @@
 
     /**
      * Remove one or more currently set wallpapers, reverting to the system default
-     * display for each one.  If {@link #FLAG_SYSTEM} is set in the {@code which}
-     * parameter, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} will be broadcast
-     * upon success.
+     * display for each one. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
+     * is broadcast.
+     * <ul>
+     *     <li> If {@link #FLAG_SYSTEM} is set in the {@code which} parameter, put the default
+     *     wallpaper on both home and lock screen, removing any user defined wallpaper. </li>
+     *     <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper.
+     *     The home screen wallpaper will become visible on the lock screen. </li>
+     * </ul>
      *
      * @param which A bitwise combination of {@link #FLAG_SYSTEM} or
      *   {@link #FLAG_LOCK}
@@ -2821,6 +2846,7 @@
     public void clear(@SetWallpaperFlags int which) throws IOException {
         if ((which & FLAG_SYSTEM) != 0) {
             clear();
+            if (isLockscreenLiveWallpaperEnabled()) return;
         }
         if ((which & FLAG_LOCK) != 0) {
             clearWallpaper(FLAG_LOCK, mContext.getUserId());
diff --git a/core/java/android/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java
index 3fefe4b..ed53967 100644
--- a/core/java/android/app/admin/WifiSsidPolicy.java
+++ b/core/java/android/app/admin/WifiSsidPolicy.java
@@ -135,6 +135,10 @@
         dest.writeArraySet(mSsids);
     }
 
+    /**
+     * Two instances of WifiSsidPolicy is considered equal if they have
+     * the same WifiSsidPolicyType and the same set of WifiSsids
+     */
     @Override
     public boolean equals(Object thatObject) {
         if (this == thatObject) {
@@ -147,6 +151,9 @@
         return mPolicyType == that.mPolicyType && Objects.equals(mSsids, that.mSsids);
     }
 
+    /**
+     * Returns the hash code value of WifiSsidPolicyType and WifiSsid set
+     */
     @Override
     public int hashCode() {
         return Objects.hash(mPolicyType, mSsids);
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index a853714..75640bd 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -16,32 +16,117 @@
 package android.service.notification;
 
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+
+import java.nio.ByteBuffer;
 
 /**
+ * Represents an update to notification rankings.
  * @hide
  */
+@SuppressLint({"ParcelNotFinal", "ParcelCreator"})
+@TestApi
 public class NotificationRankingUpdate implements Parcelable {
     private final NotificationListenerService.RankingMap mRankingMap;
 
+    // The ranking map is stored in shared memory when parceled, for sending across the binder.
+    // This is done because the ranking map can grow large if there are many notifications.
+    private SharedMemory mRankingMapFd = null;
+    private final String mSharedMemoryName = "NotificationRankingUpdatedSharedMemory";
+
+    /**
+     * @hide
+     */
     public NotificationRankingUpdate(NotificationListenerService.Ranking[] rankings) {
         mRankingMap = new NotificationListenerService.RankingMap(rankings);
     }
 
+    /**
+     * @hide
+     */
     public NotificationRankingUpdate(Parcel in) {
-        mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class);
+        if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+                SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+            // Recover the ranking map from the SharedMemory and store it in mapParcel.
+            final Parcel mapParcel = Parcel.obtain();
+            ByteBuffer buffer = null;
+            try {
+                // The ranking map should be stored in shared memory when it is parceled, so we
+                // unwrap the SharedMemory object.
+                mRankingMapFd = in.readParcelable(getClass().getClassLoader(), SharedMemory.class);
+
+                // In the case that the ranking map can't be read, readParcelable may return null.
+                // In this case, we set mRankingMap to null;
+                if (mRankingMapFd == null) {
+                    mRankingMap = null;
+                    return;
+                }
+                // We only need read-only access to the shared memory region.
+                buffer = mRankingMapFd.mapReadOnly();
+                if (buffer == null) {
+                    mRankingMap = null;
+                    return;
+                }
+                byte[] payload = new byte[buffer.remaining()];
+                buffer.get(payload);
+                mapParcel.unmarshall(payload, 0, payload.length);
+                mapParcel.setDataPosition(0);
+
+                mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(),
+                        android.service.notification.NotificationListenerService.RankingMap.class);
+            } catch (ErrnoException e) {
+                // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
+                // avoid crashes; change to Log.wtf.
+                throw new RuntimeException(e);
+            } finally {
+                mapParcel.recycle();
+                if (buffer != null) {
+                    mRankingMapFd.unmap(buffer);
+                }
+            }
+        } else {
+            mRankingMap = in.readParcelable(getClass().getClassLoader(),
+                    android.service.notification.NotificationListenerService.RankingMap.class);
+        }
     }
 
+    /**
+     * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing.
+     * @hide
+     */
+    @TestApi
+    public final boolean isFdNotNullAndClosed() {
+        return mRankingMapFd != null && mRankingMapFd.getFd() == -1;
+    }
+
+    /**
+     * @hide
+     */
     public NotificationListenerService.RankingMap getRankingMap() {
         return mRankingMap;
     }
 
+    /**
+     * @hide
+     */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /**
+     * @hide
+     */
     @Override
     public boolean equals(@Nullable Object o) {
         if (this == o) return true;
@@ -51,11 +136,51 @@
         return mRankingMap.equals(other.mRankingMap);
     }
 
+    /**
+     * @hide
+     */
     @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mRankingMap, flags);
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+                SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+            final Parcel mapParcel = Parcel.obtain();
+            try {
+                // Parcels the ranking map and measures its size.
+                mapParcel.writeParcelable(mRankingMap, flags);
+                int mapSize = mapParcel.dataSize();
+
+                // Creates a new SharedMemory object with enough space to hold the ranking map.
+                SharedMemory mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize);
+                if (mRankingMapFd == null) {
+                    return;
+                }
+
+                // Gets a read/write buffer mapping the entire shared memory region.
+                final ByteBuffer buffer = mRankingMapFd.mapReadWrite();
+
+                // Puts the ranking map into the shared memory region buffer.
+                buffer.put(mapParcel.marshall(), 0, mapSize);
+
+                // Protects the region from being written to, by setting it to be read-only.
+                mRankingMapFd.setProtect(OsConstants.PROT_READ);
+
+                // Puts the SharedMemory object in the parcel.
+                out.writeParcelable(mRankingMapFd, flags);
+            } catch (ErrnoException e) {
+                // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
+                // avoid crashes; change to Log.wtf.
+                throw new RuntimeException(e);
+            } finally {
+                mapParcel.recycle();
+            }
+        } else {
+            out.writeParcelable(mRankingMap, flags);
+        }
     }
 
+    /**
+    * @hide
+    */
     public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
             = new Parcelable.Creator<NotificationRankingUpdate>() {
         public NotificationRankingUpdate createFromParcel(Parcel parcel) {
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index c9e7600..6b8ae94 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -85,6 +85,10 @@
         /** Gating the holding of WakeLocks until NLSes are told about a new notification. */
         public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION =
                 devFlag("persist.sysui.notification.wake_lock_for_posting_notification");
+
+        /** Gating storing NotificationRankingUpdate ranking map in shared memory. */
+        public static final Flag RANKING_UPDATE_ASHMEM = devFlag(
+                "persist.sysui.notification.ranking_update_ashmem");
     }
 
     //// == End of flags.  Everything below this line is the implementation. == ////
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
new file mode 100644
index 0000000..a84ac55
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@SmallTest
+@RunWith(Parameterized.class)
+public class NotificationRankingUpdateTest {
+
+    private static final String NOTIFICATION_CHANNEL_ID = "test_channel_id";
+    private static final String TEST_KEY = "key";
+
+    private NotificationChannel mNotificationChannel;
+
+    // TODO(b/284297289): remove this flag set once resolved.
+    @Parameterized.Parameters(name = "rankingUpdateAshmem={0}")
+    public static Boolean[] getRankingUpdateAshmem() {
+        return new Boolean[] { true, false };
+    }
+
+    @Parameterized.Parameter
+    public boolean mRankingUpdateAshmem;
+
+    @Before
+    public void setUp() {
+        mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel",
+                NotificationManager.IMPORTANCE_DEFAULT);
+
+        SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> {
+            if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) {
+                return mRankingUpdateAshmem;
+            }
+            return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag);
+        };
+    }
+
+    @After
+    public void tearDown() {
+        SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
+    }
+
+    public NotificationListenerService.Ranking createTestRanking(String key, int rank) {
+        NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+        ranking.populate(
+                /* key= */ key,
+                /* rank= */ rank,
+                /* matchesInterruptionFilter= */ false,
+                /* visibilityOverride= */ 0,
+                /* suppressedVisualEffects= */ 0,
+                mNotificationChannel.getImportance(),
+                /* explanation= */ null,
+                /* overrideGroupKey= */ null,
+                mNotificationChannel,
+                /* overridePeople= */ null,
+                /* snoozeCriteria= */ null,
+                /* showBadge= */ true,
+                /* userSentiment= */ 0,
+                /* hidden= */ false,
+                /* lastAudiblyAlertedMs= */ -1,
+                /* noisy= */ false,
+                /* smartActions= */ null,
+                /* smartReplies= */ null,
+                /* canBubble= */ false,
+                /* isTextChanged= */ false,
+                /* isConversation= */ false,
+                /* shortcutInfo= */ null,
+                /* rankingAdjustment= */ 0,
+                /* isBubble= */ false,
+                /* proposedImportance= */ 0,
+                /* sensitiveContent= */ false
+        );
+        return ranking;
+    }
+
+    @Test
+    public void testRankingUpdate_rankingConstructor() {
+        NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+        NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+                new NotificationListenerService.Ranking[]{ranking});
+
+        NotificationListenerService.RankingMap retrievedRankings = rankingUpdate.getRankingMap();
+        NotificationListenerService.Ranking retrievedRanking =
+                new NotificationListenerService.Ranking();
+        assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
+        assertEquals(123, retrievedRanking.getRank());
+    }
+
+    @Test
+    public void testRankingUpdate_parcelConstructor() {
+        NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+        NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+                new NotificationListenerService.Ranking[]{ranking});
+
+        Parcel parceledRankingUpdate = Parcel.obtain();
+        rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
+        parceledRankingUpdate.setDataPosition(0);
+
+        NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
+                parceledRankingUpdate);
+
+        NotificationListenerService.RankingMap retrievedRankings =
+                retrievedRankingUpdate.getRankingMap();
+        assertNotNull(retrievedRankings);
+        assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+        NotificationListenerService.Ranking retrievedRanking =
+                new NotificationListenerService.Ranking();
+        assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
+        assertEquals(123, retrievedRanking.getRank());
+        assertTrue(retrievedRankingUpdate.equals(rankingUpdate));
+        parceledRankingUpdate.recycle();
+    }
+
+    @Test
+    public void testRankingUpdate_emptyParcelInCheck() {
+        NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+        NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+                new NotificationListenerService.Ranking[]{ranking});
+
+        Parcel parceledRankingUpdate = Parcel.obtain();
+        rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
+
+        // This will fail to read the parceledRankingUpdate, because the data position hasn't
+        // been reset, so it'll find no data to read.
+        NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
+                parceledRankingUpdate);
+        assertNull(retrievedRankingUpdate.getRankingMap());
+    }
+
+    @Test
+    public void testRankingUpdate_describeContents() {
+        NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+        NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+                new NotificationListenerService.Ranking[]{ranking});
+        assertEquals(0, rankingUpdate.describeContents());
+    }
+
+    @Test
+    public void testRankingUpdate_equals() {
+        NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+        NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+                new NotificationListenerService.Ranking[]{ranking});
+        // Reflexive equality.
+        assertTrue(rankingUpdate.equals(rankingUpdate));
+        // Null or wrong class inequality.
+        assertFalse(rankingUpdate.equals(null));
+        assertFalse(rankingUpdate.equals(ranking));
+
+        // Different ranking contents inequality.
+        NotificationListenerService.Ranking ranking2 = createTestRanking(TEST_KEY, 456);
+        NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate(
+                new NotificationListenerService.Ranking[]{ranking2});
+        assertFalse(rankingUpdate.equals(rankingUpdate2));
+
+        // Same ranking contents equality.
+        ranking2 = createTestRanking(TEST_KEY, 123);
+        rankingUpdate2 = new NotificationRankingUpdate(
+                new NotificationListenerService.Ranking[]{ranking2});
+        assertTrue(rankingUpdate.equals(rankingUpdate2));
+    }
+}
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 2141259..2e3f604 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -416,7 +416,7 @@
     <!-- The radius of the caption menu shadow. -->
     <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
 
-    <dimen name="freeform_resize_handle">30dp</dimen>
+    <dimen name="freeform_resize_handle">15dp</dimen>
 
     <dimen name="freeform_resize_corner">44dp</dimen>
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 467e9c7..1959eb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -300,9 +300,12 @@
         return mAllowSeamlessRotationDespiteNavBarMoving;
     }
 
-    /** @return whether the navigation bar will change sides during rotation. */
+    /**
+     * Returns {@code true} if the navigation bar will change sides during rotation and the display
+     * is not square.
+     */
     public boolean navigationBarCanMove() {
-        return mNavigationBarCanMove;
+        return mNavigationBarCanMove && mWidth != mHeight;
     }
 
     /** @return the rotation that would make the physical display "upside down". */
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 fb966c7..86b0f33 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
@@ -1018,24 +1018,17 @@
     private void resetPrevPip(@NonNull TransitionInfo.Change prevPipTaskChange,
             @NonNull SurfaceControl.Transaction startTransaction) {
         final SurfaceControl leash = prevPipTaskChange.getLeash();
-        final Rect bounds = prevPipTaskChange.getEndAbsBounds();
-        final Point offset = prevPipTaskChange.getEndRelOffset();
-        bounds.offset(-offset.x, -offset.y);
+        startTransaction.remove(leash);
 
-        startTransaction.setWindowCrop(leash, null);
-        startTransaction.setMatrix(leash, 1, 0, 0, 1);
-        startTransaction.setCornerRadius(leash, 0);
-        startTransaction.setPosition(leash, bounds.left, bounds.top);
-
-        if (mHasFadeOut && prevPipTaskChange.getTaskInfo().isVisible()) {
-            if (mPipAnimationController.getCurrentAnimator() != null) {
-                mPipAnimationController.getCurrentAnimator().cancel();
-            }
-            startTransaction.setAlpha(leash, 1);
-        }
         mHasFadeOut = false;
         mCurrentPipTaskToken = null;
-        mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo());
+
+        // clean-up the state in PipTaskOrganizer if the PipTaskOrganizer#onTaskAppeared() hasn't
+        // been called yet with its leash reference now pointing to a new SurfaceControl not
+        // matching the leash of the pip we are removing.
+        if (mPipOrganizer.getSurfaceControl() == leash) {
+            mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo());
+        }
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 4176fe8..bda25d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -24,6 +24,7 @@
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
 import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
@@ -172,7 +173,9 @@
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
             @NonNull TransitionRequestInfo request) {
-        if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) {
+        if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()
+                && request.getTriggerTask() != null && mSplitHandler.getSplitItemPosition(
+                        request.getTriggerTask().token) != SPLIT_POSITION_UNDEFINED) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while "
                     + "Split-Screen is active, so treat it as Mixed.");
             if (request.getRemoteTransition() != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 3bf278c..e52fd00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -260,6 +260,12 @@
         // This is the only way to get display-id currently, so check display capabilities here.
         final DisplayLayout displayLayout = displayController.getDisplayLayout(
                 topTaskInfo.displayId);
+        // This condition should be true when using gesture navigation or the screen size is large
+        // (>600dp) because the bar is small relative to screen.
+        if (displayLayout.allowSeamlessRotationDespiteNavBarMoving()) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  nav bar allows seamless.");
+            return ROTATION_ANIMATION_SEAMLESS;
+        }
         // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
         // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
         // will not enter the reverse portrait orientation, so actually the orientation won't
@@ -272,13 +278,9 @@
             return animationHint;
         }
 
-        // If the navigation bar can't change sides, then it will jump when we change orientations
-        // and we don't rotate seamlessly - unless that is allowed, e.g. with gesture navigation
-        // where the navbar is low-profile enough that this isn't very noticeable.
-        if (!displayLayout.allowSeamlessRotationDespiteNavBarMoving()
-                && (!(displayLayout.navigationBarCanMove()
-                        && (displayChange.getStartAbsBounds().width()
-                                != displayChange.getStartAbsBounds().height())))) {
+        // If the navigation bar cannot change sides, then it will jump when changing orientation
+        // so do not use seamless rotation.
+        if (!displayLayout.navigationBarCanMove()) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                     "  nav bar changes sides, so not seamless.");
             return animationHint;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index cea0fcb..b217bd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -143,6 +143,7 @@
                     mHandler,
                     mChoreographer,
                     mDisplay.getDisplayId(),
+                    0 /* taskCornerRadius */,
                     mDecorationContainerSurface,
                     mDragPositioningCallback);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 3ccb938..e3cb8af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -235,6 +235,7 @@
                     mHandler,
                     mChoreographer,
                     mDisplay.getDisplayId(),
+                    mRelayoutParams.mCornerRadius,
                     mDecorationContainerSurface,
                     mDragPositioningCallback);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 05a7588..68b63e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -56,7 +56,7 @@
  */
 class DragResizeInputListener implements AutoCloseable {
     private static final String TAG = "DragResizeInputListener";
-
+    private static final float TOP_CORNER_PADDING = 1.5f;
     private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
     private final Handler mHandler;
     private final Choreographer mChoreographer;
@@ -74,6 +74,7 @@
     private int mTaskHeight;
     private int mResizeHandleThickness;
     private int mCornerSize;
+    private int mTaskCornerRadius;
 
     private Rect mLeftTopCornerBounds;
     private Rect mRightTopCornerBounds;
@@ -88,12 +89,14 @@
             Handler handler,
             Choreographer choreographer,
             int displayId,
+            int taskCornerRadius,
             SurfaceControl decorationSurface,
             DragPositioningCallback callback) {
         mInputManager = context.getSystemService(InputManager.class);
         mHandler = handler;
         mChoreographer = choreographer;
         mDisplayId = displayId;
+        mTaskCornerRadius = taskCornerRadius;
         mDecorationSurface = decorationSurface;
         // Use a fake window as the backing surface is a container layer and we don't want to create
         // a buffer layer for it so we can't use ViewRootImpl.
@@ -384,19 +387,67 @@
         @DragPositioningCallback.CtrlType
         private int calculateResizeHandlesCtrlType(float x, float y) {
             int ctrlType = 0;
-            if (x < 0) {
+            // mTaskCornerRadius is only used in comparing with corner regions. Comparisons with
+            // sides will use the bounds specified in setGeometry and not go into task bounds.
+            if (x < mTaskCornerRadius) {
                 ctrlType |= CTRL_TYPE_LEFT;
             }
-            if (x > mTaskWidth) {
+            if (x > mTaskWidth - mTaskCornerRadius) {
                 ctrlType |= CTRL_TYPE_RIGHT;
             }
-            if (y < 0) {
+            if (y < mTaskCornerRadius) {
                 ctrlType |= CTRL_TYPE_TOP;
             }
-            if (y > mTaskHeight) {
+            if (y > mTaskHeight - mTaskCornerRadius) {
                 ctrlType |= CTRL_TYPE_BOTTOM;
             }
-            return ctrlType;
+            return checkDistanceFromCenter(ctrlType, x, y);
+        }
+
+        // If corner input is not within appropriate distance of corner radius, do not use it.
+        // If input is not on a corner or is within valid distance, return ctrlType.
+        @DragPositioningCallback.CtrlType
+        private int checkDistanceFromCenter(@DragPositioningCallback.CtrlType int ctrlType,
+                float x, float y) {
+            int centerX;
+            int centerY;
+
+            // Determine center of rounded corner circle; this is simply the corner if radius is 0.
+            switch (ctrlType) {
+                case CTRL_TYPE_LEFT | CTRL_TYPE_TOP: {
+                    centerX = mTaskCornerRadius;
+                    centerY = mTaskCornerRadius;
+                    break;
+                }
+                case CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM: {
+                    centerX = mTaskCornerRadius;
+                    centerY = mTaskHeight - mTaskCornerRadius;
+                    break;
+                }
+                case CTRL_TYPE_RIGHT | CTRL_TYPE_TOP: {
+                    centerX = mTaskWidth - mTaskCornerRadius;
+                    centerY = mTaskCornerRadius;
+                    break;
+                }
+                case CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM: {
+                    centerX = mTaskWidth - mTaskCornerRadius;
+                    centerY = mTaskHeight - mTaskCornerRadius;
+                    break;
+                }
+                default: {
+                    return ctrlType;
+                }
+            }
+            double distanceFromCenter = Math.hypot(x - centerX, y - centerY);
+
+            // TODO(b/286461778): Remove this when input in top corner gap no longer goes to header
+            float cornerPadding = (ctrlType & CTRL_TYPE_TOP) != 0 ? TOP_CORNER_PADDING : 1;
+
+            if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness * cornerPadding
+                    && distanceFromCenter >= mTaskCornerRadius) {
+                return ctrlType;
+            }
+            return 0;
         }
 
         @DragPositioningCallback.CtrlType
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 961e3e9..ff380e9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -702,8 +702,8 @@
                 createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
 
         final DisplayController displays = createTestDisplayController();
-        final @Surface.Rotation int upsideDown = displays
-                .getDisplayLayout(DEFAULT_DISPLAY).getUpsideDownRotation();
+        final DisplayLayout displayLayout = displays.getDisplayLayout(DEFAULT_DISPLAY);
+        final @Surface.Rotation int upsideDown = displayLayout.getUpsideDownRotation();
 
         TransitionInfo.Change displayChange = new ChangeBuilder(TRANSIT_CHANGE)
                 .setFlags(FLAG_IS_DISPLAY).setRotate().build();
@@ -743,7 +743,8 @@
         assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint(
                 displayChange, noTask, displays));
 
-        // Not seamless if one of rotations is upside-down
+        // Not seamless if the nav bar cares rotation and one of rotations is upside-down.
+        doReturn(false).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving();
         displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
                 .setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build();
         final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 0332c9f..363dd01 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -208,7 +208,7 @@
         @Override
         public void run() {
             final View host = mHost.get();
-            if (host != null) {
+            if (host != null && host.isVisibleToUser()) {
                 host.announceForAccessibility(mTextToAnnounce);
             }
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 7cedecc..239a0cc 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -326,7 +326,7 @@
         }
 
         if (!Objects.equals(prevContentDescription, mView.getContentDescription())
-                && mView.getContentDescription() != null) {
+                && mView.getContentDescription() != null && mView.isVisibleToUser()) {
             mView.announceForAccessibility(mView.getContentDescription());
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 8af92ce..806d1af 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -34,6 +34,7 @@
 import android.content.res.Resources;
 import android.graphics.RectF;
 import android.os.Handler;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -277,9 +278,11 @@
 
     // invalidate the view's own bounds all the way up the view hierarchy
     public static void invalidateGlobalRegion(View view) {
+        Trace.beginSection("SwipeHelper.invalidateGlobalRegion");
         invalidateGlobalRegion(
             view,
             new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
+        Trace.endSection();
     }
 
     // invalidate a rectangle relative to the view's coordinate system all the way up the view
@@ -492,7 +495,7 @@
                 }
                 if (!mCancelled || wasRemoved) {
                     mCallback.onChildDismissed(animView);
-                    resetSwipeOfView(animView);
+                    resetViewIfSwiping(animView);
                 }
                 if (endAction != null) {
                     endAction.accept(mCancelled);
@@ -547,7 +550,7 @@
 
             if (!cancelled) {
                 updateSwipeProgressFromOffset(animView, canBeDismissed);
-                resetSwipeOfView(animView);
+                resetViewIfSwiping(animView);
                 // Clear the snapped view after success, assuming it's not being swiped now
                 if (animView == mTouchedView && !mIsSwiping) {
                     mTouchedView = null;
@@ -811,7 +814,7 @@
         return mIsSwiping ? mTouchedView : null;
     }
 
-    protected void resetSwipeOfView(View view) {
+    protected void resetViewIfSwiping(View view) {
         if (getSwipedView() == view) {
             resetSwipeState();
         }
@@ -825,6 +828,12 @@
         resetSwipeStates(/* resetAll= */ true);
     }
 
+    public void forceResetSwipeState(@NonNull View view) {
+        if (view.getTranslationX() == 0) return;
+        setTranslation(view, 0);
+        updateSwipeProgressFromOffset(view, /* dismissable= */ true, 0);
+    }
+
     /** This method resets the swipe state, and if `resetAll` is true, also resets the snap state */
     private void resetSwipeStates(boolean resetAll) {
         final View touchedView = mTouchedView;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0e0f17b..e5170b8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -89,7 +89,7 @@
     // TODO(b/277338665): Tracking Bug
     @JvmField
     val NOTIFICATION_SHELF_REFACTOR =
-        unreleasedFlag(271161129, "notification_shelf_refactor", teamfood = true)
+        unreleasedFlag(271161129, "notification_shelf_refactor")
 
     @JvmField
     val ANIMATED_NOTIFICATION_SHADE_INSETS =
@@ -259,6 +259,11 @@
     @JvmField
     val FP_LISTEN_OCCLUDING_APPS = unreleasedFlag(237, "fp_listen_occluding_apps")
 
+    /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
+    // TODO(b/286563884): Tracking bug
+    @JvmField
+    val KEYGUARD_TALKBACK_FIX = unreleasedFlag(238, "keyguard_talkback_fix")
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7294137..6d00a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1659,10 +1659,9 @@
                 Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding));
         mKeyguardNotificationBottomPadding = bottomPadding;
 
-        float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
-                // getMinStackScrollerPadding is from the top of the screen,
-                // but we need it from the top of the NSSL.
-                - mNotificationStackScrollLayoutController.getTop();
+        float staticTopPadding = mClockPositionAlgorithm.getLockscreenNotifPadding(
+                mNotificationStackScrollLayoutController.getTop());
+
         mKeyguardNotificationTopPadding = staticTopPadding;
 
         // To debug the available space, enable debug lines in this class. If you change how the
@@ -1676,8 +1675,8 @@
             Log.i(TAG, "\n");
             Log.i(TAG, "staticTopPadding[" + staticTopPadding
                     + "] = Clock.padding["
-                    + mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
-                    + "] - NSSLC.top[" + mNotificationStackScrollLayoutController.getTop()
+                    + mClockPositionAlgorithm.getLockscreenNotifPadding(
+                            mNotificationStackScrollLayoutController.getTop())
                     + "]"
             );
             Log.i(TAG, "bottomPadding[" + bottomPadding
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 36025e8..0b5e436 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3992,7 +3992,7 @@
             mNotificationsController.resetUserExpandedStates();
             clearTemporaryViews();
             clearUserLockedViews();
-            cancelActiveSwipe();
+            resetAllSwipeState();
         }
     }
 
@@ -4058,7 +4058,7 @@
                 mGroupExpansionManager.collapseGroups();
                 mExpandHelper.cancelImmediately();
                 if (!mIsExpansionChanging) {
-                    cancelActiveSwipe();
+                    resetAllSwipeState();
                 }
                 finalizeClearAllAnimation();
             }
@@ -4387,7 +4387,7 @@
         boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
         if (nowFullyHidden != wasFullyHidden) {
             updateVisibility();
-            mSwipeHelper.resetTouchState();
+            resetAllSwipeState();
         }
         if (!wasHiddenAtAll && nowHiddenAtAll) {
             resetExposedMenuView(true /* animate */, true /* animate */);
@@ -5847,9 +5847,14 @@
         }
     }
 
-    private void cancelActiveSwipe() {
+    private void resetAllSwipeState() {
+        Trace.beginSection("NSSL.resetAllSwipeState()");
         mSwipeHelper.resetTouchState();
+        for (int i = 0; i < getChildCount(); i++) {
+            mSwipeHelper.forceResetSwipeState(getChildAt(i));
+        }
         updateContinuousShadowDrawing();
+        Trace.endSection();
     }
 
     void updateContinuousShadowDrawing() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index ce2658d..fc213b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -309,7 +309,8 @@
             float newNotificationEnd = newYTranslation + newHeight;
             boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned();
             if (mClipNotificationScrollToTop
-                    && ((isHeadsUp && !firstHeadsUp) || child.isHeadsUpAnimatingAway())
+                    && !firstHeadsUp
+                    && (isHeadsUp || child.isHeadsUpAnimatingAway())
                     && newNotificationEnd > firstHeadsUpEnd
                     && !ambientState.isShadeExpanded()) {
                 // The bottom of this view is peeking out from under the previous view.
@@ -613,13 +614,12 @@
                     updateViewWithShelf(view, viewState, shelfStart);
                 }
             }
-            // Avoid pulsing notification flicker during AOD to LS
-            // A pulsing notification is already expanded, no need to expand it again with animation
-            if (ambientState.isPulsingRow(view)) {
-                expansionFraction = 1.0f;
+            viewState.height = getMaxAllowedChildHeight(view);
+            if (!view.isPinned() && !view.isHeadsUpAnimatingAway()
+                    && !ambientState.isPulsingRow(view)) {
+                // The expansion fraction should not affect HUNs or pulsing notifications.
+                viewState.height *= expansionFraction;
             }
-            // Clip height of view right before shelf.
-            viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
         }
 
         algorithmState.mCurrentYPosition +=
@@ -779,6 +779,8 @@
                 }
             }
             if (row.isPinned()) {
+                // Make sure row yTranslation is at maximum the HUN yTranslation,
+                // which accounts for AmbientState.stackTopMargin in split-shade.
                 childState.setYTranslation(
                         Math.max(childState.getYTranslation(), headsUpTranslation));
                 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
@@ -803,7 +805,11 @@
                 }
             }
             if (row.isHeadsUpAnimatingAway()) {
-                childState.setYTranslation(Math.max(childState.getYTranslation(), mHeadsUpInset));
+                // Make sure row yTranslation is at maximum the HUN yTranslation,
+                // which accounts for AmbientState.stackTopMargin in split-shade.
+                childState.setYTranslation(
+                        Math.max(childState.getYTranslation(), headsUpTranslation));
+                // keep it visible for the animation
                 childState.hidden = false;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 561bd91..fc3c85a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -229,12 +229,18 @@
         }
     }
 
-    public float getLockscreenMinStackScrollerPadding() {
+    /**
+     * @param nsslTop NotificationStackScrollLayout top, which is below top of the srceen.
+     * @return Distance from nsslTop to top of the first view in the lockscreen shade.
+     */
+    public float getLockscreenNotifPadding(float nsslTop) {
         if (mBypassEnabled) {
-            return mUnlockedStackScrollerPadding;
+            return mUnlockedStackScrollerPadding - nsslTop;
         } else if (mIsSplitShade) {
-            return mSplitShadeTargetTopMargin + mUserSwitchHeight;
+            return mSplitShadeTargetTopMargin + mUserSwitchHeight - nsslTop;
         } else {
+            // Non-bypass portrait shade already uses values from nsslTop
+            // so we don't need to subtract it here.
             return mMinTopMargin + mKeyguardStatusHeight;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 7632d01..df65c09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -29,6 +30,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.animation.Animator;
@@ -670,6 +672,28 @@
     }
 
     @Test
+    public void testForceResetSwipeStateDoesNothingIfTranslationIsZero() {
+        doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
+        doReturn(0f).when(mNotificationRow).getTranslationX();
+
+        mSwipeHelper.forceResetSwipeState(mNotificationRow);
+
+        verify(mNotificationRow).getTranslationX();
+        verifyNoMoreInteractions(mNotificationRow);
+    }
+
+    @Test
+    public void testForceResetSwipeStateResetsTranslationAndAlpha() {
+        doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
+        doReturn(10f).when(mNotificationRow).getTranslationX();
+
+        mSwipeHelper.forceResetSwipeState(mNotificationRow);
+
+        verify(mNotificationRow).setTranslation(eq(0f));
+        verify(mNotificationRow).setContentAlpha(eq(1f));
+    }
+
+    @Test
     public void testContentAlphaRemainsUnchangedWhenFeatureFlagIsDisabled() {
 
         // Returning true prevents alpha fade. In an unmocked scenario the callback is instantiated
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 7b1565e..d107a0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -34,7 +34,6 @@
 @SmallTest
 class StackScrollAlgorithmTest : SysuiTestCase() {
 
-
     @JvmField @Rule
     var expect: Expect = Expect.create()
 
@@ -81,26 +80,58 @@
     fun resetViewStates_defaultHun_yTranslationIsInset() {
         whenever(notificationRow.isPinned).thenReturn(true)
         whenever(notificationRow.isHeadsUp).thenReturn(true)
-
-        stackScrollAlgorithm.resetViewStates(ambientState, 0)
-
-        assertThat(notificationRow.viewState.yTranslation)
-                .isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
+        resetViewStates_hunYTranslationIsInset()
     }
 
     @Test
-    fun resetViewStates_stackMargin_changesHunYTranslation() {
+    fun resetViewStates_defaultHunWithStackMargin_changesHunYTranslation() {
         whenever(notificationRow.isPinned).thenReturn(true)
         whenever(notificationRow.isHeadsUp).thenReturn(true)
-        val minHeadsUpTranslation = context.resources
-                .getDimensionPixelSize(R.dimen.notification_side_paddings)
+        resetViewStates_stackMargin_changesHunYTranslation()
+    }
 
-        // split shade case with top margin introduced by shade's status bar
-        ambientState.stackTopMargin = 100
-        stackScrollAlgorithm.resetViewStates(ambientState, 0)
+    @Test
+    fun resetViewStates_hunAnimatingAway_yTranslationIsInset() {
+        whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+        resetViewStates_hunYTranslationIsInset()
+    }
 
-        // top margin presence should decrease heads up translation up to minHeadsUpTranslation
-        assertThat(notificationRow.viewState.yTranslation).isEqualTo(minHeadsUpTranslation)
+    @Test
+    fun resetViewStates_hunAnimatingAway_StackMarginChangesHunYTranslation() {
+        whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+        resetViewStates_stackMargin_changesHunYTranslation()
+    }
+
+    @Test
+    fun resetViewStates_hunAnimatingAway_bottomNotClipped() {
+        whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+        stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+        assertThat(notificationRow.viewState.clipBottomAmount).isEqualTo(0)
+    }
+
+    @Test
+    fun resetViewStates_hunsOverlapping_bottomHunClipped() {
+        val topHun = mockExpandableNotificationRow()
+        val bottomHun = mockExpandableNotificationRow()
+        whenever(topHun.isHeadsUp).thenReturn(true)
+        whenever(topHun.isPinned).thenReturn(true)
+        whenever(bottomHun.isHeadsUp).thenReturn(true)
+        whenever(bottomHun.isPinned).thenReturn(true)
+
+        resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun)
+    }
+
+    @Test
+    fun resetViewStates_hunsOverlappingAndBottomHunAnimatingAway_bottomHunClipped() {
+        val topHun = mockExpandableNotificationRow()
+        val bottomHun = mockExpandableNotificationRow()
+        whenever(topHun.isHeadsUp).thenReturn(true)
+        whenever(topHun.isPinned).thenReturn(true)
+        whenever(bottomHun.isHeadsUpAnimatingAway).thenReturn(true)
+
+        resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun)
     }
 
     @Test
@@ -835,6 +866,57 @@
         ambientState.stackHeight = ambientState.stackEndHeight * fraction
     }
 
+    private fun resetViewStates_hunYTranslationIsInset() {
+        stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+        assertThat(notificationRow.viewState.yTranslation)
+                .isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
+    }
+
+    private fun resetViewStates_stackMargin_changesHunYTranslation() {
+        val stackTopMargin = 50
+        val headsUpTranslationY = stackScrollAlgorithm.mHeadsUpInset - stackTopMargin
+
+        // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild
+        ambientState.shelf = notificationShelf
+
+        // split shade case with top margin introduced by shade's status bar
+        ambientState.stackTopMargin = stackTopMargin
+        stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+        // heads up translation should be decreased by the top margin
+        assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTranslationY)
+    }
+
+    private fun resetViewStates_hunsOverlapping_bottomHunClipped(
+            topHun: ExpandableNotificationRow,
+            bottomHun: ExpandableNotificationRow
+    ) {
+        val topHunHeight = mContext.resources.getDimensionPixelSize(
+                R.dimen.notification_content_min_height)
+        val bottomHunHeight = mContext.resources.getDimensionPixelSize(
+                R.dimen.notification_max_heads_up_height)
+        whenever(topHun.intrinsicHeight).thenReturn(topHunHeight)
+        whenever(bottomHun.intrinsicHeight).thenReturn(bottomHunHeight)
+
+        // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild
+        ambientState.shelf = notificationShelf
+
+        // add two overlapping HUNs
+        hostView.removeAllViews()
+        hostView.addView(topHun)
+        hostView.addView(bottomHun)
+
+        stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+        // the height shouldn't change
+        assertThat(topHun.viewState.height).isEqualTo(topHunHeight)
+        assertThat(bottomHun.viewState.height).isEqualTo(bottomHunHeight)
+        // the HUN at the bottom should be clipped
+        assertThat(topHun.viewState.clipBottomAmount).isEqualTo(0)
+        assertThat(bottomHun.viewState.clipBottomAmount).isEqualTo(bottomHunHeight - topHunHeight)
+    }
+
     private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
             expansionFraction: Float,
             expectedAlpha: Float,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 7e69efa..5d2b59b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -67,6 +67,8 @@
     private float mQsExpansion;
     private int mCutoutTopInset = 0;
     private boolean mIsSplitShade = false;
+    private boolean mBypassEnabled = false;
+    private int mUnlockedStackScrollerPadding = 0;
     private float mUdfpsTop = -1;
     private float mClockBottom = SCREEN_HEIGHT / 2;
     private boolean mClockTopAligned;
@@ -339,15 +341,52 @@
     }
 
     @Test
-    public void notifMinPaddingAlignedWithClockInSplitShadeMode() {
+    public void notifPadding_splitShade() {
         givenLockScreen();
         mIsSplitShade = true;
         mKeyguardStatusHeight = 200;
         // WHEN the position algorithm is run
         positionClock();
         // THEN the padding DOESN'T adjust for keyguard status height.
-        assertThat(mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding())
-                .isEqualTo(mKeyguardStatusBarHeaderHeight);
+        assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 10))
+                .isEqualTo(mKeyguardStatusBarHeaderHeight - 10);
+    }
+
+    @Test
+    public void notifPadding_portraitShade_bypassOff() {
+        givenLockScreen();
+        mIsSplitShade = false;
+        mBypassEnabled = false;
+
+        // mMinTopMargin = 100 = 80 + max(20, 0)
+        mKeyguardStatusBarHeaderHeight = 80;
+        mUserSwitchHeight = 20;
+        when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin))
+                .thenReturn(0);
+
+        mKeyguardStatusHeight = 200;
+
+        // WHEN the position algorithm is run
+        positionClock();
+
+        // THEN padding = 300 = mMinTopMargin(100) + mKeyguardStatusHeight(200)
+        assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 50))
+                .isEqualTo(300);
+    }
+
+    @Test
+    public void notifPadding_portraitShade_bypassOn() {
+        givenLockScreen();
+        mIsSplitShade = false;
+        mBypassEnabled = true;
+        mUnlockedStackScrollerPadding = 200;
+
+        // WHEN the position algorithm is run
+        positionClock();
+
+        // THEN padding = 150 = mUnlockedStackScrollerPadding(200) - nsslTop(50)
+        assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 50))
+                .isEqualTo(150);
     }
 
     @Test
@@ -589,8 +628,8 @@
                 0 /* userSwitchPreferredY */,
                 mDark,
                 ZERO_DRAG,
-                false /* bypassEnabled */,
-                0 /* unlockedStackScrollerPadding */,
+                mBypassEnabled,
+                mUnlockedStackScrollerPadding,
                 mQsExpansion,
                 mCutoutTopInset,
                 mIsSplitShade,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 3efb6c3..c6e9a7d 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -162,6 +162,8 @@
 // TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
 public class VcnManagementService extends IVcnManagementService.Stub {
     @NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
+    @NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
+
     private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5);
     private static final int LOCAL_LOG_LINE_COUNT = 512;
 
@@ -223,7 +225,9 @@
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
-        mContext = requireNonNull(context, "Missing context");
+        mContext =
+                requireNonNull(context, "Missing context")
+                        .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
         mDeps = requireNonNull(deps, "Missing dependencies");
 
         mLooper = mDeps.getLooper();
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 9b4f968..0d423d8 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -80,6 +80,7 @@
  */
 public class VpnManagerService extends IVpnManager.Stub {
     private static final String TAG = VpnManagerService.class.getSimpleName();
+    private static final String CONTEXT_ATTRIBUTION_TAG = "VPN_MANAGER";
 
     @VisibleForTesting
     protected final HandlerThread mHandlerThread;
@@ -157,7 +158,7 @@
     }
 
     public VpnManagerService(Context context, Dependencies deps) {
-        mContext = context;
+        mContext = context.createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
         mDeps = deps;
         mHandlerThread = mDeps.makeHandlerThread();
         mHandlerThread.start();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 76af50d..42a4a85 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -691,10 +691,10 @@
 
             // If this receiver is going to be skipped, skip it now itself and don't even enqueue
             // it.
-            final boolean wouldBeSkipped = (mSkipPolicy.shouldSkipMessage(r, receiver) != null);
-            if (wouldBeSkipped) {
+            final String skipReason = mSkipPolicy.shouldSkipMessage(r, receiver);
+            if (skipReason != null) {
                 setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,
-                        "skipped by policy at enqueue");
+                        "skipped by policy at enqueue: " + skipReason);
                 continue;
             }
 
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index 01fb0d1..af99684 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -392,11 +392,9 @@
             if (TEMP_DUMP_TIME_LIMIT <= timeTaken) {
                 Slog.e(TAG, "Aborted stack trace dump (current primary pid=" + pid
                         + "); deadline exceeded.");
-                tmpTracesFile.delete();
                 if (latencyTracker != null) {
                     latencyTracker.dumpStackTracesTempFileTimedOut();
                 }
-                return null;
             }
             if (DEBUG_ANR) {
                 Slog.d(TAG, "Done with primary pid " + pid + " in " + timeTaken + "ms"
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index fc72a77..29a1941 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1018,13 +1018,15 @@
         }
     }
 
-    /*package*/ void clearA2dpSuspended() {
+    /*package*/ void clearA2dpSuspended(boolean internalOnly) {
         if (AudioService.DEBUG_COMM_RTE) {
-            Log.v(TAG, "clearA2dpSuspended");
+            Log.v(TAG, "clearA2dpSuspended, internalOnly: " + internalOnly);
         }
         synchronized (mBluetoothAudioStateLock) {
             mBluetoothA2dpSuspendedInt = false;
-            mBluetoothA2dpSuspendedExt = false;
+            if (!internalOnly) {
+                mBluetoothA2dpSuspendedExt = false;
+            }
             updateAudioHalBluetoothState();
         }
     }
@@ -1046,13 +1048,15 @@
         }
     }
 
-    /*package*/ void clearLeAudioSuspended() {
+    /*package*/ void clearLeAudioSuspended(boolean internalOnly) {
         if (AudioService.DEBUG_COMM_RTE) {
-            Log.v(TAG, "clearLeAudioSuspended");
+            Log.v(TAG, "clearLeAudioSuspended, internalOnly: " + internalOnly);
         }
         synchronized (mBluetoothAudioStateLock) {
             mBluetoothLeSuspendedInt = false;
-            mBluetoothLeSuspendedExt = false;
+            if (!internalOnly) {
+                mBluetoothLeSuspendedExt = false;
+            }
             updateAudioHalBluetoothState();
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 219dda3..ec85d57 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1547,7 +1547,7 @@
         }
 
         // Reset A2DP suspend state each time a new sink is connected
-        mDeviceBroker.clearA2dpSuspended();
+        mDeviceBroker.clearA2dpSuspended(true /* internalOnly */);
 
         // The convention for head tracking sensors associated with A2DP devices is to
         // use a UUID derived from the MAC address as follows:
@@ -1970,7 +1970,7 @@
                         "LE Audio device addr=" + address + " now available").printLog(TAG));
             }
             // Reset LEA suspend state each time a new sink is connected
-            mDeviceBroker.clearLeAudioSuspended();
+            mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
 
             UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ac03c82..c177c4e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -353,7 +353,6 @@
     private static final int MSG_PLAY_SOUND_EFFECT = 5;
     private static final int MSG_LOAD_SOUND_EFFECTS = 7;
     private static final int MSG_SET_FORCE_USE = 8;
-    private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
     private static final int MSG_SET_ALL_VOLUMES = 10;
     private static final int MSG_UNLOAD_SOUND_EFFECTS = 15;
     private static final int MSG_SYSTEM_READY = 16;
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index e46c3cc..c8a17e5 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -445,8 +445,8 @@
     /*package*/ synchronized void resetBluetoothSco() {
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-        mDeviceBroker.clearA2dpSuspended();
-        mDeviceBroker.clearLeAudioSuspended();
+        mDeviceBroker.clearA2dpSuspended(false /* internalOnly */);
+        mDeviceBroker.clearLeAudioSuspended(false /* internalOnly */);
         mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
     }
 
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index d2dcc50..41651fd 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -126,6 +126,7 @@
     private final boolean mDreamsActivatedOnChargeByDefault;
     private final boolean mDreamsActivatedOnDockByDefault;
     private final boolean mKeepDreamingWhenUnpluggingDefault;
+    private final boolean mDreamsDisabledByAmbientModeSuppressionConfig;
 
     private final CopyOnWriteArrayList<DreamManagerInternal.DreamManagerStateListener>
             mDreamManagerStateListeners = new CopyOnWriteArrayList<>();
@@ -239,6 +240,9 @@
         mSettingsObserver = new SettingsObserver(mHandler);
         mKeepDreamingWhenUnpluggingDefault = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
+        mDreamsDisabledByAmbientModeSuppressionConfig = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
+
     }
 
     @Override
@@ -406,6 +410,13 @@
                 return false;
             }
 
+            if (mDreamsDisabledByAmbientModeSuppressionConfig
+                    && mPowerManagerInternal.isAmbientDisplaySuppressed()) {
+                // Don't dream if Bedtime (or something else) is suppressing ambient.
+                Slog.i(TAG, "Can't start dreaming because ambient is suppressed.");
+                return false;
+            }
+
             if ((mWhenToDream & DREAM_ON_CHARGE) == DREAM_ON_CHARGE) {
                 return mIsCharging;
             }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c38bfd7..322083c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -366,6 +366,7 @@
                     if (lockedWallpaper != null) {
                         detachWallpaperLocked(lockedWallpaper);
                     }
+                    clearWallpaperBitmaps(mWallpaper.userId, FLAG_LOCK);
                     mLockWallpaperMap.remove(wallpaper.userId);
                     notifyColorsWhich |= FLAG_LOCK;
                 }
@@ -1991,7 +1992,11 @@
 
         WallpaperData data = null;
         synchronized (mLock) {
-            clearWallpaperLocked(false, which, userId, null);
+            if (mIsLockscreenLiveWallpaperEnabled) {
+                clearWallpaperLocked(callingPackage, false, which, userId);
+            } else {
+                clearWallpaperLocked(false, which, userId, null);
+            }
 
             if (which == FLAG_LOCK) {
                 data = mLockWallpaperMap.get(userId);
@@ -2008,7 +2013,64 @@
         }
     }
 
-    void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
+    private void clearWallpaperLocked(String callingPackage, boolean defaultFailed,
+            int which, int userId) {
+
+        // Might need to bring it in the first time to establish our rewrite
+        if (!mWallpaperMap.contains(userId)) {
+            loadSettingsLocked(userId, false, FLAG_LOCK | FLAG_SYSTEM);
+        }
+        final WallpaperData wallpaper = mWallpaperMap.get(userId);
+        final WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
+        if (which == FLAG_LOCK && lockWallpaper == null) {
+            // It's already gone; we're done.
+            if (DEBUG) {
+                Slog.i(TAG, "Lock wallpaper already cleared");
+            }
+            return;
+        }
+
+        RuntimeException e = null;
+        try {
+            if (userId != mCurrentUserId && !hasCrossUserPermission()) return;
+
+            final ComponentName component;
+            final int finalWhich;
+
+            if ((which & FLAG_LOCK) > 0 && lockWallpaper != null) {
+                clearWallpaperBitmaps(lockWallpaper);
+            }
+            if ((which & FLAG_SYSTEM) > 0) {
+                clearWallpaperBitmaps(wallpaper);
+            }
+
+            // lock only case: set the system wallpaper component to both screens
+            if (which == FLAG_LOCK) {
+                component = wallpaper.wallpaperComponent;
+                finalWhich = FLAG_LOCK | FLAG_SYSTEM;
+            } else {
+                component = defaultFailed ? mImageWallpaper : null;
+                finalWhich = which;
+            }
+
+            boolean success = withCleanCallingIdentity(() -> setWallpaperComponent(
+                    component, callingPackage, finalWhich, userId));
+            if (success) return;
+        } catch (IllegalArgumentException e1) {
+            e = e1;
+        }
+
+        // This can happen if the default wallpaper component doesn't
+        // exist. This should be a system configuration problem, but
+        // let's not let it crash the system and just live with no
+        // wallpaper.
+        Slog.e(TAG, "Default wallpaper component not found!", e);
+        withCleanCallingIdentity(() -> clearWallpaperComponentLocked(wallpaper));
+    }
+
+    // TODO(b/266818039) remove this version of the method
+    private void clearWallpaperLocked(boolean defaultFailed, int which, int userId,
+            IRemoteCallback reply) {
         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
         }
@@ -3099,8 +3161,13 @@
 
         // Migrate the bitmap files outright; no need to copy
         try {
-            Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath());
-            Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
+            if (!mIsLockscreenLiveWallpaperEnabled || sysWP.wallpaperFile.exists()) {
+                Os.rename(sysWP.wallpaperFile.getAbsolutePath(),
+                        lockWP.wallpaperFile.getAbsolutePath());
+            }
+            if (!mIsLockscreenLiveWallpaperEnabled || sysWP.cropFile.exists()) {
+                Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
+            }
             mLockWallpaperMap.put(userId, lockWP);
             if (mIsLockscreenLiveWallpaperEnabled) {
                 SELinux.restorecon(lockWP.wallpaperFile);
@@ -3163,16 +3230,17 @@
     }
 
     @VisibleForTesting
-    void setWallpaperComponent(ComponentName name, String callingPackage,
+    boolean setWallpaperComponent(ComponentName name, String callingPackage,
             @SetWallpaperFlags int which, int userId) {
         if (mIsLockscreenLiveWallpaperEnabled) {
-            setWallpaperComponentInternal(name, callingPackage, which, userId);
+            return setWallpaperComponentInternal(name, callingPackage, which, userId);
         } else {
             setWallpaperComponentInternalLegacy(name, callingPackage, which, userId);
+            return true;
         }
     }
 
-    private void setWallpaperComponentInternal(ComponentName name, String callingPackage,
+    private boolean setWallpaperComponentInternal(ComponentName name, String callingPackage,
             @SetWallpaperFlags int which, int userIdIn) {
         if (DEBUG) {
             Slog.v(TAG, "Setting new live wallpaper: which=" + which + ", component: " + name);
@@ -3183,6 +3251,7 @@
         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
 
         boolean shouldNotifyColors = false;
+        boolean bindSuccess;
         final WallpaperData newWallpaper;
 
         synchronized (mLock) {
@@ -3231,7 +3300,7 @@
                  */
                 boolean forceRebind = same && systemIsBoth && which == FLAG_SYSTEM;
 
-                boolean bindSuccess = bindWallpaperComponentLocked(name, /* force */
+                bindSuccess = bindWallpaperComponentLocked(name, /* force */
                         forceRebind, /* fromUser */ true, newWallpaper, callback);
                 if (bindSuccess) {
                     if (!same) {
@@ -3250,8 +3319,10 @@
                             });
                         }
                     }
+                    boolean lockBitmapCleared = false;
                     if (!mImageWallpaper.equals(newWallpaper.wallpaperComponent)) {
                         clearWallpaperBitmaps(newWallpaper);
+                        lockBitmapCleared = newWallpaper.mWhich == FLAG_LOCK;
                     }
                     newWallpaper.wallpaperId = makeWallpaperIdLocked();
                     notifyCallbacksLocked(newWallpaper);
@@ -3269,6 +3340,9 @@
                                 updateEngineFlags(newWallpaper);
                             }
                         }
+                        if (!lockBitmapCleared) {
+                            clearWallpaperBitmaps(newWallpaper.userId, FLAG_LOCK);
+                        }
                         mLockWallpaperMap.remove(newWallpaper.userId);
                     }
                 }
@@ -3281,6 +3355,7 @@
             notifyWallpaperColorsChanged(newWallpaper, which);
             notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         }
+        return bindSuccess;
     }
 
     // TODO(b/266818039) Remove this method
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 4123f80..960b57c 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -118,6 +118,7 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class VcnManagementServiceTest {
+    private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
     private static final String TEST_PACKAGE_NAME =
             VcnManagementServiceTest.class.getPackage().getName();
     private static final String TEST_PACKAGE_NAME_2 = "TEST_PKG_2";
@@ -177,6 +178,7 @@
                     0 /* carrierId */,
                     0 /* profileClass */);
 
+    private final Context mMockContextWithoutAttributionTag = mock(Context.class);
     private final Context mMockContext = mock(Context.class);
     private final VcnManagementService.Dependencies mMockDeps =
             mock(VcnManagementService.Dependencies.class);
@@ -202,6 +204,10 @@
     private final IBinder mMockIBinder = mock(IBinder.class);
 
     public VcnManagementServiceTest() throws Exception {
+        doReturn(mMockContext)
+                .when(mMockContextWithoutAttributionTag)
+                .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
+
         setupSystemService(
                 mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
         setupSystemService(
@@ -249,7 +255,7 @@
         doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk();
 
         setupMockedCarrierPrivilege(true);
-        mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
+        mVcnMgmtSvc = new VcnManagementService(mMockContextWithoutAttributionTag, mMockDeps);
         setupActiveSubscription(TEST_UUID_1);
 
         doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();