Merge "FreeformHandler should just intercept, not claim animations" into tm-qpr-dev
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6fc0c26..f17d5b7 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -369,7 +371,8 @@
      * @hide
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getTasks(maxNum, false /* filterForVisibleRecents */);
+        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
@@ -378,7 +381,8 @@
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(
             int maxNum, boolean filterOnlyVisibleRecents) {
-        return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */);
+        return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
@@ -388,8 +392,20 @@
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(
             int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+        return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+    }
+
+    /**
+     * @return List of running tasks that can be filtered by visibility and displayId in recents
+     * and keep intent extra.
+     * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+     * @hide
+     */
+    public List<ActivityManager.RunningTaskInfo> getTasks(
+            int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
         try {
-            return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra);
+            return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra,
+                    displayId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 0bf16a0..02be051 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -158,7 +158,7 @@
     boolean removeTask(int taskId);
     void removeAllVisibleRecentTasks();
     List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
-            boolean keepIntentExtra);
+            boolean keepIntentExtra, int displayId);
     void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task,
             int flags, in Bundle options);
     ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index cce3e8c..a2cb1d5 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -733,7 +733,7 @@
         }
         for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
             // Only update the server side insets here.
-            if (type == ITYPE_CAPTION_BAR) continue;
+            if (!CAPTION_ON_SHELL && type == ITYPE_CAPTION_BAR) continue;
             InsetsSource source = mState.peekSource(type);
             if (source == null) continue;
             if (newState.peekSource(type) == null) {
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index dbfa4d3..48343803 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -66,6 +66,8 @@
     private static final long INVALID_ID = -1;
     public static final int NANOS_IN_MILLISECOND = 1_000_000;
 
+    private static final int MAX_LENGTH_EVENT_DESC = 20;
+
     static final int REASON_END_UNKNOWN = -1;
     static final int REASON_END_NORMAL = 0;
     static final int REASON_END_SURFACE_DESTROYED = 1;
@@ -394,7 +396,18 @@
         return true;
     }
 
-    private void markEvent(String desc) {
+    /**
+     * Mark the FrameTracker events in the trace.
+     *
+     * @param desc The description of the trace event,
+     *            shouldn't exceed {@link #MAX_LENGTH_EVENT_DESC}.
+     */
+    private void markEvent(@NonNull String desc) {
+        if (desc.length() > MAX_LENGTH_EVENT_DESC) {
+            throw new IllegalArgumentException(TextUtils.formatSimple(
+                    "The length of the trace event description <%s> exceeds %d",
+                    desc, MAX_LENGTH_EVENT_DESC));
+        }
         Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
         Trace.endSection();
     }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e625b31..72de78c 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -149,6 +149,10 @@
     private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
     private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
 
+    @VisibleForTesting
+    public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
+    private static final int MAX_LENGTH_SESSION_NAME = 100;
+
     public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
     public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
 
@@ -732,6 +736,9 @@
      * @return the name of the cuj type
      */
     public static String getNameOfCuj(int cujType) {
+        // Please note:
+        // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+        // 2. The returned string should be the same with the name defined in atoms.proto.
         switch (cujType) {
             case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
                 return "SHADE_EXPAND_COLLAPSE";
@@ -1106,9 +1113,27 @@
         public Session(@CujType int cujType, @NonNull String postfix) {
             mCujType = cujType;
             mTimeStamp = System.nanoTime();
-            mName = TextUtils.isEmpty(postfix)
-                    ? String.format("J<%s>", getNameOfCuj(mCujType))
-                    : String.format("J<%s::%s>", getNameOfCuj(mCujType), postfix);
+            mName = generateSessionName(getNameOfCuj(cujType), postfix);
+        }
+
+        private String generateSessionName(@NonNull String cujName, @NonNull String cujPostfix) {
+            final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix);
+            // We assert that the cujName shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+            if (cujName.length() > MAX_LENGTH_OF_CUJ_NAME) {
+                throw new IllegalArgumentException(TextUtils.formatSimple(
+                        "The length of cuj name <%s> exceeds %d", cujName, MAX_LENGTH_OF_CUJ_NAME));
+            }
+            if (hasPostfix) {
+                final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length();
+                if (cujPostfix.length() > remaining) {
+                    cujPostfix = cujPostfix.substring(0, remaining - 3).concat("...");
+                }
+            }
+            // The max length of the whole string should be:
+            // 105 with postfix, 83 without postfix
+            return hasPostfix
+                    ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix)
+                    : TextUtils.formatSimple("J<%s>", cujName);
         }
 
         @CujType
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index d96f041..1519e48 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,10 +16,26 @@
 
 package com.android.internal.jank;
 
+import static android.text.TextUtils.formatSimple;
+
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
 import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_LAUNCH_CAMERA;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ADD;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_APPEAR;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_REMOVE;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
+import static com.android.internal.jank.InteractionJankMonitor.MAX_LENGTH_OF_CUJ_NAME;
+import static com.android.internal.jank.InteractionJankMonitor.getNameOfCuj;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -38,6 +54,7 @@
 import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.provider.DeviceConfig;
+import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewAttachTestActivity;
 
@@ -52,8 +69,12 @@
 import com.android.internal.jank.FrameTracker.ViewRootWrapper;
 import com.android.internal.jank.InteractionJankMonitor.Configuration;
 import com.android.internal.jank.InteractionJankMonitor.Session;
+import com.android.internal.util.FrameworkStatsLog;
+
+import com.google.common.truth.Expect;
 
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -64,11 +85,17 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 @SmallTest
 public class InteractionJankMonitorTest {
     private static final String CUJ_POSTFIX = "";
+    private static final SparseArray<String> ENUM_NAME_EXCEPTION_MAP = new SparseArray<>();
+    private static final SparseArray<String> CUJ_NAME_EXCEPTION_MAP = new SparseArray<>();
+    private static final String ENUM_NAME_PREFIX =
+            "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__";
+
     private ViewAttachTestActivity mActivity;
     private View mView;
     private HandlerThread mWorker;
@@ -77,6 +104,53 @@
     public ActivityTestRule<ViewAttachTestActivity> mRule =
             new ActivityTestRule<>(ViewAttachTestActivity.class);
 
+    @Rule
+    public final Expect mExpect = Expect.create();
+
+    @BeforeClass
+    public static void initialize() {
+        ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD"));
+        ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE"));
+        ENUM_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"));
+
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, "CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+                "CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, "CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_ROW_EXPAND, "CUJ_NOTIFICATION_SHADE_ROW_EXPAND");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_ROW_SWIPE, "CUJ_NOTIFICATION_SHADE_ROW_SWIPE");
+        CUJ_NAME_EXCEPTION_MAP.put(
+                CUJ_NOTIFICATION_SHADE_SCROLL_FLING, "CUJ_NOTIFICATION_SHADE_SCROLL_FLING");
+        CUJ_NAME_EXCEPTION_MAP.put(CUJ_LOCKSCREEN_LAUNCH_CAMERA, "CUJ_LOCKSCREEN_LAUNCH_CAMERA");
+        CUJ_NAME_EXCEPTION_MAP.put(CUJ_SPLIT_SCREEN_RESIZE, "CUJ_SPLIT_SCREEN_RESIZE");
+    }
+
+    private static String getEnumName(String name) {
+        return formatSimple("%s%s", ENUM_NAME_PREFIX, name);
+    }
+
     @Before
     public void setup() {
         // Prepare an activity for getting ThreadedRenderer later.
@@ -174,6 +248,82 @@
         }
     }
 
+    @Test
+    public void testCujsMapToEnumsCorrectly() {
+        List<Field> cujs = Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+                .filter(f -> f.getName().startsWith("CUJ_")
+                        && Modifier.isStatic(f.getModifiers())
+                        && f.getType() == int.class)
+                .collect(Collectors.toList());
+
+        Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
+                .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
+                        && Modifier.isStatic(f.getModifiers())
+                        && f.getType() == int.class)
+                .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
+
+        cujs.forEach(f -> {
+            final int cuj = getIntFieldChecked(f);
+            final String cujName = f.getName();
+            final String expectedEnumName = ENUM_NAME_EXCEPTION_MAP.contains(cuj)
+                    ? ENUM_NAME_EXCEPTION_MAP.get(cuj)
+                    : formatSimple("%s%s", ENUM_NAME_PREFIX, cujName.substring(4));
+            final int enumKey = CUJ_TO_STATSD_INTERACTION_TYPE[cuj];
+            final String enumName = enumsMap.get(enumKey);
+            final String expectedNameOfCuj = CUJ_NAME_EXCEPTION_MAP.contains(cuj)
+                    ? CUJ_NAME_EXCEPTION_MAP.get(cuj)
+                    : formatSimple("CUJ_%s", getNameOfCuj(cuj));
+            mExpect
+                    .withMessage(formatSimple(
+                            "%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey))
+                    .that(expectedEnumName.equals(enumName))
+                    .isTrue();
+            mExpect
+                    .withMessage(formatSimple("getNameOfCuj(%d) not matches %s", cuj, cujName))
+                    .that(cujName.equals(expectedNameOfCuj))
+                    .isTrue();
+        });
+    }
+
+    @Test
+    public void testCujNameLimit() {
+        Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+                .filter(f -> f.getName().startsWith("CUJ_")
+                        && Modifier.isStatic(f.getModifiers())
+                        && f.getType() == int.class)
+                .forEach(f -> {
+                    final int cuj = getIntFieldChecked(f);
+                    mExpect
+                            .withMessage(formatSimple(
+                                    "Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj)))
+                            .that(getNameOfCuj(cuj).length())
+                            .isAtMost(MAX_LENGTH_OF_CUJ_NAME);
+                });
+    }
+
+    @Test
+    public void testSessionNameLengthLimit() {
+        final int cujType = CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+        final String cujName = getNameOfCuj(cujType);
+        final String cujTag = "ThisIsTheCujTag";
+        final String tooLongTag = cujTag.repeat(10);
+
+        // Normal case, no postfix.
+        Session noPostfix = new Session(cujType, "");
+        assertThat(noPostfix.getName()).isEqualTo(formatSimple("J<%s>", cujName));
+
+        // Normal case, with postfix.
+        Session withPostfix = new Session(cujType, cujTag);
+        assertThat(withPostfix.getName()).isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag));
+
+        // Since the length of the cuj name is tested in another test, no need to test it here.
+        // Too long postfix case, should trim the postfix and keep the cuj name completed.
+        final String expectedTrimmedName = formatSimple("J<%s::%s>", cujName,
+                "ThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagT...");
+        Session longPostfix = new Session(cujType, tooLongTag);
+        assertThat(longPostfix.getName()).isEqualTo(expectedTrimmedName);
+    }
+
     private InteractionJankMonitor createMockedInteractionJankMonitor() {
         InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
         doReturn(true).when(monitor).shouldMonitor(anyInt());
@@ -217,4 +367,12 @@
 
         return tracker;
     }
+
+    private int getIntFieldChecked(Field field) {
+        try {
+            return field.getInt(null);
+        } catch (IllegalAccessException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
index 4eeb207..d6803e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
@@ -19,14 +19,14 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Paint;
+import android.graphics.Color;
 import android.graphics.Path;
+import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
 
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ShadowGenerator;
 import com.android.wm.shell.R;
 
 /**
@@ -44,78 +44,77 @@
      * will include the workprofile indicator on the badge if appropriate.
      */
     BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
-        ShadowGenerator shadowGenerator = new ShadowGenerator(mIconBitmapSize);
-        Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mIconBitmapSize);
-
         if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
-            userBadgedBitmap = Bitmap.createScaledBitmap(
-                    getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
-                            userBadgedAppIcon.getIntrinsicWidth()),
-                    mIconBitmapSize, mIconBitmapSize, /* filter */ true);
+            AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
+            userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(), ad.getForeground());
+        }
+        if (isImportantConversation) {
+            userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
+        }
+        Bitmap userBadgedBitmap = createIconBitmap(
+                userBadgedAppIcon, 1, BITMAP_GENERATION_MODE_WITH_SHADOW);
+        return createIconBitmap(userBadgedBitmap);
+    }
+
+    private class CircularRingDrawable extends CircularAdaptiveIcon {
+
+        final int mImportantConversationColor;
+        final Rect mTempBounds = new Rect();
+
+        final Drawable mDr;
+
+        CircularRingDrawable(Drawable dr) {
+            super(null, null);
+            mDr = dr;
+            mImportantConversationColor = mContext.getResources().getColor(
+                    R.color.important_conversation, null);
         }
 
-        if (isImportantConversation) {
-            final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+        @Override
+        public void draw(Canvas canvas) {
+            int save = canvas.save();
+            canvas.clipPath(getIconMask());
+            canvas.drawColor(mImportantConversationColor);
+            int ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
                     com.android.internal.R.dimen.importance_ring_stroke_width);
-            final int importantConversationColor = mContext.getResources().getColor(
-                    R.color.important_conversation, null);
-            Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
-                    userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
-            Canvas c = new Canvas(badgeAndRing);
-
-            Paint ringPaint = new Paint();
-            ringPaint.setStyle(Paint.Style.FILL);
-            ringPaint.setColor(importantConversationColor);
-            ringPaint.setAntiAlias(true);
-            c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
-
-            final int bitmapTop = (int) ringStrokeWidth;
-            final int bitmapLeft = (int) ringStrokeWidth;
-            final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
-            final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
-
-            Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
-                    bitmapHeight, /* filter */ true);
-            c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
-
-            shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
-            return createIconBitmap(badgeAndRing);
-        } else {
-            Canvas c = new Canvas();
-            c.setBitmap(userBadgedBitmap);
-            shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
-            return createIconBitmap(userBadgedBitmap);
+            mTempBounds.set(getBounds());
+            mTempBounds.inset(ringStrokeWidth, ringStrokeWidth);
+            mDr.setBounds(mTempBounds);
+            mDr.draw(canvas);
+            canvas.restoreToCount(save);
         }
     }
 
-    private Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
-        Drawable foreground = icon.getForeground();
-        Drawable background = icon.getBackground();
-        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas();
-        canvas.setBitmap(bitmap);
+    private static class CircularAdaptiveIcon extends AdaptiveIconDrawable {
 
-        // Clip canvas to circle.
-        Path circlePath = new Path();
-        circlePath.addCircle(/* x */ size / 2f,
-                /* y */ size / 2f,
-                /* radius */ size / 2f,
-                Path.Direction.CW);
-        canvas.clipPath(circlePath);
+        final Path mPath = new Path();
 
-        // Draw background.
-        background.setBounds(0, 0, size, size);
-        background.draw(canvas);
+        CircularAdaptiveIcon(Drawable bg, Drawable fg) {
+            super(bg, fg);
+        }
 
-        // Draw foreground. The foreground and background drawables are derived from adaptive icons
-        // Some icon shapes fill more space than others, so adaptive icons are normalized to about
-        // the same size. This size is smaller than the original bounds, so we estimate
-        // the difference in this offset.
-        int offset = size / 5;
-        foreground.setBounds(-offset, -offset, size + offset, size + offset);
-        foreground.draw(canvas);
+        @Override
+        public Path getIconMask() {
+            mPath.reset();
+            Rect bounds = getBounds();
+            mPath.addOval(bounds.left, bounds.top, bounds.right, bounds.bottom, Path.Direction.CW);
+            return mPath;
+        }
 
-        canvas.setBitmap(null);
-        return bitmap;
+        @Override
+        public void draw(Canvas canvas) {
+            int save = canvas.save();
+            canvas.clipPath(getIconMask());
+
+            canvas.drawColor(Color.BLACK);
+            Drawable d;
+            if ((d = getBackground()) != null) {
+                d.draw(canvas);
+            }
+            if ((d = getForeground()) != null) {
+                d.draw(canvas);
+            }
+            canvas.restoreToCount(save);
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
index 9d3bf34..5dab8a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 
@@ -65,4 +66,19 @@
             return null;
         }
     }
+
+    /**
+     * Creates the bitmap for the provided drawable and returns the scale used for
+     * drawing the actual drawable.
+     */
+    public Bitmap createIconBitmap(@NonNull Drawable icon, float[] outScale) {
+        if (outScale == null) {
+            outScale = new float[1];
+        }
+        icon = normalizeAndWrapToAdaptiveIcon(icon,
+                true /* shrinkNonAdaptiveIcons */,
+                null /* outscale */,
+                outScale);
+        return createIconBitmap(icon, outScale[0], BITMAP_GENERATION_MODE_WITH_SHADOW);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 69762c9..f437553 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -195,15 +195,18 @@
                     b.isImportantConversation());
             info.badgeBitmap = badgeBitmapInfo.icon;
             // Raw badge bitmap never includes the important conversation ring
-            info.mRawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon;
-            info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
+            info.mRawBadgeBitmap = b.isImportantConversation()
+                    ? badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon
+                    : badgeBitmapInfo.icon;
+
+            float[] bubbleBitmapScale = new float[1];
+            info.bubbleBitmap = iconFactory.createIconBitmap(bubbleDrawable, bubbleBitmapScale);
 
             // Dot color & placement
             Path iconPath = PathParser.createPathFromPathData(
                     c.getResources().getString(com.android.internal.R.string.config_icon_mask));
             Matrix matrix = new Matrix();
-            float scale = iconFactory.getNormalizer().getScale(bubbleDrawable,
-                    null /* outBounds */, null /* path */, null /* outMaskShape */);
+            float scale = bubbleBitmapScale[0];
             float radius = DEFAULT_PATH_SIZE / 2f;
             matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
                     radius /* pivot y */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index cd29741..0f997f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -158,9 +158,9 @@
 
     private void onInit() {
         // The very last handler (0 in the list) should be the default one.
-        mHandlers.add(mDefaultTransitionHandler);
+        mHandlers.add(0, mDefaultTransitionHandler);
         // Next lowest priority is remote transitions.
-        mHandlers.add(mRemoteTransitionHandler);
+        mHandlers.add(1, mRemoteTransitionHandler);
 
         ContentResolver resolver = mContext.getContentResolver();
         mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 4380bdc..087304b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -66,6 +66,7 @@
     final DisplayController mDisplayController;
     final ShellTaskOrganizer mTaskOrganizer;
     final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
+    final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
     final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
     private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
             new DisplayController.OnDisplaysChangedListener() {
@@ -102,7 +103,8 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface) {
         this(context, displayController, taskOrganizer, taskInfo, taskSurface,
-                SurfaceControl.Builder::new, new SurfaceControlViewHostFactory() {});
+                SurfaceControl.Builder::new, WindowContainerTransaction::new,
+                new SurfaceControlViewHostFactory() {});
     }
 
     WindowDecoration(
@@ -112,6 +114,7 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+            Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
             SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
         mContext = context;
         mDisplayController = displayController;
@@ -119,6 +122,7 @@
         mTaskInfo = taskInfo;
         mTaskSurface = taskSurface;
         mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
+        mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
         mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
 
         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
@@ -301,6 +305,10 @@
             mTaskBackgroundSurface.release();
             mTaskBackgroundSurface = null;
         }
+
+        final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
+        wct.removeInsetsProvider(mTaskInfo.token, CAPTION_INSETS_TYPES);
+        mTaskOrganizer.applyTransaction(wct);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index b318bb2..1e7d5fe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -233,6 +233,57 @@
     }
 
     @Test
+    public void testLayoutResultCalculation_visibleFocusedTaskToInvisible() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder decorContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(decorContainerSurface);
+        mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+                createMockSurfaceControlBuilder(taskBackgroundSurface);
+        mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+
+        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+                new ActivityManager.TaskDescription.Builder()
+                        .setBackgroundColor(Color.YELLOW);
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setTaskDescriptionBuilder(taskDescriptionBuilder)
+                .setBounds(TASK_BOUNDS)
+                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+                .setVisible(true)
+                .build();
+        taskInfo.isFocused = true;
+        // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
+        // 64px.
+        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+        mOutsetsDp.set(10, 20, 30, 40);
+
+        final SurfaceControl taskSurface = mock(SurfaceControl.class);
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+        windowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlViewHost, never()).release();
+        verify(decorContainerSurface, never()).release();
+        verify(taskBackgroundSurface, never()).release();
+        verify(mMockWindowContainerTransaction, never())
+                .removeInsetsProvider(eq(taskInfo.token), any());
+
+        taskInfo.isVisible = false;
+        windowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlViewHost).release();
+        verify(decorContainerSurface).release();
+        verify(taskBackgroundSurface).release();
+        verify(mMockWindowContainerTransaction).removeInsetsProvider(eq(taskInfo.token), any());
+    }
+
+    @Test
     public void testNotCrashWhenDisplayAppearsAfterTask() {
         doReturn(mock(Display.class)).when(mMockDisplayController)
                 .getDisplay(Display.DEFAULT_DISPLAY);
@@ -282,7 +333,7 @@
             ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
         return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
                 taskInfo, testSurface, new MockSurfaceControlBuilderSupplier(),
-                mMockSurfaceControlViewHostFactory);
+                () -> mMockWindowContainerTransaction, mMockSurfaceControlViewHostFactory);
     }
 
     private class MockSurfaceControlBuilderSupplier implements Supplier<SurfaceControl.Builder> {
@@ -313,9 +364,11 @@
                 ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
                 SurfaceControl taskSurface,
                 Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+                Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
                 SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
             super(context, displayController, taskOrganizer, taskInfo, taskSurface,
-                    surfaceControlBuilderSupplier, surfaceControlViewHostFactory);
+                    surfaceControlBuilderSupplier, windowContainerTransactionSupplier,
+                    surfaceControlViewHostFactory);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 333f6b9..ee45c42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1105,7 +1105,6 @@
                     isFolded, willGoToSleep, isShadeOpen, leaveOpen));
         }
         if (leaveOpen) {
-            mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
             if (mKeyguardStateController.isShowing()) {
                 // When device state changes on keyguard we don't want to keep the state of
                 // the shade and instead we open clean state of keyguard with shade closed.
@@ -1114,6 +1113,9 @@
                 // expanded. To prevent that we can close QS which resets QS and some parts of
                 // the shade to its default state. Read more in b/201537421
                 mCloseQsBeforeScreenOff = true;
+            } else {
+                // below makes shade stay open when going from folded to unfolded
+                mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 12597e0..eba2795 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -118,6 +118,33 @@
     private final UserInfoController mUserInfoController;
     private final Executor mSysUiMainExecutor;
 
+    // Listeners and callbacks. Note that we prefer member variable over anonymous class here to
+    // avoid the situation that some implementations, like KeyguardUpdateMonitor, use WeakReference
+    // internally and anonymous class could be released after registration.
+    private final ConfigurationController.ConfigurationListener mConfigurationListener =
+            new ConfigurationController.ConfigurationListener() {
+                @Override
+                public void onConfigChanged(Configuration newConfig) {
+                    mShell.onConfigurationChanged(newConfig);
+                }
+            };
+    private final KeyguardStateController.Callback mKeyguardStateCallback =
+            new KeyguardStateController.Callback() {
+                @Override
+                public void onKeyguardShowingChanged() {
+                    mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
+                            mKeyguardStateController.isOccluded(),
+                            mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
+                }
+            };
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onKeyguardDismissAnimationFinished() {
+                    mShell.onKeyguardDismissAnimationFinished();
+                }
+            };
+
     private boolean mIsSysUiStateValid;
     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
     private WakefulnessLifecycle.Observer mWakefulnessObserver;
@@ -159,28 +186,11 @@
     public void start() {
         // Notify with the initial configuration and subscribe for new config changes
         mShell.onConfigurationChanged(mContext.getResources().getConfiguration());
-        mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
-            @Override
-            public void onConfigChanged(Configuration newConfig) {
-                mShell.onConfigurationChanged(newConfig);
-            }
-        });
+        mConfigurationController.addCallback(mConfigurationListener);
 
         // Subscribe to keyguard changes
-        mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
-            @Override
-            public void onKeyguardShowingChanged() {
-                mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
-                        mKeyguardStateController.isOccluded(),
-                        mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
-            }
-        });
-        mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
-            @Override
-            public void onKeyguardDismissAnimationFinished() {
-                mShell.onKeyguardDismissAnimationFinished();
-            }
-        });
+        mKeyguardStateController.addCallback(mKeyguardStateCallback);
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
 
         // TODO: Consider piping config change and other common calls to a shell component to
         //  delegate internally
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 2a3509c..7b7f45a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -965,6 +965,7 @@
 
     @Test
     public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
         setFoldedStates(FOLD_STATE_FOLDED);
         setGoToSleepStates(FOLD_STATE_FOLDED);
         when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
@@ -975,6 +976,19 @@
     }
 
     @Test
+    public void deviceStateChange_unfolded_shadeOpen_onKeyguard_doesNotSetLeaveOpenOnKeyguardHide() {
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        setFoldedStates(FOLD_STATE_FOLDED);
+        setGoToSleepStates(FOLD_STATE_FOLDED);
+        when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+
+        setDeviceState(FOLD_STATE_UNFOLDED);
+
+        verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true);
+    }
+
+
+    @Test
     public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
         setFoldedStates(FOLD_STATE_FOLDED);
         setGoToSleepStates(FOLD_STATE_FOLDED);
diff --git a/services/core/java/com/android/server/app/GameTaskInfoProvider.java b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
index f078d98..25a4f37 100644
--- a/services/core/java/com/android/server/app/GameTaskInfoProvider.java
+++ b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.app;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -93,7 +95,8 @@
             runningTaskInfos = mActivityTaskManager.getTasks(
                     /* maxNum= */ Integer.MAX_VALUE,
                     /* filterOnlyVisibleRecents= */ false,
-                    /* keepIntentExtra= */ false);
+                    /* keepIntentExtra= */ false,
+                    INVALID_DISPLAY);
         } catch (RemoteException ex) {
             Slog.w(TAG, "Failed to fetch running tasks");
             return null;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index fcb2eb4..479629e 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -35,6 +35,7 @@
 import static com.android.server.display.DisplayDeviceInfo.FLAG_TOUCH_FEEDBACK_DISABLED;
 import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Point;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -110,15 +111,20 @@
         } else {
             uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
         }
+        MediaProjectionCallback mediaProjectionCallback =  null;
+        if (projection != null) {
+            mediaProjectionCallback = new MediaProjectionCallback(appToken);
+        }
         VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
-                ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+                ownerUid, ownerPackageName, surface, flags,
+                new Callback(callback, mHandler), projection, mediaProjectionCallback,
                 uniqueId, uniqueIndex, virtualDisplayConfig);
 
         mVirtualDisplayDevices.put(appToken, device);
 
         try {
             if (projection != null) {
-                projection.registerCallback(new MediaProjectionCallback(appToken));
+                projection.registerCallback(mediaProjectionCallback);
             }
             appToken.linkToDeath(device, 0);
         } catch (RemoteException ex) {
@@ -203,7 +209,7 @@
     }
 
     private void handleMediaProjectionStoppedLocked(IBinder appToken) {
-        VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
+        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
         if (device != null) {
             Slog.i(TAG, "Virtual display device released because media projection stopped: "
                     + device.mName);
@@ -223,6 +229,8 @@
         final String mName;
         private final int mFlags;
         private final Callback mCallback;
+        @Nullable private final IMediaProjection mProjection;
+        @Nullable private final IMediaProjectionCallback mMediaProjectionCallback;
 
         private int mWidth;
         private int mHeight;
@@ -240,7 +248,8 @@
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName, Surface surface, int flags,
-                Callback callback, String uniqueId, int uniqueIndex,
+                Callback callback, IMediaProjection projection,
+                IMediaProjectionCallback mediaProjectionCallback, String uniqueId, int uniqueIndex,
                 VirtualDisplayConfig virtualDisplayConfig) {
             super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
             mAppToken = appToken;
@@ -254,6 +263,8 @@
             mSurface = surface;
             mFlags = flags;
             mCallback = callback;
+            mProjection = projection;
+            mMediaProjectionCallback = mediaProjectionCallback;
             mDisplayState = Display.STATE_UNKNOWN;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
             mUniqueIndex = uniqueIndex;
@@ -269,6 +280,13 @@
                 Slog.i(TAG, "Virtual display device released because application token died: "
                     + mOwnerPackageName);
                 destroyLocked(false);
+                if (mProjection != null && mMediaProjectionCallback != null) {
+                    try {
+                        mProjection.unregisterCallback(mMediaProjectionCallback);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to unregister callback in binderDied", e);
+                    }
+                }
                 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED);
             }
         }
@@ -279,6 +297,13 @@
                 mSurface = null;
             }
             SurfaceControl.destroyDisplay(getDisplayTokenLocked());
+            if (mProjection != null && mMediaProjectionCallback != null) {
+                try {
+                    mProjection.unregisterCallback(mMediaProjectionCallback);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to unregister callback in destroy", e);
+                }
+            }
             if (binderAlive) {
                 mCallback.dispatchDisplayStopped();
             }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2591a36..7323718 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2311,16 +2311,25 @@
      * @return a list of {@link ActivityManager.RunningTaskInfo} with up to {@code maxNum} items
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */);
+        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
      * @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever
      *                                 be visible in the recent task list in systemui
      */
-    @Override
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
             boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+        return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+    }
+
+    /**
+     * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+     */
+    @Override
+    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
+            boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
 
@@ -2342,7 +2351,7 @@
             final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
             flags |= (allowed ? RunningTasks.FLAG_ALLOWED : 0);
             mRootWindowContainer.getRunningTasks(
-                    maxNum, list, flags, callingUid, callingProfileIds);
+                    maxNum, list, flags, callingUid, callingProfileIds, displayId);
         }
 
         return list;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index fe91122..aab9d5b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3345,9 +3345,16 @@
 
     @VisibleForTesting
     void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
-            int flags, int callingUid, ArraySet<Integer> profileIds) {
-        mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, this, callingUid,
-                profileIds);
+            int flags, int callingUid, ArraySet<Integer> profileIds, int displayId) {
+        WindowContainer root = this;
+        if (displayId != INVALID_DISPLAY) {
+            root = getDisplayContent(displayId);
+            if (root == null) {
+                return;
+            }
+        }
+        mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, mService.getRecentTasks(),
+                root, callingUid, profileIds);
     }
 
     void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1ec191e..120fec0 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -60,8 +60,8 @@
     private RecentTasks mRecentTasks;
     private boolean mKeepIntentExtra;
 
-    void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
-            RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+    void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+            WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
         // Return early if there are no tasks to fetch
         if (maxNum <= 0) {
             return;
@@ -76,7 +76,7 @@
         mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
         mFilterOnlyVisibleRecents =
                 (flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
-        mRecentTasks = root.mService.getRecentTasks();
+        mRecentTasks = recentTasks;
         mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
 
         final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 2749b11..25193d0 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3393,6 +3393,13 @@
             pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
             pw.println(prefix + "deepestLastOrientationSource=" + getLastOrientationSource());
         }
+        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+            pw.println(prefix + mLocalInsetsSourceProviders.size() + " LocalInsetsSourceProviders");
+            final String childPrefix = prefix + "  ";
+            for (int i = 0; i < mLocalInsetsSourceProviders.size(); ++i) {
+                mLocalInsetsSourceProviders.valueAt(i).dump(pw, childPrefix);
+            }
+        }
     }
 
     final void updateSurfacePositionNonOrganized() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index a8b340c..e4f9eaf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -218,8 +218,8 @@
         }).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any());
 
         mRunningTaskInfos = new ArrayList<>();
-        when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
-                mRunningTaskInfos);
+        when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt()))
+                .thenReturn(mRunningTaskInfos);
 
 
         final UserHandle userHandle = new UserHandle(USER_ID);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 83f375f..adf694c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1531,10 +1531,10 @@
         public boolean mLastAllowed;
 
         @Override
-        void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
-                RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+        void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+                WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
             mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
-            super.getTasks(maxNum, list, flags, root, callingUid, profileIds);
+            super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 33b2366..b1acae2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -35,6 +35,8 @@
 
 import androidx.test.filters.MediumTest;
 
+import com.google.common.truth.Correspondence;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,6 +54,9 @@
 public class RunningTasksTest extends WindowTestsBase {
 
     private static final ArraySet<Integer> PROFILE_IDS = new ArraySet<>();
+    private static final Correspondence<RunningTaskInfo, Integer> TASKINFO_HAS_ID =
+            Correspondence.transforming((RunningTaskInfo t) -> t.taskId, "has id");
+
 
     private RunningTasks mRunningTasks;
 
@@ -91,8 +96,8 @@
         // collected from all tasks across all the stacks
         final int numFetchTasks = 5;
         ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
-        mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+        mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < numFetchTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -102,7 +107,7 @@
         // and does not crash
         tasks.clear();
         mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
-                mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numTasks);
         for (int i = 0; i < numTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -126,7 +131,7 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
-                mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < tasks.size(); i++) {
             final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -151,8 +156,8 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, tasks,
-                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < tasks.size(); i++) {
             final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -184,8 +189,8 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, fetchTasks,
-                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(fetchTasks).hasSize(numFetchTasks);
         assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId);
         assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId);
@@ -210,4 +215,46 @@
         task.intent = activity.intent;
         return task;
     }
+
+    @Test
+    public void testMultipleDisplays() {
+        final DisplayContent display0 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+        final DisplayContent display1 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+        final int numTasks = 10;
+        final ArrayList<Task> tasks = new ArrayList<>();
+        for (int i = 0; i < numTasks; i++) {
+            final Task stack = new TaskBuilder(mSupervisor)
+                    .setDisplay(i % 2 == 0 ? display0 : display1)
+                    .setOnTop(true)
+                    .build();
+            final Task task = createTask(stack, ".Task" + i, i, i, null);
+            tasks.add(task);
+        }
+
+        final int numFetchTasks = numTasks;
+        final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
+
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), display0, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks / 2);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(0, 2, 4, 6, 8);
+
+        fetchTasks.clear();
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), display1, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks / 2);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(1, 3, 5, 7, 9);
+
+        fetchTasks.clear();
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    }
 }