Merge "Move the compose code to tm-qpr-dev (1/2)" into tm-qpr-dev
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 57dacd0..877e7d3 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -263,11 +263,6 @@
         // After orientation change, the onResume can come in before the top Activity has
         // left, so if the Activity is not top, wait a second for the top Activity to exit.
         if (mEnterTransitionCoordinator == null || activity.isTopOfTask()) {
-            if (mEnterTransitionCoordinator != null) {
-                mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
-                    mEnterTransitionCoordinator = null;
-                });
-            }
             restoreExitedViews();
             restoreReenteringViews();
         } else {
@@ -276,11 +271,6 @@
                 public void run() {
                     if (mEnterTransitionCoordinator == null ||
                             mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
-                        if (mEnterTransitionCoordinator != null) {
-                            mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
-                                mEnterTransitionCoordinator = null;
-                            });
-                        }
                         restoreExitedViews();
                         restoreReenteringViews();
                     } else if (mEnterTransitionCoordinator.isReturning()) {
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8be2b48..e3bca9c 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -103,7 +103,6 @@
     private boolean mOnLightBackground;
     private SizeF mCurrentSize = null;
     private RemoteViews.ColorResources mColorResources = null;
-    private SparseIntArray mColorMapping = null;
     // Stores the last remote views last inflated.
     private RemoteViews mLastInflatedRemoteViews = null;
     private long mLastInflatedRemoteViewsId = -1;
@@ -900,11 +899,19 @@
      * {@link android.R.color#system_neutral1_500}.
      */
     public void setColorResources(@NonNull SparseIntArray colorMapping) {
-        if (mColorMapping != null && isSameColorMapping(mColorMapping, colorMapping)) {
+        if (mColorResources != null
+                && isSameColorMapping(mColorResources.getColorMapping(), colorMapping)) {
             return;
         }
-        mColorMapping = colorMapping.clone();
-        mColorResources = RemoteViews.ColorResources.create(mContext, mColorMapping);
+        setColorResources(RemoteViews.ColorResources.create(mContext, colorMapping));
+    }
+
+    /** @hide **/
+    public void setColorResources(RemoteViews.ColorResources colorResources) {
+        if (colorResources == mColorResources) {
+            return;
+        }
+        mColorResources = colorResources;
         mColorMappingChanged = true;
         mViewMode = VIEW_MODE_NOINIT;
         reapplyLastRemoteViews();
@@ -934,7 +941,6 @@
     public void resetColorResources() {
         if (mColorResources != null) {
             mColorResources = null;
-            mColorMapping = null;
             mColorMappingChanged = true;
             mViewMode = VIEW_MODE_NOINIT;
             reapplyLastRemoteViews();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7690af6..fa37ca1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -276,7 +276,7 @@
      * @hide
      */
     public static final boolean CAPTION_ON_SHELL =
-            SystemProperties.getBoolean("persist.debug.caption_on_shell", false);
+            SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", false);
 
     /**
      * Whether the client should compute the window frame on its own.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2879cd8..5eec054 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -594,8 +594,8 @@
      *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
      */
     private abstract static class Action implements Parcelable {
-        public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException;
+        public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException;
 
         public static final int MERGE_REPLACE = 0;
         public static final int MERGE_APPEND = 1;
@@ -626,7 +626,7 @@
          * Override this if some of the tasks can be performed async.
          */
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             return this;
         }
 
@@ -661,9 +661,7 @@
     // Constant used during async execution. It is not parcelable.
     private static final Action ACTION_NOOP = new RuntimeAction() {
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
-        }
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { }
     };
 
     /**
@@ -798,8 +796,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View view = root.findViewById(viewId);
             if (!(view instanceof AdapterView<?>)) return;
 
@@ -834,8 +831,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -846,7 +842,7 @@
                 OnItemClickListener listener = (parent, view, position, id) -> {
                     RemoteResponse response = findRemoteResponseTag(view);
                     if (response != null) {
-                        response.handleViewInteraction(view, handler);
+                        response.handleViewInteraction(view, params.handler);
                     }
                 };
                 av.setOnItemClickListener(listener);
@@ -910,8 +906,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -935,7 +930,7 @@
                     ((RemoteViewsListAdapter) a).setViewsList(list);
                 } else {
                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
-                            colorResources));
+                            params.colorResources));
                 }
             } else if (target instanceof AdapterViewAnimator) {
                 AdapterViewAnimator v = (AdapterViewAnimator) target;
@@ -944,7 +939,7 @@
                     ((RemoteViewsListAdapter) a).setViewsList(list);
                 } else {
                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
-                            colorResources));
+                            params.colorResources));
                 }
             }
         }
@@ -1025,8 +1020,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException {
             View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1053,7 +1048,7 @@
                     && adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
                 try {
                     ((RemoteCollectionItemsAdapter) adapter).setData(
-                            mItems, handler, colorResources);
+                            mItems, params.handler, params.colorResources);
                 } catch (Throwable throwable) {
                     // setData should never failed with the validation in the items builder, but if
                     // it does, catch and rethrow.
@@ -1063,8 +1058,8 @@
             }
 
             try {
-                adapterView.setAdapter(
-                        new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
+                adapterView.setAdapter(new RemoteCollectionItemsAdapter(mItems,
+                        params.handler, params.colorResources));
             } catch (Throwable throwable) {
                 // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
                 // a type error.
@@ -1095,8 +1090,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1124,17 +1118,17 @@
             if (target instanceof AbsListView) {
                 AbsListView v = (AbsListView) target;
                 v.setRemoteViewsAdapter(intent, isAsync);
-                v.setRemoteViewsInteractionHandler(handler);
+                v.setRemoteViewsInteractionHandler(params.handler);
             } else if (target instanceof AdapterViewAnimator) {
                 AdapterViewAnimator v = (AdapterViewAnimator) target;
                 v.setRemoteViewsAdapter(intent, isAsync);
-                v.setRemoteViewsOnClickHandler(handler);
+                v.setRemoteViewsOnClickHandler(params.handler);
             }
         }
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
             copy.isAsync = true;
             return copy;
@@ -1173,8 +1167,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1215,7 +1208,7 @@
                 target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
                 return;
             }
-            target.setOnClickListener(v -> mResponse.handleViewInteraction(v, handler));
+            target.setOnClickListener(v -> mResponse.handleViewInteraction(v, params.handler));
         }
 
         @Override
@@ -1253,8 +1246,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
             if (!(target instanceof CompoundButton)) {
@@ -1287,7 +1279,7 @@
             }
 
             OnCheckedChangeListener onCheckedChangeListener =
-                    (v, isChecked) -> mResponse.handleViewInteraction(v, handler);
+                    (v, isChecked) -> mResponse.handleViewInteraction(v, params.handler);
             button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener);
             button.setOnCheckedChangeListener(onCheckedChangeListener);
         }
@@ -1459,8 +1451,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1517,8 +1508,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1561,8 +1551,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
@@ -1675,12 +1664,12 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException {
             ReflectionAction ra = new ReflectionAction(viewId, methodName,
                     BaseReflectionAction.BITMAP,
                     bitmap);
-            ra.apply(root, rootParent, handler, colorResources);
+            ra.apply(root, rootParent, params);
         }
 
         @Override
@@ -1756,8 +1745,7 @@
         protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
 
         @Override
-        public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
@@ -1775,7 +1763,7 @@
 
         @Override
         public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             final View view = root.findViewById(viewId);
             if (view == null) return ACTION_NOOP;
 
@@ -2307,8 +2295,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             mRunnable.run();
         }
     }
@@ -2421,8 +2408,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final Context context = root.getContext();
             final ViewGroup target = root.findViewById(viewId);
 
@@ -2451,8 +2437,7 @@
                             target.removeViews(nextChild, recycledViewIndex - nextChild);
                         }
                         setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
-                        rvToApply.reapplyNestedViews(context, child, rootParent, handler,
-                                null /* size */, colorResources);
+                        rvToApply.reapplyNestedViews(context, child, rootParent, params);
                         return;
                     }
                     // If we cannot recycle the views, we still remove all views in between to
@@ -2463,8 +2448,7 @@
             // If we cannot recycle, insert the new view before the next recyclable child.
 
             // Inflate nested views and add as children
-            View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler,
-                    null /* size */, colorResources);
+            View nestedView = rvToApply.apply(context, target, rootParent, null /* size */, params);
             if (mStableId != NO_ID) {
                 setStableId(nestedView, mStableId);
             }
@@ -2477,7 +2461,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the current view.
             root.createTree();
@@ -2511,8 +2495,7 @@
                         setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
                         final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
                                 context,
-                                targetVg, null /* listener */, handler, null /* size */,
-                                colorResources,
+                                targetVg, null /* listener */, params, null /* size */,
                                 recycled.mRoot);
                         final ViewTree tree = reapplyTask.doInBackground();
                         if (tree == null) {
@@ -2521,8 +2504,7 @@
                         return new RuntimeAction() {
                             @Override
                             public void apply(View root, ViewGroup rootParent,
-                                    InteractionHandler handler, ColorResources colorResources)
-                                    throws ActionException {
+                                    ActionApplyParams params) throws ActionException {
                                 reapplyTask.onPostExecute(tree);
                                 if (recycledViewIndex > nextChild) {
                                     targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
@@ -2533,23 +2515,22 @@
                     // If the layout id is different, still remove the children as if we recycled
                     // the view, to insert at the same place.
                     target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
-                    return insertNewView(context, target, handler, colorResources,
+                    return insertNewView(context, target, params,
                             () -> targetVg.removeViews(nextChild,
                                     recycledViewIndex - nextChild + 1));
 
                 }
             }
             // If we cannot recycle, simply add the view at the same available slot.
-            return insertNewView(context, target, handler, colorResources, () -> {});
+            return insertNewView(context, target, params, () -> {});
         }
 
-        private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
-                ColorResources colorResources, Runnable finalizeAction) {
+        private Action insertNewView(Context context, ViewTree target,
+                ActionApplyParams params, Runnable finalizeAction) {
             ViewGroup targetVg = (ViewGroup) target.mRoot;
             int nextChild = getNextRecyclableChild(targetVg);
             final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
-                    null /* listener */, handler, null /* size */, colorResources,
-                    null /* result */);
+                    null /* listener */, params, null /* size */,  null /* result */);
             final ViewTree tree = task.doInBackground();
 
             if (tree == null) {
@@ -2569,8 +2550,7 @@
 
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                        ColorResources colorResources) throws ActionException {
+                public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
                     task.onPostExecute(tree);
                     finalizeAction.run();
                     targetVg.addView(task.mResult, insertIndex);
@@ -2627,8 +2607,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final ViewGroup target = root.findViewById(viewId);
 
             if (target == null) {
@@ -2652,7 +2631,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the current view.
             root.createTree();
@@ -2676,8 +2655,7 @@
             }
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                        ColorResources colorResources) throws ActionException {
+                public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
                     if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
                         for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
                             if (!hasStableId(targetVg.getChildAt(i))) {
@@ -2736,8 +2714,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
 
             if (target == null || target == root) {
@@ -2752,7 +2729,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the correct view.
             root.createTree();
@@ -2771,8 +2748,7 @@
             parent.mChildren.remove(target);
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                        ColorResources colorResources) throws ActionException {
+                public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
                     parentVg.removeView(target.mRoot);
                 }
             };
@@ -2851,8 +2827,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return;
             if (drawablesLoaded) {
@@ -2883,7 +2858,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler, ColorResources colorResources) {
+                ActionApplyParams params) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return ACTION_NOOP;
 
@@ -2961,8 +2936,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return;
             target.setTextSize(units, size);
@@ -3007,8 +2981,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
             target.setPadding(left, top, right, bottom);
@@ -3084,8 +3057,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) {
                 return;
@@ -3230,8 +3202,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -3266,8 +3237,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             // Let's traverse the viewtree and override all textColors!
             Stack<View> viewsToProcess = new Stack<>();
             viewsToProcess.add(root);
@@ -3317,8 +3287,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
             final View target = root.findViewById(mViewId);
             if (target == null) return;
 
@@ -3352,8 +3321,7 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources)
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
                 throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
@@ -3404,8 +3372,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -3483,8 +3451,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
-                ColorResources colorResources) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+                throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -5578,52 +5546,39 @@
     /** @hide */
     public View apply(@NonNull Context context, @NonNull ViewGroup parent,
             @Nullable InteractionHandler handler, @Nullable SizeF size) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        View result = inflateView(context, rvToApply, parent);
-        rvToApply.performApply(result, parent, handler, null);
-        return result;
+        return apply(context, parent, size, new ActionApplyParams()
+                .withInteractionHandler(handler));
     }
 
     /** @hide */
     public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
             @Nullable InteractionHandler handler, @StyleRes int applyThemeResId) {
-        return applyWithTheme(context, parent, handler, applyThemeResId, null);
-    }
-
-    /** @hide */
-    public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
-            @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
-            @Nullable SizeF size) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
-        rvToApply.performApply(result, parent, handler, null);
-        return result;
+        return apply(context, parent, null, new ActionApplyParams()
+                .withInteractionHandler(handler)
+                .withThemeResId(applyThemeResId));
     }
 
     /** @hide */
     public View apply(Context context, ViewGroup parent, InteractionHandler handler,
             @Nullable SizeF size, @Nullable ColorResources colorResources) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        View result = inflateView(context, rvToApply, parent, 0, colorResources);
-        rvToApply.performApply(result, parent, handler, colorResources);
-        return result;
+        return apply(context, parent, size, new ActionApplyParams()
+                .withInteractionHandler(handler)
+                .withColorResources(colorResources));
     }
 
-    private View applyNestedViews(Context context, ViewGroup directParent,
-            ViewGroup rootParent, InteractionHandler handler, SizeF size,
-            ColorResources colorResources) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        View result = inflateView(context, rvToApply, directParent, 0, colorResources);
-        rvToApply.performApply(result, rootParent, handler, colorResources);
-        return result;
+    /** @hide **/
+    public View apply(Context context, ViewGroup parent, @Nullable SizeF size,
+            ActionApplyParams params) {
+        return apply(context, parent, parent, size, params);
     }
 
-    private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
-        return inflateView(context, rv, parent, 0, null);
+    private View apply(Context context, ViewGroup directParent, ViewGroup rootParent,
+            @Nullable SizeF size, ActionApplyParams params) {
+        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+        View result = inflateView(context, rvToApply, directParent,
+                params.applyThemeResId, params.colorResources);
+        rvToApply.performApply(result, rootParent, params);
+        return result;
     }
 
     private View inflateView(Context context, RemoteViews rv, @Nullable ViewGroup parent,
@@ -5704,7 +5659,6 @@
         return applyAsync(context, parent, executor, listener, null /* handler */);
     }
 
-
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
@@ -5723,16 +5677,19 @@
     public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
+
+        ActionApplyParams params = new ActionApplyParams()
+                .withInteractionHandler(handler)
+                .withColorResources(colorResources)
+                .withExecutor(executor);
         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
-                handler, colorResources, null /* result */,
-                true /* topLevel */).startTaskOnExecutor(executor);
+                params, null /* result */, true /* topLevel */).startTaskOnExecutor(executor);
     }
 
     private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
-            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
-            ColorResources colorResources, View result) {
+            OnViewAppliedListener listener, ActionApplyParams params, SizeF size, View result) {
         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
-                handler, colorResources, result, false /* topLevel */);
+                params, result, false /* topLevel */);
     }
 
     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -5742,8 +5699,8 @@
         final ViewGroup mParent;
         final Context mContext;
         final OnViewAppliedListener mListener;
-        final InteractionHandler mHandler;
-        final ColorResources mColorResources;
+        final ActionApplyParams mApplyParams;
+
         /**
          * Whether the remote view is the top-level one (i.e. not within an action).
          *
@@ -5758,16 +5715,13 @@
 
         private AsyncApplyTask(
                 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
-                InteractionHandler handler, ColorResources colorResources,
-                View result, boolean topLevel) {
+                ActionApplyParams applyParams, View result, boolean topLevel) {
             mRV = rv;
             mParent = parent;
             mContext = context;
             mListener = listener;
-            mColorResources = colorResources;
-            mHandler = handler;
             mTopLevel = topLevel;
-
+            mApplyParams = applyParams;
             mResult = result;
         }
 
@@ -5776,17 +5730,18 @@
         protected ViewTree doInBackground(Void... params) {
             try {
                 if (mResult == null) {
-                    mResult = inflateView(mContext, mRV, mParent, 0, mColorResources);
+                    mResult = inflateView(mContext, mRV, mParent, 0, mApplyParams.colorResources);
                 }
 
                 mTree = new ViewTree(mResult);
+
                 if (mRV.mActions != null) {
                     int count = mRV.mActions.size();
                     mActions = new Action[count];
                     for (int i = 0; i < count && !isCancelled(); i++) {
                         // TODO: check if isCancelled in nested views.
-                        mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler,
-                                mColorResources);
+                        mActions[i] = mRV.mActions.get(i)
+                                .initActionAsync(mTree, mParent, mApplyParams);
                     }
                 } else {
                     mActions = null;
@@ -5808,10 +5763,13 @@
 
                 try {
                     if (mActions != null) {
-                        InteractionHandler handler = mHandler == null
-                                ? DEFAULT_INTERACTION_HANDLER : mHandler;
+
+                        ActionApplyParams applyParams = mApplyParams.clone();
+                        if (applyParams.handler == null) {
+                            applyParams.handler = DEFAULT_INTERACTION_HANDLER;
+                        }
                         for (Action a : mActions) {
-                            a.apply(viewTree.mRoot, mParent, handler, mColorResources);
+                            a.apply(viewTree.mRoot, mParent, applyParams);
                         }
                     }
                     // If the parent of the view is has is a root, resolve the recycling.
@@ -5859,18 +5817,43 @@
      * the {@link #apply(Context,ViewGroup)} call.
      */
     public void reapply(Context context, View v) {
-        reapply(context, v, null /* handler */);
+        reapply(context, v, null /* size */, new ActionApplyParams());
     }
 
     /** @hide */
     public void reapply(Context context, View v, InteractionHandler handler) {
-        reapply(context, v, handler, null /* size */, null /* colorResources */);
+        reapply(context, v, null /* size */,
+                new ActionApplyParams().withInteractionHandler(handler));
     }
 
     /** @hide */
     public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
-        reapply(context, v, handler, size, colorResources, true);
+        reapply(context, v, size, new ActionApplyParams()
+                .withInteractionHandler(handler).withColorResources(colorResources));
+    }
+
+    /** @hide */
+    public void reapply(Context context, View v, @Nullable SizeF size, ActionApplyParams params) {
+        reapply(context, v, (ViewGroup) v.getParent(), size, params, true);
+    }
+
+    private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
+            ActionApplyParams params) {
+        reapply(context, v, rootParent, null, params, false);
+    }
+
+    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+    // should set it to false.
+    private void reapply(Context context, View v, ViewGroup rootParent,
+            @Nullable SizeF size, ActionApplyParams params, boolean topLevel) {
+        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+        rvToApply.performApply(v, rootParent, params);
+
+        // If the parent of the view is has is a root, resolve the recycling.
+        if (topLevel && v instanceof ViewGroup) {
+            finalizeViewRecycling((ViewGroup) v);
+        }
     }
 
     /** @hide */
@@ -5922,27 +5905,6 @@
         return rvToApply;
     }
 
-    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
-    // should set it to false.
-    private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
-            ColorResources colorResources, boolean topLevel) {
-
-        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
-
-        rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
-
-        // If the parent of the view is has is a root, resolve the recycling.
-        if (topLevel && v instanceof ViewGroup) {
-            finalizeViewRecycling((ViewGroup) v);
-        }
-    }
-
-    private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
-            InteractionHandler handler, SizeF size, ColorResources colorResources) {
-        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
-        rvToApply.performApply(v, rootParent, handler, colorResources);
-    }
-
     /**
      * Applies all the actions to the provided view, moving as much of the task on the background
      * thread as possible.
@@ -5973,19 +5935,25 @@
             ColorResources colorResources) {
         RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
 
+        ActionApplyParams params = new ActionApplyParams()
+                .withColorResources(colorResources)
+                .withInteractionHandler(handler)
+                .withExecutor(executor);
+
         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
-                context, listener, handler, colorResources, v, true /* topLevel */)
+                context, listener, params, v, true /* topLevel */)
                 .startTaskOnExecutor(executor);
     }
 
-    private void performApply(View v, ViewGroup parent, InteractionHandler handler,
-            ColorResources colorResources) {
+    private void performApply(View v, ViewGroup parent, ActionApplyParams params) {
+        params = params.clone();
+        if (params.handler == null) {
+            params.handler = DEFAULT_INTERACTION_HANDLER;
+        }
         if (mActions != null) {
-            handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
             final int count = mActions.size();
             for (int i = 0; i < count; i++) {
-                Action a = mActions.get(i);
-                a.apply(v, parent, handler, colorResources);
+                mActions.get(i).apply(v, parent, params);
             }
         }
     }
@@ -6043,6 +6011,47 @@
     }
 
     /**
+     * Utility class to hold all the options when applying the remote views
+     * @hide
+     */
+    public class ActionApplyParams {
+
+        public InteractionHandler handler;
+        public ColorResources colorResources;
+        public Executor executor;
+        @StyleRes public int applyThemeResId;
+
+        @Override
+        public ActionApplyParams clone() {
+            return new ActionApplyParams()
+                    .withInteractionHandler(handler)
+                    .withColorResources(colorResources)
+                    .withExecutor(executor)
+                    .withThemeResId(applyThemeResId);
+        }
+
+        public ActionApplyParams withInteractionHandler(InteractionHandler handler) {
+            this.handler = handler;
+            return this;
+        }
+
+        public ActionApplyParams withColorResources(ColorResources colorResources) {
+            this.colorResources = colorResources;
+            return this;
+        }
+
+        public ActionApplyParams withThemeResId(@StyleRes int themeResId) {
+            this.applyThemeResId = themeResId;
+            return this;
+        }
+
+        public ActionApplyParams withExecutor(Executor executor) {
+            this.executor = executor;
+            return this;
+        }
+    }
+
+    /**
      * Object allowing the modification of a context to overload the system's dynamic colors.
      *
      * Only colors from {@link android.R.color#system_accent1_0} to
@@ -6056,10 +6065,12 @@
         // Size, in bytes, of an entry in the array of colors in an ARSC file.
         private static final int ARSC_ENTRY_SIZE = 16;
 
-        private ResourcesLoader mLoader;
+        private final ResourcesLoader mLoader;
+        private final SparseIntArray mColorMapping;
 
-        private ColorResources(ResourcesLoader loader) {
+        private ColorResources(ResourcesLoader loader, SparseIntArray colorMapping) {
             mLoader = loader;
+            mColorMapping = colorMapping;
         }
 
         /**
@@ -6071,6 +6082,10 @@
             context.getResources().addLoaders(mLoader);
         }
 
+        public SparseIntArray getColorMapping() {
+            return mColorMapping;
+        }
+
         private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
             ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
             byte[] buffer = new byte[4096];
@@ -6145,7 +6160,7 @@
                             ResourcesLoader colorsLoader = new ResourcesLoader();
                             colorsLoader.addProvider(ResourcesProvider
                                     .loadFromTable(pfd, null /* assetsProvider */));
-                            return new ColorResources(colorsLoader);
+                            return new ColorResources(colorsLoader, colorMapping.clone());
                         }
                     }
                 } finally {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 9b583be..00b0105 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -339,6 +339,8 @@
     // A map from package name of vendor APEXes that can be updated to an installer package name
     // allowed to install updates for it.
     private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>();
+    // A set of package names that are allowed to use <install-constraints> manifest tag.
+    private final Set<String> mInstallConstraintsAllowlist = new ArraySet<>();
 
     private String mModulesInstallerPackageName;
 
@@ -535,6 +537,10 @@
         return mAllowedVendorApexes;
     }
 
+    public Set<String> getInstallConstraintsAllowlist() {
+        return mInstallConstraintsAllowlist;
+    }
+
     public String getModulesInstallerPackageName() {
         return mModulesInstallerPackageName;
     }
@@ -1455,6 +1461,20 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "install-constraints-allowed": {
+                        if (allowAppConfigs) {
+                            String packageName = parser.getAttributeValue(null, "package");
+                            if (packageName == null) {
+                                Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            } else {
+                                mInstallConstraintsAllowlist.add(packageName);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     default: {
                         Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser.getPositionDescription());
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 7562b9a..a62f6ad 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3602,4 +3602,19 @@
              false, the application cannot be profiled at all. Defaults to true. -->
         <attr name="enabled" format="boolean" />
     </declare-styleable>
+
+    <!-- <code>install-constraints</code> tag rejects installs unless one the constraints defined by
+         its child elements is true.
+         It is possible to have multiple <code>install-constraints</code> tags in a single manifest,
+         where each tag is evaluated independently.
+         @hide -->
+    <declare-styleable name="AndroidManifestInstallConstraints" parent="AndroidManifest" />
+
+    <!-- A constraint for <code>install-constraints</code>. Checks that the device fingerprint
+         starts with the given prefix.
+         @hide -->
+    <declare-styleable name="AndroidManifestInstallConstraintsFingerprintPrefix"
+                       parent="AndroidManifestInstallConstraints">
+        <attr name="value" />
+    </declare-styleable>
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 31fc6a5..2c02006 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -452,6 +452,7 @@
      */
     void setEntry(@NonNull final BubbleEntry entry) {
         Objects.requireNonNull(entry);
+        boolean showingDotPreviously = showDot();
         mLastUpdated = entry.getStatusBarNotification().getPostTime();
         mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
         mPackageName = entry.getStatusBarNotification().getPackageName();
@@ -498,6 +499,10 @@
         mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
         mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
         mShouldSuppressPeek = entry.shouldSuppressPeek();
+        if (showingDotPreviously != showDot()) {
+            // This will update the UI if needed
+            setShowDot(showDot());
+        }
     }
 
     @Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 3982616..8771ceb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1055,18 +1055,28 @@
     public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) {
         // If this is an interruptive notif, mark that it's interrupted
         mSysuiProxy.setNotificationInterruption(notif.getKey());
-        if (!notif.getRanking().isTextChanged()
+        boolean isNonInterruptiveNotExpanding = !notif.getRanking().isTextChanged()
                 && (notif.getBubbleMetadata() != null
-                && !notif.getBubbleMetadata().getAutoExpandBubble())
+                && !notif.getBubbleMetadata().getAutoExpandBubble());
+        if (isNonInterruptiveNotExpanding
                 && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
             // Update the bubble but don't promote it out of overflow
             Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
-            b.setEntry(notif);
+            if (notif.isBubble()) {
+                notif.setFlagBubble(false);
+            }
+            updateNotNotifyingEntry(b, notif, showInShade);
+        } else if (mBubbleData.hasAnyBubbleWithKey(notif.getKey())
+                && isNonInterruptiveNotExpanding) {
+            Bubble b = mBubbleData.getAnyBubbleWithkey(notif.getKey());
+            if (b != null) {
+                updateNotNotifyingEntry(b, notif, showInShade);
+            }
         } else if (mBubbleData.isSuppressedWithLocusId(notif.getLocusId())) {
             // Update the bubble but don't promote it out of overflow
             Bubble b = mBubbleData.getSuppressedBubbleWithKey(notif.getKey());
             if (b != null) {
-                b.setEntry(notif);
+                updateNotNotifyingEntry(b, notif, showInShade);
             }
         } else {
             Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
@@ -1076,13 +1086,25 @@
                 if (bubble.shouldAutoExpand()) {
                     bubble.setShouldAutoExpand(false);
                 }
+                mImpl.mCachedState.updateBubbleSuppressedState(bubble);
             } else {
                 inflateAndAdd(bubble, suppressFlyout, showInShade);
             }
         }
     }
 
-    void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
+    void updateNotNotifyingEntry(Bubble b, BubbleEntry entry, boolean showInShade) {
+        boolean isBubbleSelected = Objects.equals(b, mBubbleData.getSelectedBubble());
+        boolean isBubbleExpandedAndSelected = isStackExpanded() && isBubbleSelected;
+        b.setEntry(entry);
+        boolean suppress = isBubbleExpandedAndSelected || !showInShade || !b.showInShade();
+        b.setSuppressNotification(suppress);
+        b.setShowDot(!isBubbleExpandedAndSelected);
+        mImpl.mCachedState.updateBubbleSuppressedState(b);
+    }
+
+    @VisibleForTesting
+    public void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
         // Lazy init stack view when a bubble is created
         ensureStackViewCreated();
         bubble.setInflateSynchronously(mInflateSynchronously);
@@ -1111,7 +1133,10 @@
     }
 
     @VisibleForTesting
-    public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+    public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
+        if (!fromSystem) {
+            return;
+        }
         // shouldBubbleUp checks canBubble & for bubble metadata
         boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
         if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -1172,9 +1197,9 @@
                 // notification, so that the bubble will be re-created if shouldBubbleUp returns
                 // true.
                 mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
-            } else if (entry != null && mTmpRanking.isBubble() && !isActive) {
+            } else if (entry != null && mTmpRanking.isBubble() && !isActiveOrInOverflow) {
                 entry.setFlagBubble(true);
-                onEntryUpdated(entry, shouldBubbleUp);
+                onEntryUpdated(entry, shouldBubbleUp, /* fromSystem= */ true);
             }
         }
     }
@@ -1773,9 +1798,9 @@
         }
 
         @Override
-        public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+        public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
             mMainExecutor.execute(() -> {
-                BubbleController.this.onEntryUpdated(entry, shouldBubbleUp);
+                BubbleController.this.onEntryUpdated(entry, shouldBubbleUp, fromSystem);
             });
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index cf792cd..37b96ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -171,8 +171,9 @@
      *
      * @param entry the {@link BubbleEntry} by the notification.
      * @param shouldBubbleUp {@code true} if this notification should bubble up.
+     * @param fromSystem {@code true} if this update is from NotificationManagerService.
      */
-    void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp);
+    void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
 
     /**
      * Called when new notification entry removed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index de7e7bd..a372acb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -21,6 +21,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -90,7 +91,6 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.Executor;
@@ -337,17 +337,39 @@
     }
 
     public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+        final int[] result = new int[1];
+        IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+            @Override
+            public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                    RemoteAnimationTarget[] apps,
+                    RemoteAnimationTarget[] wallpapers,
+                    RemoteAnimationTarget[] nonApps,
+                    final IRemoteAnimationFinishedCallback finishedCallback) {
+                try {
+                    finishedCallback.onAnimationFinished();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
+                }
+                if (result[0] == START_SUCCESS || result[0] == START_TASK_TO_FRONT) {
+                    final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+                    mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
+                    mSyncQueue.queue(evictWct);
+                }
+            }
+            @Override
+            public void onAnimationCancelled(boolean isKeyguardOccluded) {
+            }
+        };
         options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
                 null /* wct */);
+        RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
+                0 /* duration */, 0 /* statusBarTransitionDelay */);
+        ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+        activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
 
         try {
-            final WindowContainerTransaction evictWct = new WindowContainerTransaction();
-            mStageCoordinator.prepareEvictChildTasks(position, evictWct);
-            final int result =
-                    ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
-            if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
-                mSyncQueue.queue(evictWct);
-            }
+            result[0] = ActivityTaskManager.getService().startActivityFromRecents(taskId,
+                    activityOptions.toBundle());
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to launch task", e);
         }
@@ -403,7 +425,7 @@
 
         // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
         // split.
-        if (isLaunchingAdjacently(intent.getIntent(), position)) {
+        if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
             fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
         }
@@ -418,8 +440,7 @@
 
     /** Returns {@code true} if it's launching the same component on both sides of the split. */
     @VisibleForTesting
-    boolean isLaunchingAdjacently(@Nullable Intent startIntent,
-            @SplitPosition int position) {
+    boolean shouldAddMultipleTaskFlag(@Nullable Intent startIntent, @SplitPosition int position) {
         if (startIntent == null) {
             return false;
         }
@@ -430,6 +451,16 @@
         }
 
         if (isSplitScreenVisible()) {
+            // To prevent users from constantly dropping the same app to the same side resulting in
+            // a large number of instances in the background.
+            final ActivityManager.RunningTaskInfo targetTaskInfo = getTaskInfo(position);
+            final ComponentName targetActivity = targetTaskInfo != null
+                    ? targetTaskInfo.baseIntent.getComponent() : null;
+            if (Objects.equals(launchingActivity, targetActivity)) {
+                return false;
+            }
+
+            // Allow users to start a new instance the same to adjacent side.
             final ActivityManager.RunningTaskInfo pairedTaskInfo =
                     getTaskInfo(SplitLayout.reversePosition(position));
             final ComponentName pairedActivity = pairedTaskInfo != null
@@ -453,12 +484,12 @@
             mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
             mSyncQueue.queue(wct);
         }
-        return reparentSplitTasksForAnimation(apps, true /*splitExpectedToBeVisible*/);
+        return reparentSplitTasksForAnimation(apps, false /* enterSplitScreen */);
     }
 
     RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
         try {
-            return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/);
+            return reparentSplitTasksForAnimation(apps, true /* enterSplitScreen */);
         } finally {
             for (RemoteAnimationTarget appTarget : apps) {
                 if (appTarget.leash != null) {
@@ -469,14 +500,23 @@
     }
 
     private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
-            boolean splitExpectedToBeVisible) {
+            boolean enterSplitScreen) {
         if (ENABLE_SHELL_TRANSITIONS) return null;
-        // TODO(b/206487881): Integrate this with shell transition.
-        if (splitExpectedToBeVisible && !isSplitScreenVisible()) return null;
-        // Split not visible, but not enough apps to have split, also return null
-        if (!splitExpectedToBeVisible && apps.length < 2) return null;
 
-        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        if (enterSplitScreen) {
+            int openingApps = 0;
+            for (int i = 0; i < apps.length; ++i) {
+                if (apps[i].mode == MODE_OPENING) openingApps++;
+            }
+            if (openingApps < 2) {
+                // Not having enough apps to enter split screen
+                return null;
+            }
+        } else if (!isSplitScreenVisible()) {
+            return null;
+        }
+
+        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
         if (mSplitTasksContainerLayer != null) {
             // Remove the previous layer before recreating
             transaction.remove(mSplitTasksContainerLayer);
@@ -489,17 +529,14 @@
         mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
         mSplitTasksContainerLayer = builder.build();
 
-        // Ensure that we order these in the parent in the right z-order as their previous order
-        Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
-        int layer = 1;
-        for (RemoteAnimationTarget appTarget : apps) {
+        for (int i = 0; i < apps.length; ++i) {
+            final RemoteAnimationTarget appTarget = apps[i];
             transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
             transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
                     appTarget.screenSpaceBounds.top);
-            transaction.setLayer(appTarget.leash, layer++);
         }
         transaction.apply();
-        transaction.close();
+        mTransactionPool.release(transaction);
         return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
     }
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 80ef74e..8e1ae39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -452,10 +452,16 @@
                     IRemoteAnimationFinishedCallback finishedCallback,
                     SurfaceControl.Transaction t) {
                 if (apps == null || apps.length == 0) {
-                    // Switch the split position if launching as MULTIPLE_TASK failed.
-                    if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
-                        setSideStagePosition(SplitLayout.reversePosition(
-                                getSideStagePosition()), null);
+                    if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+                        mMainExecutor.execute(() ->
+                                exitSplitScreen(mMainStage.getChildCount() == 0
+                                        ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+                    } else {
+                        // Switch the split position if launching as MULTIPLE_TASK failed.
+                        if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+                            setSideStagePosition(SplitLayout.reversePosition(
+                                    getSideStagePosition()), null);
+                        }
                     }
 
                     // Do nothing when the animation was cancelled.
@@ -651,7 +657,7 @@
         mShouldUpdateRecents = true;
         // If any stage has no child after animation finished, it means that split will display
         // nothing, such status will happen if task and intent is same app but not support
-        // multi-instagce, we should exit split and expand that app as full screen.
+        // multi-instance, we should exit split and expand that app as full screen.
         if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
             mMainExecutor.execute(() ->
                     exitSplitScreen(mMainStage.getChildCount() == 0
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index f414d69..1af9415 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -33,6 +33,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -376,7 +377,13 @@
             SurfaceControl leash, boolean firstAppeared) {
         final Point taskPositionInParent = taskInfo.positionInParent;
         mSyncQueue.runInSync(t -> {
-            t.setWindowCrop(leash, null);
+            // The task surface might be released before running in the sync queue for the case like
+            // trampoline launch, so check if the surface is valid before processing it.
+            if (!leash.isValid()) {
+                Slog.w(TAG, "Skip updating invalid child task surface of task#" + taskInfo.taskId);
+                return;
+            }
+            t.setCrop(leash, null);
             t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
             if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) {
                 t.setAlpha(leash, 1f);
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 087304b..506a4c0 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
@@ -233,7 +233,9 @@
         mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
         startT.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(), taskBounds.height())
                 .setShadowRadius(mTaskBackgroundSurface, shadowRadius)
-                .setColor(mTaskBackgroundSurface, mTmpColor);
+                .setColor(mTaskBackgroundSurface, mTmpColor)
+                .setLayer(mTaskBackgroundSurface, -1)
+                .show(mTaskBackgroundSurface);
 
         // Caption view
         mCaptionWindowManager.setConfiguration(taskConfig);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 10788f9..c7c78d3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 
 import static org.junit.Assert.assertFalse;
@@ -111,7 +112,7 @@
     }
 
     @Test
-    public void testIsLaunchingAdjacently_notInSplitScreen() {
+    public void testShouldAddMultipleTaskFlag_notInSplitScreen() {
         doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
         doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
 
@@ -120,7 +121,7 @@
         ActivityManager.RunningTaskInfo focusTaskInfo =
                 createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
         doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
-        assertTrue(mSplitScreenController.isLaunchingAdjacently(
+        assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
                 startIntent, SPLIT_POSITION_TOP_OR_LEFT));
 
         // Verify launching different activity returns false.
@@ -128,28 +129,40 @@
         focusTaskInfo =
                 createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
         doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
-        assertFalse(mSplitScreenController.isLaunchingAdjacently(
+        assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
                 startIntent, SPLIT_POSITION_TOP_OR_LEFT));
     }
 
     @Test
-    public void testIsLaunchingAdjacently_inSplitScreen() {
+    public void testShouldAddMultipleTaskFlag_inSplitScreen() {
         doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
-
-        // Verify launching the same activity returns true.
         Intent startIntent = createStartIntent("startActivity");
-        ActivityManager.RunningTaskInfo pairingTaskInfo =
+        ActivityManager.RunningTaskInfo sameTaskInfo =
                 createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
-        doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
-        assertTrue(mSplitScreenController.isLaunchingAdjacently(
+        Intent diffIntent = createStartIntent("diffActivity");
+        ActivityManager.RunningTaskInfo differentTaskInfo =
+                createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
+
+        // Verify launching the same activity return false.
+        doReturn(sameTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+        assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
                 startIntent, SPLIT_POSITION_TOP_OR_LEFT));
 
-        // Verify launching different activity returns false.
-        Intent diffIntent = createStartIntent("diffActivity");
-        pairingTaskInfo =
-                createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
-        doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
-        assertFalse(mSplitScreenController.isLaunchingAdjacently(
+        // Verify launching the same activity as adjacent returns true.
+        doReturn(differentTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+        doReturn(sameTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+        assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
+                startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+
+        // Verify launching different activity from adjacent returns false.
+        doReturn(differentTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+        doReturn(differentTaskInfo).when(mSplitScreenController)
+                .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+        assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
                 startIntent, SPLIT_POSITION_TOP_OR_LEFT));
     }
 
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 1e7d5fe..226843e 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
@@ -204,6 +204,8 @@
         verify(mMockSurfaceControlStartT)
                 .setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
         verify(mMockSurfaceControlStartT).setShadowRadius(taskBackgroundSurface, 10);
+        verify(mMockSurfaceControlStartT).setLayer(taskBackgroundSurface, -1);
+        verify(mMockSurfaceControlStartT).show(taskBackgroundSurface);
 
         verify(mMockSurfaceControlViewHostFactory)
                 .create(any(), eq(defaultDisplay), any(), anyBoolean());
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 322e1be..5795fd4 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1610,6 +1610,9 @@
     <string name="dream_complication_title_cast_info">Cast Info</string>
     <!-- Screensaver overlay which displays home controls. [CHAR LIMIT=20] -->
     <string name="dream_complication_title_home_controls">Home Controls</string>
+    <!-- Screensaver overlay which displays smartspace. [CHAR LIMIT=20] -->
+    <string name="dream_complication_title_smartspace">Smartspace</string>
+
 
     <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
     <string name="avatar_picker_title">Choose a profile picture</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index a46e232..2258617 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -92,7 +92,8 @@
             COMPLICATION_TYPE_WEATHER,
             COMPLICATION_TYPE_AIR_QUALITY,
             COMPLICATION_TYPE_CAST_INFO,
-            COMPLICATION_TYPE_HOME_CONTROLS
+            COMPLICATION_TYPE_HOME_CONTROLS,
+            COMPLICATION_TYPE_SMARTSPACE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ComplicationType {}
@@ -103,6 +104,7 @@
     public static final int COMPLICATION_TYPE_AIR_QUALITY = 4;
     public static final int COMPLICATION_TYPE_CAST_INFO = 5;
     public static final int COMPLICATION_TYPE_HOME_CONTROLS = 6;
+    public static final int COMPLICATION_TYPE_SMARTSPACE = 7;
 
     private final Context mContext;
     private final IDreamManager mDreamManager;
@@ -351,6 +353,9 @@
             case COMPLICATION_TYPE_HOME_CONTROLS:
                 res = R.string.dream_complication_title_home_controls;
                 break;
+            case COMPLICATION_TYPE_SMARTSPACE:
+                res = R.string.dream_complication_title_smartspace;
+                break;
             default:
                 return null;
         }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 2f36ab9..8f9ced6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -23,7 +23,6 @@
 import android.graphics.Color
 import android.graphics.Rect
 import android.os.Looper
-import android.service.dreams.IDreamManager
 import android.util.Log
 import android.util.MathUtils
 import android.view.GhostView
@@ -54,7 +53,7 @@
 class DialogLaunchAnimator
 @JvmOverloads
 constructor(
-    private val dreamManager: IDreamManager,
+    private val callback: Callback,
     private val interactionJankMonitor: InteractionJankMonitor,
     private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
     private val isForTesting: Boolean = false
@@ -126,7 +125,7 @@
         val animatedDialog =
             AnimatedDialog(
                 launchAnimator,
-                dreamManager,
+                callback,
                 interactionJankMonitor,
                 animateFrom,
                 onDialogDismissed = { openedDialogs.remove(it) },
@@ -194,8 +193,12 @@
 
         val dialog = animatedDialog.dialog
 
-        // Don't animate if the dialog is not showing.
-        if (!dialog.isShowing) {
+        // Don't animate if the dialog is not showing or if we are locked and going to show the
+        // bouncer.
+        if (
+            !dialog.isShowing ||
+            (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock())
+        ) {
             return null
         }
 
@@ -285,6 +288,23 @@
             ?.let { it.touchSurface = it.prepareForStackDismiss() }
         dialog.dismiss()
     }
+
+    interface Callback {
+        /** Whether the device is currently in dreaming (screensaver) mode. */
+        fun isDreaming(): Boolean
+
+        /**
+         * Whether the device is currently unlocked, i.e. if it is *not* on the keyguard or if the
+         * keyguard can be dismissed.
+         */
+        fun isUnlocked(): Boolean
+
+        /**
+         * Whether we are going to show alternate authentication (like UDFPS) instead of the
+         * traditional bouncer when unlocking the device.
+         */
+        fun isShowingAlternateAuthOnUnlock(): Boolean
+    }
 }
 
 /**
@@ -296,7 +316,7 @@
 
 private class AnimatedDialog(
     private val launchAnimator: LaunchAnimator,
-    private val dreamManager: IDreamManager,
+    private val callback: DialogLaunchAnimator.Callback,
     private val interactionJankMonitor: InteractionJankMonitor,
 
     /** The view that triggered the dialog after being tapped. */
@@ -850,7 +870,7 @@
 
         // If we are dreaming, the dialog was probably closed because of that so we don't animate
         // into the touchSurface.
-        if (dreamManager.isDreaming) {
+        if (callback.isDreaming()) {
             return false
         }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f07d9ce..98946ac 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -214,7 +214,8 @@
      * If no cancel signal has been received after this amount of time, set the biometric running
      * state to stopped to allow Keyguard to retry authentication.
      */
-    private static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
+    @VisibleForTesting
+    protected static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
 
     private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
             "com.android.settings", "com.android.settings.FallbackHome");
@@ -332,10 +333,15 @@
     private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
     private static final int HAL_ERROR_RETRY_MAX = 20;
 
-    private final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
+    @VisibleForTesting
+    protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
 
     private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived;
 
+    @VisibleForTesting
+    protected Handler getHandler() {
+        return mHandler;
+    }
     private final Handler mHandler;
 
     private SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray();
@@ -723,6 +729,11 @@
 
     private void handleFingerprintAuthFailed() {
         Assert.isMainThread();
+        if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+            Log.d(TAG, "handleFingerprintAuthFailed()"
+                    + " triggered while waiting for cancellation, removing watchdog");
+            mHandler.removeCallbacks(mFpCancelNotReceived);
+        }
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -753,6 +764,11 @@
 
     private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
         Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
+        if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+            Log.d(TAG, "handleFingerprintAuthenticated()"
+                    + " triggered while waiting for cancellation, removing watchdog");
+            mHandler.removeCallbacks(mFpCancelNotReceived);
+        }
         try {
             final int userId;
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index e16ac08..c21e36a 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -97,6 +97,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
@@ -132,7 +133,7 @@
     private static final int FONT_SEARCH_STEP_PX = 4;
 
     private final Context mContext;
-    private final UiEventLogger mUiEventLogger;
+    private final ClipboardLogger mClipboardLogger;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final DisplayManager mDisplayManager;
     private final DisplayMetrics mDisplayMetrics;
@@ -181,7 +182,7 @@
         final Context displayContext = context.createDisplayContext(getDefaultDisplay());
         mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
 
-        mUiEventLogger = uiEventLogger;
+        mClipboardLogger = new ClipboardLogger(uiEventLogger);
 
         mAccessibilityManager = AccessibilityManager.getInstance(mContext);
         mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
@@ -231,7 +232,7 @@
 
             @Override
             public void onSwipeDismissInitiated(Animator animator) {
-                mUiEventLogger.log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
+                mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
                 mExitAnimator = animator;
             }
 
@@ -249,7 +250,7 @@
         });
 
         mDismissButton.setOnClickListener(view -> {
-            mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
+            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
             animateOut();
         });
 
@@ -285,7 +286,8 @@
                                 int newDisplayId) {
                             if (mContext.getResources().getConfiguration().orientation
                                     != mOrientation) {
-                                mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+                                mClipboardLogger.logSessionComplete(
+                                        CLIPBOARD_OVERLAY_DISMISSED_OTHER);
                                 hideImmediate();
                             }
                         }
@@ -300,7 +302,7 @@
         });
 
         mTimeoutHandler.setOnTimeoutRunnable(() -> {
-            mUiEventLogger.log(CLIPBOARD_OVERLAY_TIMED_OUT);
+            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT);
             animateOut();
         });
 
@@ -308,7 +310,7 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
-                    mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+                    mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
                     animateOut();
                 }
             }
@@ -320,7 +322,7 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (SCREENSHOT_ACTION.equals(intent.getAction())) {
-                    mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+                    mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
                     animateOut();
                 }
             }
@@ -390,7 +392,7 @@
                     mContext.getString(R.string.clipboard_send_nearby_description));
             mRemoteCopyChip.setVisibility(View.VISIBLE);
             mRemoteCopyChip.setOnClickListener((v) -> {
-                mUiEventLogger.log(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
+                mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
                 mContext.startActivity(remoteCopyIntent);
                 animateOut();
             });
@@ -450,7 +452,7 @@
         chip.setContentDescription(action.getTitle());
         chip.setIcon(action.getIcon(), false);
         chip.setPendingIntent(action.getActionIntent(), () -> {
-            mUiEventLogger.log(CLIPBOARD_OVERLAY_ACTION_TAPPED);
+            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
             animateOut();
         });
         chip.setAlpha(1);
@@ -486,7 +488,7 @@
                         touchRegion.op(tmpRect, Region.Op.UNION);
                         if (!touchRegion.contains(
                                 (int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
-                            mUiEventLogger.log(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
+                            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
                             animateOut();
                         }
                     }
@@ -497,7 +499,7 @@
     }
 
     private void editImage(Uri uri) {
-        mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
         String editorPackage = mContext.getString(R.string.config_screenshotEditor);
         Intent editIntent = new Intent(Intent.ACTION_EDIT);
         if (!TextUtils.isEmpty(editorPackage)) {
@@ -512,7 +514,7 @@
     }
 
     private void editText() {
-        mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
         Intent editIntent = new Intent(mContext, EditTextActivity.class);
         editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         mContext.startActivity(editIntent);
@@ -520,13 +522,15 @@
     }
 
     private void shareContent(ClipData clip) {
-        mUiEventLogger.log(CLIPBOARD_OVERLAY_SHARE_TAPPED);
+        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
         Intent shareIntent = new Intent(Intent.ACTION_SEND);
-        shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
         shareIntent.setDataAndType(
                 clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
-        shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
-        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
+        if (clip.getItemAt(0).getUri() != null) {
+            shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
+            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        }
         Intent chooserIntent = Intent.createChooser(shareIntent, null)
                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
                 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -864,6 +868,7 @@
         mRemoteCopyChip.setVisibility(View.GONE);
         resetActionChips();
         mTimeoutHandler.cancelTimeout();
+        mClipboardLogger.reset();
     }
 
     @MainThread
@@ -969,4 +974,24 @@
             mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
         }
     }
+
+    static class ClipboardLogger {
+        private final UiEventLogger mUiEventLogger;
+        private boolean mGuarded = false;
+
+        ClipboardLogger(UiEventLogger uiEventLogger) {
+            mUiEventLogger = uiEventLogger;
+        }
+
+        void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) {
+            if (!mGuarded) {
+                mGuarded = true;
+                mUiEventLogger.log(event);
+            }
+        }
+
+        void reset() {
+            mGuarded = false;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index de7bf28..55c1806 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -215,16 +215,15 @@
         final boolean cameraBlocked = mSensorPrivacyController
                 .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
         @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
-        if (micBlocked && cameraBlocked) {
-            iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED;
-        } else if (!micBlocked && cameraBlocked) {
-            iconType = DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED;
-        } else if (micBlocked && !cameraBlocked) {
-            iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED;
-        }
-        if (iconType != Resources.ID_NULL) {
-            showIcon(iconType, true);
-        }
+        showIcon(
+                DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
+                !micBlocked && cameraBlocked);
+        showIcon(
+                DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
+                micBlocked && !cameraBlocked);
+        showIcon(
+                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+                micBlocked && cameraBlocked);
     }
 
     private String buildNotificationsContentDescription(int notificationCount) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
index 5457144..29bb2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
@@ -163,7 +163,8 @@
             COMPLICATION_TYPE_WEATHER,
             COMPLICATION_TYPE_AIR_QUALITY,
             COMPLICATION_TYPE_CAST_INFO,
-            COMPLICATION_TYPE_HOME_CONTROLS
+            COMPLICATION_TYPE_HOME_CONTROLS,
+            COMPLICATION_TYPE_SMARTSPACE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface ComplicationType {}
@@ -175,6 +176,7 @@
     int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
     int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
     int COMPLICATION_TYPE_HOME_CONTROLS = 1 << 5;
+    int COMPLICATION_TYPE_SMARTSPACE = 1 << 6;
 
     /**
      * The {@link Host} interface specifies a way a {@link Complication} to communicate with its
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
index dcab90f..d5db63d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -21,6 +21,7 @@
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_NONE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
 
@@ -51,6 +52,8 @@
                 return COMPLICATION_TYPE_CAST_INFO;
             case DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS:
                 return COMPLICATION_TYPE_HOME_CONTROLS;
+            case DreamBackend.COMPLICATION_TYPE_SMARTSPACE:
+                return COMPLICATION_TYPE_SMARTSPACE;
             default:
                 return COMPLICATION_TYPE_NONE;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index ac6edba..567bdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -52,6 +52,11 @@
         return mViewHolderProvider.get();
     }
 
+    @Override
+    public int getRequiredTypeAvailability() {
+        return COMPLICATION_TYPE_SMARTSPACE;
+    }
+
     /**
      * {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
      * SystemUI.
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 0dc07ac..d9e75c4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -207,6 +207,14 @@
     public static final SysPropBooleanFlag HIDE_NAVBAR_WINDOW =
             new SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false);
 
+    @Keep
+    public static final SysPropBooleanFlag WM_DESKTOP_WINDOWING =
+            new SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false);
+
+    @Keep
+    public static final SysPropBooleanFlag WM_CAPTION_ON_SHELL =
+            new SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false);
+
     // 1200 - predictive back
     @Keep
     public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index dc23684d..6124e10 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -22,17 +22,11 @@
 import com.android.systemui.util.collection.RingBuffer
 import com.google.errorprone.annotations.CompileTimeConstant
 import java.io.PrintWriter
-import java.text.SimpleDateFormat
-import java.util.Arrays.stream
-import java.util.Locale
 import java.util.concurrent.ArrayBlockingQueue
 import java.util.concurrent.BlockingQueue
 import kotlin.concurrent.thread
 import kotlin.math.max
 
-const val UNBOUNDED_STACK_TRACE = -1
-const val NESTED_TRACE_DEPTH = 10
-
 /**
  * A simple ring buffer of recyclable log messages
  *
@@ -74,18 +68,12 @@
  * @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start
  * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches
  * the maximum, it behaves like a ring buffer.
- * @param rootStackTraceDepth The number of stack trace elements to be logged for an exception when
- * the logBuffer is dumped. Defaulted to -1 [UNBOUNDED_STACK_TRACE] to print the entire stack trace.
- * @param nestedStackTraceDepth The number of stack trace elements to be logged for any nested
- * exceptions present in [Throwable.cause] or [Throwable.suppressedExceptions].
  */
 class LogBuffer @JvmOverloads constructor(
     private val name: String,
     private val maxSize: Int,
     private val logcatEchoTracker: LogcatEchoTracker,
     private val systrace: Boolean = true,
-    private val rootStackTraceDepth: Int = UNBOUNDED_STACK_TRACE,
-    private val nestedStackTraceDepth: Int = NESTED_TRACE_DEPTH,
 ) {
     private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
 
@@ -236,7 +224,7 @@
         val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) }
 
         for (i in iterationStart until buffer.size) {
-            dumpMessage(buffer[i], pw)
+            buffer[i].dump(pw)
         }
     }
 
@@ -264,76 +252,6 @@
         }
     }
 
-    private fun dumpMessage(
-        message: LogMessage,
-        pw: PrintWriter
-    ) {
-        val formattedTimestamp = DATE_FORMAT.format(message.timestamp)
-        val shortLevel = message.level.shortString
-        val messageToPrint = message.messagePrinter(message)
-        val tag = message.tag
-        printLikeLogcat(pw, formattedTimestamp, shortLevel, tag, messageToPrint)
-        message.exception?.let { ex ->
-            printException(
-                pw,
-                formattedTimestamp,
-                shortLevel,
-                ex,
-                tag,
-                stackTraceDepth = rootStackTraceDepth)
-        }
-    }
-
-    private fun printException(
-            pw: PrintWriter,
-            timestamp: String,
-            level: String,
-            exception: Throwable,
-            tag: String,
-            exceptionMessagePrefix: String = "",
-            stackTraceDepth: Int = UNBOUNDED_STACK_TRACE
-    ) {
-        val message = "$exceptionMessagePrefix$exception"
-        printLikeLogcat(pw, timestamp, level, tag, message)
-        var stacktraceStream = stream(exception.stackTrace)
-        if (stackTraceDepth != UNBOUNDED_STACK_TRACE) {
-            stacktraceStream = stacktraceStream.limit(stackTraceDepth.toLong())
-        }
-        stacktraceStream.forEach { line ->
-            printLikeLogcat(pw, timestamp, level, tag, "\tat $line")
-        }
-        exception.cause?.let { cause ->
-            printException(pw, timestamp, level, cause, tag, "Caused by: ", nestedStackTraceDepth)
-        }
-        exception.suppressedExceptions.forEach { suppressed ->
-            printException(
-                pw,
-                timestamp,
-                level,
-                suppressed,
-                tag,
-                "Suppressed: ",
-                nestedStackTraceDepth
-            )
-        }
-    }
-
-    private fun printLikeLogcat(
-        pw: PrintWriter,
-        formattedTimestamp: String,
-        shortLogLevel: String,
-        tag: String,
-        message: String
-    ) {
-        pw.print(formattedTimestamp)
-        pw.print(" ")
-        pw.print(shortLogLevel)
-        pw.print(" ")
-        pw.print(tag)
-        pw.print(": ")
-        pw.println(message)
-    }
-
     private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) {
         if (toLogcat || toSystrace) {
             val strMessage = message.messagePrinter(message)
@@ -370,5 +288,4 @@
 typealias MessageInitializer = LogMessage.() -> Unit
 
 private const val TAG = "LogBuffer"
-private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
 private val FROZEN_MESSAGE = LogMessageImpl.create()
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
index 987aea8..dae2592 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
@@ -16,6 +16,10 @@
 
 package com.android.systemui.log
 
+import java.io.PrintWriter
+import java.text.SimpleDateFormat
+import java.util.Locale
+
 /**
  * Generic data class for storing messages logged to a [LogBuffer]
  *
@@ -50,6 +54,17 @@
     var bool2: Boolean
     var bool3: Boolean
     var bool4: Boolean
+
+    /**
+     * Function that dumps the [LogMessage] to the provided [writer].
+     */
+    fun dump(writer: PrintWriter) {
+        val formattedTimestamp = DATE_FORMAT.format(timestamp)
+        val shortLevel = level.shortString
+        val messageToPrint = messagePrinter(this)
+        printLikeLogcat(writer, formattedTimestamp, shortLevel, tag, messageToPrint)
+        exception?.printStackTrace(writer)
+    }
 }
 
 /**
@@ -61,3 +76,21 @@
  * of the printer for each call, thwarting our attempts at avoiding any sort of allocation.
  */
 typealias MessagePrinter = LogMessage.() -> String
+
+private fun printLikeLogcat(
+    pw: PrintWriter,
+    formattedTimestamp: String,
+    shortLogLevel: String,
+    tag: String,
+    message: String
+) {
+    pw.print(formattedTimestamp)
+    pw.print(" ")
+    pw.print(shortLogLevel)
+    pw.print(" ")
+    pw.print(tag)
+    pw.print(": ")
+    pw.println(message)
+}
+
+private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index e9b6af4..e360d10 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -263,6 +263,7 @@
         }
 
         private void onGroupActionTriggered(boolean isChecked, MediaDevice device) {
+            disableSeekBar();
             if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
                 mController.addDeviceToPlayMedia(device);
             } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index bec6739..3b4ca48 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -273,6 +273,8 @@
         void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
             if (!mController.isVolumeControlEnabled(device)) {
                 disableSeekBar();
+            } else {
+                enableSeekBar();
             }
             mSeekBar.setMaxVolume(device.getMaxVolume());
             final int currentVolume = device.getCurrentVolume();
@@ -417,11 +419,16 @@
             return drawable;
         }
 
-        private void disableSeekBar() {
+        protected void disableSeekBar() {
             mSeekBar.setEnabled(false);
             mSeekBar.setOnTouchListener((v, event) -> true);
         }
 
+        private void enableSeekBar() {
+            mSeekBar.setEnabled(true);
+            mSeekBar.setOnTouchListener((v, event) -> false);
+        }
+
         protected void setUpDeviceIcon(MediaDevice device) {
             ThreadUtils.postOnBackgroundThread(() -> {
                 Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 5d7af52..6fe06e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -194,6 +194,11 @@
     }
 
     private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) {
+        if (device == null) {
+            return isSourceDevice
+                    ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
+                    : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE;
+        }
         switch (device.getDeviceType()) {
             case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
                 return isSourceDevice
@@ -229,6 +234,9 @@
     }
 
     private int getInteractionDeviceType(MediaDevice device) {
+        if (device == null) {
+            return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE;
+        }
         switch (device.getDeviceType()) {
             case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
                 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BUILTIN_SPEAKER;
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 5f478ce..9ab83b8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -56,7 +56,7 @@
         internal val logger: MediaTttLogger,
         internal val windowManager: WindowManager,
         private val viewUtil: ViewUtil,
-        @Main private val mainExecutor: DelayableExecutor,
+        @Main internal val mainExecutor: DelayableExecutor,
         private val accessibilityManager: AccessibilityManager,
         private val configurationController: ConfigurationController,
         private val powerManager: PowerManager,
@@ -205,13 +205,15 @@
      *
      * @param appPackageName the package name of the app playing the media. Will be used to fetch
      *   the app icon and app name if overrides aren't provided.
+     *
+     * @return the content description of the icon.
      */
     internal fun setIcon(
         currentChipView: ViewGroup,
         appPackageName: String?,
         appIconDrawableOverride: Drawable? = null,
         appNameOverride: CharSequence? = null,
-    ) {
+    ): CharSequence {
         val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
         val iconInfo = getIconInfo(appPackageName)
 
@@ -224,6 +226,7 @@
 
         appIconView.contentDescription = appNameOverride ?: iconInfo.iconName
         appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon)
+        return appIconView.contentDescription.toString()
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 3ea11b8..b94b8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -122,13 +122,12 @@
         val chipState = newChipInfo.state
 
         // App icon
-        setIcon(currentChipView, newChipInfo.routeInfo.packageName)
+        val iconName = setIcon(currentChipView, newChipInfo.routeInfo.packageName)
 
         // Text
         val otherDeviceName = newChipInfo.routeInfo.name.toString()
-        currentChipView.requireViewById<TextView>(R.id.text).apply {
-            text = chipState.getChipTextString(context, otherDeviceName)
-        }
+        val chipText = chipState.getChipTextString(context, otherDeviceName)
+        currentChipView.requireViewById<TextView>(R.id.text).text = chipText
 
         // Loading
         currentChipView.requireViewById<View>(R.id.loading).visibility =
@@ -145,17 +144,29 @@
         // Failure
         currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
             chipState.isTransferFailure.visibleIfTrue()
+
+        // For accessibility
+        currentChipView.requireViewById<ViewGroup>(
+                R.id.media_ttt_sender_chip_inner
+        ).contentDescription = "$iconName $chipText"
     }
 
     override fun animateChipIn(chipView: ViewGroup) {
+        val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
         ViewHierarchyAnimator.animateAddition(
-            chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner),
+            chipInnerView,
             ViewHierarchyAnimator.Hotspot.TOP,
             Interpolators.EMPHASIZED_DECELERATE,
-            duration = 500L,
+            duration = ANIMATION_DURATION,
             includeMargins = true,
             includeFadeIn = true,
         )
+
+        // We can only request focus once the animation finishes.
+        mainExecutor.executeDelayed(
+                { chipInnerView.requestAccessibilityFocus() },
+                ANIMATION_DURATION
+        )
     }
 
     override fun removeChip(removalReason: String) {
@@ -186,3 +197,4 @@
 }
 
 const val SENDER_TAG = "MediaTapToTransferSender"
+private const val ANIMATION_DURATION = 500L
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 2d7a809..3789cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -302,10 +302,6 @@
      */
     @VisibleForTesting
     void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) {
-        if (initializeTaskbarIfNecessary()) {
-            return;
-        }
-
         if (display == null) {
             return;
         }
@@ -315,7 +311,7 @@
 
         // We may show TaskBar on the default display for large screen device. Don't need to create
         // navigation bar for this case.
-        if (mIsTablet && isOnDefaultDisplay) {
+        if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index b44b4de..6424256 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -42,7 +42,6 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.ViewController
-import com.android.wm.shell.back.BackAnimation
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.math.abs
@@ -119,7 +118,7 @@
         private val latencyTracker: LatencyTracker
     ) {
         /** Construct a [BackPanelController].  */
-        fun create(context: Context, backAnimation: BackAnimation?): BackPanelController {
+        fun create(context: Context): BackPanelController {
             val backPanelController = BackPanelController(
                 context,
                 windowManager,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index fc6dcd3..6ac3ead 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -574,10 +574,10 @@
     private void resetEdgeBackPlugin() {
         if (mIsNewBackAffordanceEnabled) {
             setEdgeBackPlugin(
-                    mBackPanelControllerFactory.create(mContext, mBackAnimation));
+                    mBackPanelControllerFactory.create(mContext));
         } else {
             setEdgeBackPlugin(
-                    new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+                    new NavigationBarEdgePanel(mContext, mLatencyTracker));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index eba9d3f..122852f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -43,7 +43,6 @@
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
-import android.window.BackEvent;
 
 import androidx.core.graphics.ColorUtils;
 import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -59,7 +58,6 @@
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.wm.shell.back.BackAnimation;
 
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
@@ -283,14 +281,11 @@
                 }
             };
     private BackCallback mBackCallback;
-    private BackAnimation mBackAnimation;
 
-    public NavigationBarEdgePanel(Context context,
-            BackAnimation backAnimation, LatencyTracker latencyTracker) {
+    public NavigationBarEdgePanel(Context context, LatencyTracker latencyTracker) {
         super(context);
 
         mWindowManager = context.getSystemService(WindowManager.class);
-        mBackAnimation = backAnimation;
         mVibratorHelper = Dependency.get(VibratorHelper.class);
 
         mDensity = context.getResources().getDisplayMetrics().density;
@@ -360,7 +355,6 @@
                 .getDimension(R.dimen.navigation_edge_action_drag_threshold);
         mSwipeProgressThreshold = context.getResources()
                 .getDimension(R.dimen.navigation_edge_action_progress_threshold);
-        initializeBackAnimation();
 
         setVisibility(GONE);
 
@@ -388,17 +382,6 @@
         mLatencyTracker = latencyTracker;
     }
 
-    public void setBackAnimation(BackAnimation backAnimation) {
-        mBackAnimation = backAnimation;
-        initializeBackAnimation();
-    }
-
-    private void initializeBackAnimation() {
-        if (mBackAnimation != null) {
-            mBackAnimation.setSwipeThresholds(mSwipeTriggerThreshold, mSwipeProgressThreshold);
-        }
-    }
-
     @Override
     public void onDestroy() {
         cancelFailsafe();
@@ -484,12 +467,6 @@
 
     @Override
     public void onMotionEvent(MotionEvent event) {
-        if (mBackAnimation != null) {
-            mBackAnimation.onBackMotion(
-                    event.getX(), event.getY(),
-                    event.getActionMasked(),
-                    mIsLeftPanel ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
-        }
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
@@ -903,9 +880,6 @@
             // Whenever the trigger back state changes the existing translation animation should be
             // cancelled
             mTranslationAnimation.cancel();
-            if (mBackAnimation != null) {
-                mBackAnimation.setTriggerBack(triggerBack);
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 68d35f9..824d3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -34,6 +34,8 @@
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
+import com.android.systemui.statusbar.notification.collection.PipelineDumper;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
 import com.android.systemui.util.time.SystemClock;
@@ -52,7 +54,8 @@
  */
 @SysUISingleton
 @SuppressLint("OverrideAbstract")
-public class NotificationListener extends NotificationListenerWithPlugins {
+public class NotificationListener extends NotificationListenerWithPlugins implements
+        PipelineDumpable {
     private static final String TAG = "NotificationListener";
     private static final boolean DEBUG = CentralSurfaces.DEBUG;
     private static final long MAX_RANKING_DELAY_MILLIS = 500L;
@@ -255,6 +258,11 @@
         }
     }
 
+    @Override
+    public void dumpPipeline(@NonNull PipelineDumper d) {
+        d.dump("notificationHandlers", mNotificationHandlers);
+    }
+
     private static Ranking getRankingOrTemporaryStandIn(RankingMap rankingMap, String key) {
         Ranking ranking = new Ranking();
         if (!rankingMap.getRanking(key, ranking)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 48e3450..0951e82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,7 +19,9 @@
 import android.app.IActivityManager;
 import android.content.Context;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.service.dreams.IDreamManager;
+import android.util.Log;
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.statusbar.IStatusBarService;
@@ -60,10 +62,12 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarIconList;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.tracing.ProtoTracer;
@@ -274,7 +278,30 @@
     @Provides
     @SysUISingleton
     static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
+            KeyguardStateController keyguardStateController,
+            Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManager,
             InteractionJankMonitor interactionJankMonitor) {
-        return new DialogLaunchAnimator(dreamManager, interactionJankMonitor);
+        DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() {
+            @Override
+            public boolean isDreaming() {
+                try {
+                    return dreamManager.isDreaming();
+                } catch (RemoteException e) {
+                    Log.e("DialogLaunchAnimator.Callback", "dreamManager.isDreaming failed", e);
+                    return false;
+                }
+            }
+
+            @Override
+            public boolean isUnlocked() {
+                return keyguardStateController.isUnlocked();
+            }
+
+            @Override
+            public boolean isShowingAlternateAuthOnUnlock() {
+                return statusBarKeyguardViewManager.get().shouldShowAltAuth();
+            }
+        };
+        return new DialogLaunchAnimator(callback, interactionJankMonitor);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 351a4be..68bf69a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -142,7 +142,7 @@
  */
 @MainThread
 @SysUISingleton
-public class NotifCollection implements Dumpable {
+public class NotifCollection implements Dumpable, PipelineDumpable {
     private final IStatusBarService mStatusBarService;
     private final SystemClock mClock;
     private final NotifPipelineFlags mNotifPipelineFlags;
@@ -870,6 +870,14 @@
         }
     }
 
+    @Override
+    public void dumpPipeline(@NonNull PipelineDumper d) {
+        d.dump("notifCollectionListeners", mNotifCollectionListeners);
+        d.dump("lifetimeExtenders", mLifetimeExtenders);
+        d.dump("dismissInterceptors", mDismissInterceptors);
+        d.dump("buildListener", mBuildListener);
+    }
+
     private final BatchableNotificationHandler mNotifHandler = new BatchableNotificationHandler() {
         @Override
         public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumpable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumpable.kt
new file mode 100644
index 0000000..a1aec3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumpable.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+interface PipelineDumpable {
+    fun dumpPipeline(d: PipelineDumper)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.kt
new file mode 100644
index 0000000..a10c745
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.withIncreasedIndent
+import java.io.PrintWriter
+
+class PipelineDumper(pw: PrintWriter) {
+    private val ipw = pw.asIndenting()
+
+    fun print(a: Any?) = ipw.print(a)
+    fun println(a: Any?) = ipw.println(a)
+    fun withIncreasedIndent(b: () -> Unit) = ipw.withIncreasedIndent(b)
+    fun withIncreasedIndent(r: Runnable) = ipw.withIncreasedIndent(r)
+
+    fun dump(label: String, value: Any?) {
+        ipw.print("$label: ")
+        dump(value)
+    }
+
+    private fun dump(value: Any?) = when (value) {
+        null, is String, is Int -> println(value)
+        is Collection<*> -> dumpCollection(value)
+        else -> {
+            println(value.fullPipelineName)
+            withIncreasedIndent { (value as? PipelineDumpable)?.dumpPipeline(this) }
+        }
+    }
+
+    private fun dumpCollection(values: Collection<Any?>) {
+        println(values.size)
+        withIncreasedIndent { values.forEach { dump(it) } }
+    }
+}
+
+private val Any.bareClassName: String get() {
+    val className = javaClass.name
+    val packageName = javaClass.`package`.name
+    return className.substring(packageName.length + 1)
+}
+
+private val Any.barePipelineName: String? get() = when (this) {
+    is NotifLifetimeExtender -> name
+    is NotifDismissInterceptor -> name
+    is Pluggable<*> -> name
+    else -> null
+}
+
+private val Any.fullPipelineName: String get() =
+    barePipelineName?.let { "\"$it\" ($bareClassName)" } ?: bareClassName
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 075a0dc..420f21d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -87,7 +87,7 @@
  */
 @MainThread
 @SysUISingleton
-public class ShadeListBuilder implements Dumpable {
+public class ShadeListBuilder implements Dumpable, PipelineDumpable {
     private final SystemClock mSystemClock;
     private final ShadeListBuilderLogger mLogger;
     private final NotificationInteractionTracker mInteractionTracker;
@@ -1396,6 +1396,21 @@
                 "\t\t"));
     }
 
+    @Override
+    public void dumpPipeline(@NonNull PipelineDumper d) {
+        d.dump("choreographer", mChoreographer);
+        d.dump("notifPreGroupFilters", mNotifPreGroupFilters);
+        d.dump("onBeforeTransformGroupsListeners", mOnBeforeTransformGroupsListeners);
+        d.dump("notifPromoters", mNotifPromoters);
+        d.dump("onBeforeSortListeners", mOnBeforeSortListeners);
+        d.dump("notifSections", mNotifSections);
+        d.dump("notifComparators", mNotifComparators);
+        d.dump("onBeforeFinalizeFilterListeners", mOnBeforeFinalizeFilterListeners);
+        d.dump("notifFinalizeFilters", mNotifFinalizeFilters);
+        d.dump("onBeforeRenderListListeners", mOnBeforeRenderListListeners);
+        d.dump("onRenderListListener", mOnRenderListListener);
+    }
+
     /** See {@link #setOnRenderListListener(OnRenderListListener)} */
     public interface OnRenderListListener {
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 050b4c1..98f2167 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -32,6 +32,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
+import com.android.systemui.statusbar.notification.collection.PipelineDumper;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
@@ -63,7 +65,7 @@
  * passed along to the NotifCollection.
  */
 @MainThread
-public class GroupCoalescer implements Dumpable {
+public class GroupCoalescer implements Dumpable, PipelineDumpable {
     private final DelayableExecutor mMainExecutor;
     private final SystemClock mClock;
     private final GroupCoalescerLogger mLogger;
@@ -314,6 +316,11 @@
         }
     }
 
+    @Override
+    public void dumpPipeline(@NonNull PipelineDumper d) {
+        d.dump("handler", mHandler);
+    }
+
     private final Comparator<CoalescedEvent> mEventComparator = (o1, o2) -> {
         int cmp = Boolean.compare(
                 o2.getSbn().getNotification().isGroupSummary(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 891e25e..1399385 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -15,24 +15,22 @@
  */
 package com.android.systemui.statusbar.notification.collection.coordinator
 
-import com.android.systemui.Dumpable
-import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
-import java.io.PrintWriter
 import javax.inject.Inject
 
 /**
  * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the
  * Coordinators can register their respective callbacks.
  */
-interface NotifCoordinators : Coordinator, Dumpable
+interface NotifCoordinators : Coordinator, PipelineDumpable
 
 @CoordinatorScope
 class NotifCoordinatorsImpl @Inject constructor(
-    dumpManager: DumpManager,
     notifPipelineFlags: NotifPipelineFlags,
     dataStoreCoordinator: DataStoreCoordinator,
     hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
@@ -66,8 +64,6 @@
      * Creates all the coordinators.
      */
     init {
-        dumpManager.registerDumpable(TAG, this)
-
         // TODO(b/208866714): formalize the system by which some coordinators may be required by the
         //  pipeline, such as this DataStoreCoordinator which cannot be removed, as it's a critical
         //  glue between the pipeline and parts of SystemUI which depend on pipeline output via the
@@ -121,15 +117,12 @@
         pipeline.setSections(mOrderedSections)
     }
 
-    override fun dump(pw: PrintWriter, args: Array<String>) {
-        pw.println()
-        pw.println("$TAG:")
-        for (c in mCoordinators) {
-            pw.println("\t${c.javaClass}")
-        }
-        for (s in mOrderedSections) {
-            pw.println("\t${s.name}")
-        }
+    /*
+     * As part of the NotifPipeline dumpable, dumps the list of coordinators; sections are omitted
+     * as they are dumped in the RenderStageManager instead.
+     */
+    override fun dumpPipeline(d: PipelineDumper) = with(d) {
+        dump("coordinators", mCoordinators)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 24ef580..a34d033 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -18,6 +18,8 @@
 
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
@@ -25,12 +27,15 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
+import com.android.systemui.statusbar.notification.collection.PipelineDumper;
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
 import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
 import com.android.systemui.statusbar.notification.collection.render.RenderStageManager;
+import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager;
 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManagerFactory;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
@@ -42,7 +47,7 @@
  * Initialization code for the new notification pipeline.
  */
 @SysUISingleton
-public class NotifPipelineInitializer implements Dumpable {
+public class NotifPipelineInitializer implements Dumpable, PipelineDumpable {
     private final NotifPipeline mPipelineWrapper;
     private final GroupCoalescer mGroupCoalescer;
     private final NotifCollection mNotifCollection;
@@ -53,6 +58,9 @@
     private final DumpManager mDumpManager;
     private final ShadeViewManagerFactory mShadeViewManagerFactory;
 
+    /* These are saved just for dumping. */
+    private ShadeViewManager mShadeViewManager;
+    private NotificationListener mNotificationService;
 
     @Inject
     public NotifPipelineInitializer(
@@ -83,9 +91,10 @@
             NotificationRowBinderImpl rowBinder,
             NotificationListContainer listContainer,
             NotifStackController stackController) {
-
         mDumpManager.registerDumpable("NotifPipeline", this);
 
+        mNotificationService = notificationService;
+
         // Setup inflation
         mNotifInflater.setRowBinder(rowBinder);
 
@@ -93,13 +102,12 @@
         mNotifPluggableCoordinators.attach(mPipelineWrapper);
 
         // Wire up pipeline
-        mShadeViewManagerFactory
-                .create(listContainer, stackController)
-                .attach(mRenderStageManager);
+        mShadeViewManager = mShadeViewManagerFactory.create(listContainer, stackController);
+        mShadeViewManager.attach(mRenderStageManager);
         mRenderStageManager.attach(mListBuilder);
         mListBuilder.attach(mNotifCollection);
         mNotifCollection.attach(mGroupCoalescer);
-        mGroupCoalescer.attach(notificationService);
+        mGroupCoalescer.attach(mNotificationService);
 
         Log.d(TAG, "Notif pipeline initialized."
                 + " rendering=" + true);
@@ -107,8 +115,37 @@
 
     @Override
     public void dump(PrintWriter pw, String[] args) {
-        mNotifPluggableCoordinators.dump(pw, args);
-        mGroupCoalescer.dump(pw, args);
+        dumpPipeline(new PipelineDumper(pw));
+    }
+
+    @Override
+    public void dumpPipeline(@NonNull PipelineDumper d) {
+        d.println("STAGE 0: SETUP");
+        d.dump("notifPluggableCoordinators", mNotifPluggableCoordinators);
+        d.println("");
+
+        d.println("STAGE 1: LISTEN");
+        d.dump("notificationService", mNotificationService);
+        d.println("");
+
+        d.println("STAGE 2: BATCH EVENTS");
+        d.dump("groupCoalescer", mGroupCoalescer);
+        d.println("");
+
+        d.println("STAGE 3: COLLECT");
+        d.dump("notifCollection", mNotifCollection);
+        d.println("");
+
+        d.println("STAGE 4: BUILD LIST");
+        d.dump("listBuilder", mListBuilder);
+        d.println("");
+
+        d.println("STAGE 5: DISPATCH RENDER");
+        d.dump("renderStageManager", mRenderStageManager);
+        d.println("");
+
+        d.println("STAGE 6: UPDATE SHADE");
+        d.dump("shadeViewManager", mShadeViewManager);
     }
 
     private static final String TAG = "NotifPipeline";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index ea66f3b..9765e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.listbuilder
 
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.render.NodeController
@@ -24,10 +26,18 @@
 data class NotifSection(
     val sectioner: NotifSectioner,
     val index: Int
-) {
+) : PipelineDumpable {
     @PriorityBucket
     val bucket: Int = sectioner.bucket
     val label: String = "$index:$bucket:${sectioner.name}"
     val headerController: NodeController? = sectioner.headerNodeController
     val comparator: NotifComparator? = sectioner.comparator
+
+    override fun dumpPipeline(d: PipelineDumper) = with(d) {
+        dump("index", index)
+        dump("bucket", bucket)
+        dump("sectioner", sectioner)
+        dump("headerController", headerController)
+        dump("comparator", comparator)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
index a9c3987..7a37846 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -20,6 +20,8 @@
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
@@ -33,7 +35,7 @@
  * provided to [setViewRenderer].
  */
 @SysUISingleton
-class RenderStageManager @Inject constructor() {
+class RenderStageManager @Inject constructor() : PipelineDumpable {
     private val onAfterRenderListListeners = mutableListOf<OnAfterRenderListListener>()
     private val onAfterRenderGroupListeners = mutableListOf<OnAfterRenderGroupListener>()
     private val onAfterRenderEntryListeners = mutableListOf<OnAfterRenderEntryListener>()
@@ -75,6 +77,13 @@
         onAfterRenderEntryListeners.add(listener)
     }
 
+    override fun dumpPipeline(d: PipelineDumper) = with(d) {
+        dump("ViewRenderer", viewRenderer)
+        dump("OnAfterRenderListListeners", onAfterRenderListListeners)
+        dump("OnAfterRenderGroupListeners", onAfterRenderGroupListeners)
+        dump("OnAfterRenderEntryListeners", onAfterRenderEntryListeners)
+    }
+
     private fun dispatchOnAfterRenderList(
         viewRenderer: NotifViewRenderer,
         entries: List<ListEntry>
@@ -139,4 +148,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
index b76169f..2073e92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.notification.collection.render
 
 import android.view.View
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -28,7 +30,7 @@
 class RootNodeController(
     private val listContainer: NotificationListContainer,
     override val view: View
-) : NodeController {
+) : NodeController, PipelineDumpable {
     override val nodeLabel: String = "<root>"
 
     override fun getChildAt(index: Int): View? {
@@ -59,4 +61,8 @@
             listContainer.setChildTransferInProgress(false)
         }
     }
+
+    override fun dumpPipeline(d: PipelineDumper) = with(d) {
+        dump("listContainer", listContainer)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 51dc728..df8e87f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -22,6 +22,8 @@
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.util.traceSection
@@ -43,12 +45,12 @@
     nodeSpecBuilderLogger: NodeSpecBuilderLogger,
     shadeViewDifferLogger: ShadeViewDifferLogger,
     private val viewBarn: NotifViewBarn
-) {
+) : PipelineDumpable {
     // We pass a shim view here because the listContainer may not actually have a view associated
     // with it and the differ never actually cares about the root node's view.
     private val rootController = RootNodeController(listContainer, View(context))
     private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager,
-            sectionHeaderVisibilityProvider, viewBarn, nodeSpecBuilderLogger)
+        sectionHeaderVisibilityProvider, viewBarn, nodeSpecBuilderLogger)
     private val viewDiffer = ShadeViewDiffer(rootController, shadeViewDifferLogger)
 
     /** Method for attaching this manager to the pipeline. */
@@ -56,6 +58,12 @@
         renderStageManager.setViewRenderer(viewRenderer)
     }
 
+    override fun dumpPipeline(d: PipelineDumper) = with(d) {
+        dump("rootController", rootController)
+        dump("specBuilder", specBuilder)
+        dump("viewDiffer", viewDiffer)
+    }
+
     private val viewRenderer = object : NotifViewRenderer {
 
         override fun onRenderList(notifList: List<ListEntry>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index e5835a8..cc539b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -86,6 +86,8 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
+import com.android.systemui.statusbar.notification.collection.PipelineDumper;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -1560,7 +1562,8 @@
         }
     }
 
-    private class NotificationListContainerImpl implements NotificationListContainer {
+    private class NotificationListContainerImpl implements NotificationListContainer,
+            PipelineDumpable {
 
         @Override
         public void setChildTransferInProgress(boolean childTransferInProgress) {
@@ -1706,6 +1709,12 @@
         public void setWillExpand(boolean willExpand) {
             mView.setWillExpand(willExpand);
         }
+
+        @Override
+        public void dumpPipeline(@NonNull PipelineDumper d) {
+            d.dump("NotificationStackScrollLayoutController.this",
+                    NotificationStackScrollLayoutController.this);
+        }
     }
 
     class TouchHandler implements Gefingerpoken {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 3880ee3..d5c6f89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -471,7 +471,8 @@
         showBouncer(scrimmed);
     }
 
-    private boolean shouldShowAltAuth() {
+    /** Whether we should show the alternate authentication instead of the traditional bouncer. */
+    public boolean shouldShowAltAuth() {
         return mAlternateAuthInterceptor != null
                 && mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e22a896..4c76270 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -405,8 +405,8 @@
             }
 
             @Override
-            public void onEntryUpdated(NotificationEntry entry) {
-                BubblesManager.this.onEntryUpdated(entry);
+            public void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+                BubblesManager.this.onEntryUpdated(entry, fromSystem);
             }
 
             @Override
@@ -444,9 +444,10 @@
         }
     }
 
-    void onEntryUpdated(NotificationEntry entry) {
+    void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+        boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry);
         mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
-                mNotificationInterruptStateProvider.shouldBubbleUp(entry));
+                shouldBubble, fromSystem);
     }
 
     void onEntryRemoved(NotificationEntry entry) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 8d27f24..c677371 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -22,6 +22,7 @@
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -109,6 +110,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
+import org.mockito.internal.util.reflection.FieldSetter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -200,9 +202,10 @@
     private ArgumentCaptor<CancellationSignal> mCancellationSignalCaptor;
 
     // Direct executor
-    private Executor mBackgroundExecutor = Runnable::run;
-    private Executor mMainExecutor = Runnable::run;
+    private final Executor mBackgroundExecutor = Runnable::run;
+    private final Executor mMainExecutor = Runnable::run;
     private TestableLooper mTestableLooper;
+    private Handler mHandler;
     private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private TestableContext mSpiedContext;
     private MockitoSession mMockitoSession;
@@ -291,6 +294,13 @@
         mBiometricEnabledOnKeyguardCallback = mBiometricEnabledCallbackArgCaptor.getValue();
         biometricsEnabledForCurrentUser();
 
+        mHandler = spy(mKeyguardUpdateMonitor.getHandler());
+        try {
+            FieldSetter.setField(mKeyguardUpdateMonitor,
+                    KeyguardUpdateMonitor.class.getDeclaredField("mHandler"), mHandler);
+        } catch (NoSuchFieldException e) {
+
+        }
         verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
         mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
         mKeyguardUpdateMonitor.registerCallback(mTestCallback);
@@ -330,7 +340,7 @@
 
         when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
         when(mTelephonyManager.getSimState(anyInt())).thenReturn(state);
-        when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[] { subId });
+        when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[]{subId});
 
         KeyguardUpdateMonitor testKUM = new TestableKeyguardUpdateMonitor(mSpiedContext);
 
@@ -505,7 +515,7 @@
         // Even SimState Loaded, still need ACTION_SERVICE_STATE turn on mTelephonyCapable
         assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
 
-        Intent intentServiceState =  new Intent(Intent.ACTION_SERVICE_STATE);
+        Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
         intentSimState.putExtra(Intent.EXTRA_SIM_STATE
                 , Intent.SIM_STATE_LOADED);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -520,7 +530,7 @@
         mTestableLooper.processAllMessages();
 
         verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
-                        anyInt());
+                anyInt());
         verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
     }
 
@@ -791,7 +801,8 @@
     public void testBiometricsCleared_whenUserSwitches() throws Exception {
         final IRemoteCallback reply = new IRemoteCallback.Stub() {
             @Override
-            public void sendResult(Bundle data) {} // do nothing
+            public void sendResult(Bundle data) {
+            } // do nothing
         };
         final BiometricAuthenticated dummyAuthentication =
                 new BiometricAuthenticated(true /* authenticated */, true /* strong */);
@@ -809,7 +820,8 @@
     public void testMultiUserJankMonitor_whenUserSwitches() throws Exception {
         final IRemoteCallback reply = new IRemoteCallback.Stub() {
             @Override
-            public void sendResult(Bundle data) {} // do nothing
+            public void sendResult(Bundle data) {
+            } // do nothing
         };
         mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */);
         verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH);
@@ -1499,6 +1511,34 @@
         assertThat(cancelSignal.isCanceled()).isTrue();
     }
 
+    @Test
+    public void testFingerprintCanAuth_whenCancellationNotReceivedAndAuthFailed() {
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+        mTestableLooper.processAllMessages();
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+                anyInt());
+
+        mKeyguardUpdateMonitor.onFaceAuthenticated(0, false);
+        // Make sure keyguard is going away after face auth attempt, and that it calls
+        // updateBiometricStateListeningState.
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false);
+        mTestableLooper.processAllMessages();
+
+        verify(mHandler).postDelayed(mKeyguardUpdateMonitor.mFpCancelNotReceived,
+                DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+
+        mKeyguardUpdateMonitor.onFingerprintAuthenticated(0, true);
+        mTestableLooper.processAllMessages();
+
+        verify(mHandler, times(1)).removeCallbacks(mKeyguardUpdateMonitor.mFpCancelNotReceived);
+        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+        mTestableLooper.processAllMessages();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(anyBoolean())).isEqualTo(true);
+    }
+
     private void fingerprintIsNotEnrolled() {
         when(mFingerprintManager.hasEnrolledTemplates(mCurrentUserId)).thenReturn(false);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index c48cbb1..0f11241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -26,6 +26,7 @@
 import junit.framework.Assert.assertNull
 import junit.framework.Assert.assertTrue
 import junit.framework.AssertionFailedError
+import kotlin.concurrent.thread
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -34,19 +35,18 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.Spy
 import org.mockito.junit.MockitoJUnit
-import kotlin.concurrent.thread
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper
 class ActivityLaunchAnimatorTest : SysuiTestCase() {
     private val launchContainer = LinearLayout(mContext)
-    private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+    private val testLaunchAnimator = fakeLaunchAnimator()
     @Mock lateinit var callback: ActivityLaunchAnimator.Callback
     @Mock lateinit var listener: ActivityLaunchAnimator.Listener
     @Spy private val controller = TestLaunchAnimatorController(launchContainer)
@@ -77,12 +77,13 @@
         // We start in a new thread so that we can ensure that the callbacks are called in the main
         // thread.
         thread {
-            animator.startIntentWithAnimation(
+                animator.startIntentWithAnimation(
                     controller = controller,
                     animate = animate,
                     intentStarter = intentStarter
-            )
-        }.join()
+                )
+            }
+            .join()
     }
 
     @Test
@@ -197,14 +198,25 @@
         val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
         val taskInfo = ActivityManager.RunningTaskInfo()
         taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity")
-        taskInfo.topActivityInfo = ActivityInfo().apply {
-            applicationInfo = ApplicationInfo()
-        }
+        taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
 
         return RemoteAnimationTarget(
-                0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
-                Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
-                taskInfo, false
+            0,
+            RemoteAnimationTarget.MODE_OPENING,
+            SurfaceControl(),
+            false,
+            Rect(),
+            Rect(),
+            0,
+            Point(),
+            Rect(),
+            bounds,
+            WindowConfiguration(),
+            false,
+            SurfaceControl(),
+            Rect(),
+            taskInfo,
+            false
         )
     }
 }
@@ -213,17 +225,17 @@
  * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
  * outside of the main thread.
  */
-private class TestLaunchAnimatorController(
-    override var launchContainer: ViewGroup
-) : ActivityLaunchAnimator.Controller {
-    override fun createAnimatorState() = LaunchAnimator.State(
+private class TestLaunchAnimatorController(override var launchContainer: ViewGroup) :
+    ActivityLaunchAnimator.Controller {
+    override fun createAnimatorState() =
+        LaunchAnimator.State(
             top = 100,
             bottom = 200,
             left = 300,
             right = 400,
             topCornerRadius = 10f,
             bottomCornerRadius = 20f
-    )
+        )
 
     private fun assertOnMainThread() {
         if (Looper.myLooper() != Looper.getMainLooper()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 4218e09..7c1e384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -5,7 +5,6 @@
 import android.graphics.Color
 import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
-import android.service.dreams.IDreamManager
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.ViewUtils
@@ -38,19 +37,16 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class DialogLaunchAnimatorTest : SysuiTestCase() {
-    private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
     private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
     private val attachedViews = mutableSetOf<View>()
 
-    @Mock lateinit var dreamManager: IDreamManager
     @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
     @get:Rule val rule = MockitoJUnit.rule()
 
     @Before
     fun setUp() {
-        dialogLaunchAnimator = DialogLaunchAnimator(
-            dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true
-        )
+        dialogLaunchAnimator =
+            fakeDialogLaunchAnimator(interactionJankMonitor = interactionJankMonitor)
     }
 
     @After
@@ -153,6 +149,22 @@
     }
 
     @Test
+    fun testActivityLaunchWhenLockedWithoutAlternateAuth() {
+        val dialogLaunchAnimator =
+            fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = false)
+        val dialog = createAndShowDialog(dialogLaunchAnimator)
+        assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+    }
+
+    @Test
+    fun testActivityLaunchWhenLockedWithAlternateAuth() {
+        val dialogLaunchAnimator =
+            fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = true)
+        val dialog = createAndShowDialog(dialogLaunchAnimator)
+        assertNotNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+    }
+
+    @Test
     fun testDialogAnimationIsChangedByAnimator() {
         // Important: the power menu animation relies on this behavior to know when to animate (see
         // http://ag/16774605).
@@ -193,11 +205,13 @@
         verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)
     }
 
-    private fun createAndShowDialog(): TestDialog {
+    private fun createAndShowDialog(
+        animator: DialogLaunchAnimator = dialogLaunchAnimator,
+    ): TestDialog {
         val touchSurface = createTouchSurface()
         return runOnMainThreadAndWaitForIdleSync {
             val dialog = TestDialog(context)
-            dialogLaunchAnimator.showFromView(dialog, touchSurface)
+            animator.showFromView(dialog, touchSurface)
             dialog
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
deleted file mode 100644
index dadf94e..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.android.systemui.animation
-
-/**
- * A [LaunchAnimator.Timings] to be used in tests.
- *
- * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
- * when computing the progress of a sub-animation (the contents fade in/out).
- */
-val TEST_TIMINGS = LaunchAnimator.Timings(
-    totalDuration = 0L,
-    contentBeforeFadeOutDelay = 1L,
-    contentBeforeFadeOutDuration = 1L,
-    contentAfterFadeInDelay = 1L,
-    contentAfterFadeInDuration = 1L
-)
-
-/** A [LaunchAnimator.Interpolators] to be used in tests. */
-val TEST_INTERPOLATORS = LaunchAnimator.Interpolators(
-    positionInterpolator = Interpolators.STANDARD,
-    positionXInterpolator = Interpolators.STANDARD,
-    contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
-    contentAfterFadeInInterpolator = Interpolators.STANDARD
-)
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
index 2915f5a..e099c92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
@@ -20,6 +20,7 @@
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
 import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
 import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType;
@@ -60,6 +61,8 @@
                 .isEqualTo(COMPLICATION_TYPE_CAST_INFO);
         assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS))
                 .isEqualTo(COMPLICATION_TYPE_HOME_CONTROLS);
+        assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_SMARTSPACE))
+                .isEqualTo(COMPLICATION_TYPE_SMARTSPACE);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 4abb973..56aff3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -24,16 +24,11 @@
     @Before
     fun setup() {
         outputWriter = StringWriter()
-        buffer = createBuffer(UNBOUNDED_STACK_TRACE, NESTED_TRACE_DEPTH)
+        buffer = createBuffer()
     }
 
-    private fun createBuffer(rootTraceDepth: Int, nestedTraceDepth: Int): LogBuffer {
-        return LogBuffer("TestBuffer",
-                1,
-                logcatEchoTracker,
-                false,
-                rootStackTraceDepth = rootTraceDepth,
-                nestedStackTraceDepth = nestedTraceDepth)
+    private fun createBuffer(): LogBuffer {
+        return LogBuffer("TestBuffer", 1, logcatEchoTracker, false)
     }
 
     @Test
@@ -56,95 +51,83 @@
     }
 
     @Test
-    fun dump_writesExceptionAndStacktraceLimitedToGivenDepth() {
-        buffer = createBuffer(rootTraceDepth = 2, nestedTraceDepth = -1)
-        // stack trace depth of 5
-        val exception = createTestException("Exception message", "TestClass", 5)
+    fun dump_writesExceptionAndStacktrace() {
+        buffer = createBuffer()
+        val exception = createTestException("Exception message", "TestClass")
         buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
 
         val dumpedString = dumpBuffer()
 
-        // logs are limited to depth 2
-        assertThat(dumpedString).contains("E Tag: Extra message")
-        assertThat(dumpedString).contains("E Tag: java.lang.RuntimeException: Exception message")
-        assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
-        assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
-        assertThat(dumpedString)
-                .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+        assertThat(dumpedString).contains("Extra message")
+        assertThat(dumpedString).contains("java.lang.RuntimeException: Exception message")
+        assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+        assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
     }
 
     @Test
-    fun dump_writesCauseAndStacktraceLimitedToGivenDepth() {
-        buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+    fun dump_writesCauseAndStacktrace() {
+        buffer = createBuffer()
         val exception = createTestException("Exception message",
                 "TestClass",
-                1,
-                cause = createTestException("The real cause!", "TestClass", 5))
+                cause = createTestException("The real cause!", "TestClass"))
         buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
 
         val dumpedString = dumpBuffer()
 
-        // logs are limited to depth 2
         assertThat(dumpedString)
-                .contains("E Tag: Caused by: java.lang.RuntimeException: The real cause!")
-        assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
-        assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
-        assertThat(dumpedString)
-                .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+                .contains("Caused by: java.lang.RuntimeException: The real cause!")
+        assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+        assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
     }
 
     @Test
-    fun dump_writesSuppressedExceptionAndStacktraceLimitedToGivenDepth() {
-        buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+    fun dump_writesSuppressedExceptionAndStacktrace() {
+        buffer = createBuffer()
         val exception = RuntimeException("Root exception message")
         exception.addSuppressed(
                 createTestException(
                         "First suppressed exception",
                         "FirstClass",
-                        5,
-                        createTestException("Cause of suppressed exp", "ThirdClass", 5)
+                        createTestException("Cause of suppressed exp", "ThirdClass")
                 ))
         exception.addSuppressed(
-                createTestException("Second suppressed exception", "SecondClass", 5))
+                createTestException("Second suppressed exception", "SecondClass"))
         buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
 
         val dumpedStr = dumpBuffer()
 
-        // logs are limited to depth 2
         // first suppressed exception
         assertThat(dumpedStr)
-                .contains("E Tag: Suppressed: " +
+                .contains("Suppressed: " +
                         "java.lang.RuntimeException: First suppressed exception")
-        assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:1)")
-        assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:2)")
-        assertThat(dumpedStr)
-                .doesNotContain("E Tag: \tat FirstClass.TestMethod(FirstClass.java:3)")
+        assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:1)")
+        assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:2)")
 
         assertThat(dumpedStr)
-                .contains("E Tag: Caused by: java.lang.RuntimeException: Cause of suppressed exp")
-        assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:1)")
-        assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:2)")
-        assertThat(dumpedStr)
-                .doesNotContain("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:3)")
+                .contains("Caused by: java.lang.RuntimeException: Cause of suppressed exp")
+        assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:1)")
+        assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:2)")
 
         // second suppressed exception
         assertThat(dumpedStr)
-                .contains("E Tag: Suppressed: " +
+                .contains("Suppressed: " +
                         "java.lang.RuntimeException: Second suppressed exception")
-        assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:1)")
-        assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:2)")
-        assertThat(dumpedStr)
-                .doesNotContain("E Tag: \tat SecondClass.TestMethod(SecondClass.java:3)")
+        assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:1)")
+        assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:2)")
     }
 
     private fun createTestException(
-        message: String,
-        errorClass: String,
-        stackTraceLength: Int,
-        cause: Throwable? = null
+            message: String,
+            errorClass: String,
+            cause: Throwable? = null,
     ): Exception {
         val exception = RuntimeException(message, cause)
-        exception.stackTrace = createStackTraceElements(errorClass, stackTraceLength)
+        exception.stackTrace = (1..5).map { lineNumber ->
+            StackTraceElement(errorClass,
+                    "TestMethod",
+                    "$errorClass.java",
+                    lineNumber)
+        }.toTypedArray()
         return exception
     }
 
@@ -152,16 +135,4 @@
         buffer.dump(PrintWriter(outputWriter), tailLength = 100)
         return outputWriter.toString()
     }
-
-    private fun createStackTraceElements(
-        errorClass: String,
-        stackTraceLength: Int
-    ): Array<StackTraceElement> {
-        return (1..stackTraceLength).map { lineNumber ->
-            StackTraceElement(errorClass,
-                    "TestMethod",
-                    "$errorClass.java",
-                    lineNumber)
-        }.toTypedArray()
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 568e0cb..260bb87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -254,4 +254,30 @@
 
         verify(mMediaOutputController).connectDevice(mMediaDevice2);
     }
+
+    @Test
+    public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
+        when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
+        List<MediaDevice> selectableDevices = new ArrayList<>();
+        selectableDevices.add(mMediaDevice1);
+        when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+        when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(true);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        mViewHolder.mContainerLayout.performClick();
+
+        assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void onBindViewHolder_volumeControlChangeToEnabled_enableSeekbarAgain() {
+        when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(false);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+        assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+
+        when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(true);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mSeekBar.isEnabled()).isTrue();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fee17c7..18acf3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -621,7 +622,7 @@
         assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
 
         // Send update
-        mEntryListener.onEntryUpdated(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
 
         // Nothing should have changed
         // Notif is suppressed after expansion
@@ -789,7 +790,7 @@
     @Test
     public void testAddNotif_notBubble() {
         mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
-        mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
+        mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry(), /* fromSystem= */ true);
 
         assertThat(mBubbleController.hasBubbles()).isFalse();
     }
@@ -827,7 +828,7 @@
         NotificationListenerService.Ranking ranking = new RankingBuilder(
                 mRow.getRanking()).setCanBubble(false).build();
         mRow.setRanking(ranking);
-        mEntryListener.onEntryUpdated(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
 
         assertFalse(mBubbleController.hasBubbles());
         verify(mDeleteIntent, never()).send();
@@ -1432,6 +1433,100 @@
         assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
     }
 
+    /**
+     * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+     * comes in for it, it stays in the overflow but the entry is updated.
+     */
+    @Test
+    public void testNonInterruptiveUpdate_doesntBubbleFromOverflow() {
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+        // Dismiss the bubble so it's in the overflow
+        mBubbleController.removeBubble(
+                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+
+        // Update the entry to not show in shade
+        setMetadataFlags(mRow,
+                Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+        mBubbleController.updateBubble(mBubbleEntry,
+                /* suppressFlyout= */ false, /* showInShade= */ true);
+
+        // Check that the update was applied - shouldn't be show in shade
+        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+        // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+        verify(mBubbleController, times(1)).inflateAndAdd(
+                any(Bubble.class), anyBoolean(), anyBoolean());
+    }
+
+    /**
+     * Verifies that if a bubble is active, and a non-interruptive notification update comes in for
+     * it, it doesn't trigger a new inflate and add for that bubble.
+     */
+    @Test
+    public void testNonInterruptiveUpdate_doesntTriggerInflate() {
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+        // Update the entry to not show in shade
+        setMetadataFlags(mRow,
+                Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+        mBubbleController.updateBubble(mBubbleEntry,
+                /* suppressFlyout= */ false, /* showInShade= */ true);
+
+        // Check that the update was applied - shouldn't be show in shade
+        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+        // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+        verify(mBubbleController, times(1)).inflateAndAdd(
+                any(Bubble.class), anyBoolean(), anyBoolean());
+    }
+
+    /**
+     * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+     * comes in for it with FLAG_BUBBLE that the flag is removed.
+     */
+    @Test
+    public void testNonInterruptiveUpdate_doesntOverrideOverflowFlagBubble() {
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+        // Dismiss the bubble so it's in the overflow
+        mBubbleController.removeBubble(
+                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+        // Once it's in the overflow it's not actively a bubble (doesn't have FLAG_BUBBLE)
+        Bubble b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+        assertThat(b.isBubble()).isFalse();
+
+        // Send a non-notifying update that has FLAG_BUBBLE
+        mRow.getSbn().getNotification().flags = FLAG_BUBBLE;
+        assertThat(mRow.getSbn().getNotification().isBubbleNotification()).isTrue();
+        mBubbleController.updateBubble(mBubbleEntry,
+                /* suppressFlyout= */ false, /* showInShade= */ true);
+
+        // Verify that it still doesn't have FLAG_BUBBLE because it's in the overflow.
+        b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+        assertThat(b.isBubble()).isFalse();
+    }
+
+    @Test
+    public void testNonSystemUpdatesIgnored() {
+        mEntryListener.onEntryAdded(mRow);
+        assertThat(mBubbleController.hasBubbles()).isTrue();
+
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+
+        // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+        verify(mBubbleController, times(1)).inflateAndAdd(
+                any(Bubble.class), anyBoolean(), anyBoolean());
+    }
+
     /** Creates a bubble using the userId and package. */
     private Bubble createBubble(int userId, String pkg) {
         final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index c52ea60f..c83189d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui;
 
+import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -34,6 +36,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.broadcast.FakeBroadcastDispatcher;
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
@@ -119,6 +122,7 @@
         // is missing (constructing the actual one would throw).
         // TODO(b/219008720): Remove this.
         mDependency.injectMockDependency(SystemUIDialogManager.class);
+        mDependency.injectTestDependency(DialogLaunchAnimator.class, fakeDialogLaunchAnimator());
     }
 
     @After
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
new file mode 100644
index 0000000..990db77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+import com.android.internal.jank.InteractionJankMonitor
+import org.mockito.Mockito.mock
+
+/** A [DialogLaunchAnimator] to be used in tests. */
+@JvmOverloads
+fun fakeDialogLaunchAnimator(
+    isUnlocked: Boolean = true,
+    isShowingAlternateAuthOnUnlock: Boolean = false,
+    interactionJankMonitor: InteractionJankMonitor = mock(InteractionJankMonitor::class.java),
+): DialogLaunchAnimator {
+    return DialogLaunchAnimator(
+        FakeCallback(
+            isUnlocked = isUnlocked,
+            isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
+        ),
+        interactionJankMonitor,
+        fakeLaunchAnimator(),
+        isForTesting = true,
+    )
+}
+
+private class FakeCallback(
+    private val isDreaming: Boolean = false,
+    private val isUnlocked: Boolean = true,
+    private val isShowingAlternateAuthOnUnlock: Boolean = false,
+) : DialogLaunchAnimator.Callback {
+    override fun isDreaming(): Boolean = isDreaming
+    override fun isUnlocked(): Boolean = isUnlocked
+    override fun isShowingAlternateAuthOnUnlock() = isShowingAlternateAuthOnUnlock
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
new file mode 100644
index 0000000..5b431e7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+/** A [LaunchAnimator] to be used in tests. */
+fun fakeLaunchAnimator(): LaunchAnimator {
+    return LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+}
+
+/**
+ * A [LaunchAnimator.Timings] to be used in tests.
+ *
+ * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
+ * when computing the progress of a sub-animation (the contents fade in/out).
+ */
+private val TEST_TIMINGS =
+    LaunchAnimator.Timings(
+        totalDuration = 0L,
+        contentBeforeFadeOutDelay = 1L,
+        contentBeforeFadeOutDuration = 1L,
+        contentAfterFadeInDelay = 1L,
+        contentAfterFadeInDuration = 1L
+    )
+
+/** A [LaunchAnimator.Interpolators] to be used in tests. */
+private val TEST_INTERPOLATORS =
+    LaunchAnimator.Interpolators(
+        positionInterpolator = Interpolators.STANDARD,
+        positionXInterpolator = Interpolators.STANDARD,
+        contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
+        contentAfterFadeInInterpolator = Interpolators.STANDARD
+    )
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 5eec6e5..9a98f54 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -370,6 +370,10 @@
     private static final float DEFAULT_LOW_BATTERY_LEVEL = 20F;
     // Decide whether charging is required to turn on the feature
     private static final boolean DEFAULT_CHARGING_REQUIRED = true;
+    // Minimum GC interval sleep time in ms
+    private static final int DEFAULT_MIN_GC_SLEEPTIME = 10000;
+    // Target dirty segment ratio to aim to
+    private static final int DEFAULT_TARGET_DIRTY_RATIO = 80;
 
     private volatile int mLifetimePercentThreshold;
     private volatile int mMinSegmentsThreshold;
@@ -377,6 +381,8 @@
     private volatile float mSegmentReclaimWeight;
     private volatile float mLowBatteryLevel;
     private volatile boolean mChargingRequired;
+    private volatile int mMinGCSleepTime;
+    private volatile int mTargetDirtyRatio;
     private volatile boolean mNeedGC;
 
     private volatile boolean mPassedLifetimeThresh;
@@ -2712,6 +2718,10 @@
                 "low_battery_level", DEFAULT_LOW_BATTERY_LEVEL);
             mChargingRequired = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
                 "charging_required", DEFAULT_CHARGING_REQUIRED);
+            mMinGCSleepTime = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "min_gc_sleeptime", DEFAULT_MIN_GC_SLEEPTIME);
+            mTargetDirtyRatio = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "target_dirty_ratio", DEFAULT_TARGET_DIRTY_RATIO);
 
             // If we use the smart idle maintenance, we need to turn off GC in the traditional idle
             // maintenance to avoid the conflict
@@ -2829,6 +2839,14 @@
         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
 
         try {
+            int latestWrite = mVold.getWriteAmount();
+            if (latestWrite == -1) {
+                Slog.w(TAG, "Failed to get storage write record");
+                return;
+            }
+
+            updateStorageWriteRecords(latestWrite);
+
             // Block based checkpoint process runs fstrim. So, if checkpoint is in progress
             // (first boot after OTA), We skip the smart idle maintenance
             if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
@@ -2836,13 +2854,6 @@
                     return;
                 }
 
-                int latestWrite = mVold.getWriteAmount();
-                if (latestWrite == -1) {
-                    Slog.w(TAG, "Failed to get storage write record");
-                    return;
-                }
-
-                updateStorageWriteRecords(latestWrite);
                 int avgWriteAmount = getAverageWriteAmount();
 
                 Slog.i(TAG, "Set smart idle maintenance: " + "latest write amount: " +
@@ -2850,9 +2861,12 @@
                             ", min segment threshold: " + mMinSegmentsThreshold +
                             ", dirty reclaim rate: " + mDirtyReclaimRate +
                             ", segment reclaim weight: " + mSegmentReclaimWeight +
-                            ", period: " + sSmartIdleMaintPeriod);
+                            ", period(min): " + sSmartIdleMaintPeriod +
+                            ", min gc sleep time(ms): " + mMinGCSleepTime +
+                            ", target dirty ratio: " + mTargetDirtyRatio);
                 mVold.setGCUrgentPace(avgWriteAmount, mMinSegmentsThreshold, mDirtyReclaimRate,
-                                      mSegmentReclaimWeight, sSmartIdleMaintPeriod);
+                                      mSegmentReclaimWeight, sSmartIdleMaintPeriod,
+                                      mMinGCSleepTime, mTargetDirtyRatio);
             } else {
                 Slog.i(TAG, "Skipping smart idle maintenance - block based checkpoint in progress");
             }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 0cdf7bf..339d5d4fe 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -43,7 +43,6 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.SharedLibraryInfo;
-import android.content.pm.UserInfo;
 import android.content.pm.VersionedPackage;
 import android.net.Uri;
 import android.os.Binder;
@@ -164,17 +163,6 @@
                 return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
             }
 
-            if (PackageManagerServiceUtils.isSystemApp(uninstalledPs)) {
-                UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
-                if (userInfo == null || (!userInfo.isAdmin() && !mUserManagerInternal.getUserInfo(
-                        mUserManagerInternal.getProfileParentId(userId)).isAdmin())) {
-                    Slog.w(TAG, "Not removing package " + packageName
-                            + " as only admin user (or their profile) may downgrade system apps");
-                    EventLog.writeEvent(0x534e4554, "170646036", -1, packageName);
-                    return PackageManager.DELETE_FAILED_USER_RESTRICTED;
-                }
-            }
-
             disabledSystemPs = mPm.mSettings.getDisabledSystemPkgLPr(packageName);
             // Static shared libs can be declared by any package, so let us not
             // allow removing a package if it provides a lib others depend on.
diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
new file mode 100644
index 0000000..1497684
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.component;
+
+
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.server.SystemConfig;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Utility methods for handling the tag {@code <install-constraints/>}
+ *
+ * @hide
+ */
+public class InstallConstraintsTagParser {
+
+    private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix";
+
+    /**
+     * @hide
+     */
+    public static ParseResult<ParsingPackage> parseInstallConstraints(
+            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist();
+        if (!allowlist.contains(pkg.getPackageName())) {
+            return input.skip("install-constraints cannot be used by this package");
+        }
+
+        ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
+        if (prefixes.isSuccess()) {
+            if (validateFingerprintPrefixes(prefixes.getResult())) {
+                return input.success(pkg);
+            } else {
+                return input.skip(
+                        "Install of this package is restricted on this device; device fingerprint"
+                                + " does not start with one of the allowed prefixes");
+            }
+        }
+        return input.skip(prefixes.getErrorMessage());
+    }
+
+    private static ParseResult<Set<String>> parseFingerprintPrefixes(
+            ParseInput input, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        Set<String> prefixes = new ArraySet<>();
+        int type;
+        while (true) {
+            // move to the tag that contains the next prefix
+            type = parser.next();
+            if (type == XmlPullParser.END_TAG) {
+                if (prefixes.size() == 0) {
+                    return input.error("install-constraints must contain at least one constraint");
+                }
+                return input.success(prefixes);
+            } else if (type == XmlPullParser.START_TAG) {
+                if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
+                    ParseResult<String> parsedPrefix =
+                            readFingerprintPrefixValue(input, res, parser);
+                    if (parsedPrefix.isSuccess()) {
+                        prefixes.add(parsedPrefix.getResult());
+                    } else {
+                        return input.error(parsedPrefix.getErrorMessage());
+                    }
+                } else {
+                    return input.error("Unexpected tag: " + parser.getName());
+                }
+
+                // consume the end tag of this attribute
+                type = parser.next();
+                if (type != XmlPullParser.END_TAG) {
+                    return input.error("Expected end tag; instead got " + type);
+                }
+            }
+        }
+    }
+
+    private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res,
+            XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix);
+        try {
+            String value = sa.getString(
+                    R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value);
+            if (value == null) {
+                return input.error("Failed to specify prefix value");
+            }
+            return input.success(value);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static boolean validateFingerprintPrefixes(Set<String> prefixes) {
+        String fingerprint = Build.FINGERPRINT;
+        for (String prefix : prefixes) {
+            if (fingerprint.startsWith(prefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 9bfb40f..7ce7f7e 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -95,6 +95,7 @@
 import com.android.server.pm.permission.CompatibilityPermissionInfo;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.InstallConstraintsTagParser;
 import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.pkg.component.ParsedActivityUtils;
 import com.android.server.pm.pkg.component.ParsedApexSystemService;
@@ -169,9 +170,11 @@
 
     public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
     public static final String TAG_APPLICATION = "application";
+    public static final String TAG_ATTRIBUTION = "attribution";
     public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
     public static final String TAG_EAT_COMMENT = "eat-comment";
     public static final String TAG_FEATURE_GROUP = "feature-group";
+    public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints";
     public static final String TAG_INSTRUMENTATION = "instrumentation";
     public static final String TAG_KEY_SETS = "key-sets";
     public static final String TAG_MANIFEST = "manifest";
@@ -179,15 +182,16 @@
     public static final String TAG_OVERLAY = "overlay";
     public static final String TAG_PACKAGE = "package";
     public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
-    public static final String TAG_ATTRIBUTION = "attribution";
     public static final String TAG_PERMISSION = "permission";
     public static final String TAG_PERMISSION_GROUP = "permission-group";
     public static final String TAG_PERMISSION_TREE = "permission-tree";
+    public static final String TAG_PROFILEABLE = "profileable";
     public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
     public static final String TAG_QUERIES = "queries";
+    public static final String TAG_RECEIVER = "receiver";
     public static final String TAG_RESTRICT_UPDATE = "restrict-update";
-    public static final String TAG_SUPPORT_SCREENS = "supports-screens";
     public static final String TAG_SUPPORTS_INPUT = "supports-input";
+    public static final String TAG_SUPPORT_SCREENS = "supports-screens";
     public static final String TAG_USES_CONFIGURATION = "uses-configuration";
     public static final String TAG_USES_FEATURE = "uses-feature";
     public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
@@ -196,8 +200,6 @@
     public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
     public static final String TAG_USES_SDK = "uses-sdk";
     public static final String TAG_USES_SPLIT = "uses-split";
-    public static final String TAG_PROFILEABLE = "profileable";
-    public static final String TAG_RECEIVER = "receiver";
 
     public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
     public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
@@ -1040,6 +1042,8 @@
                 return input.success(pkg);
             case TAG_RESTRICT_UPDATE:
                 return parseRestrictUpdateHash(flags, input, pkg, res, parser);
+            case TAG_INSTALL_CONSTRAINTS:
+                return parseInstallConstraints(input, pkg, res, parser);
             case TAG_QUERIES:
                 return parseQueries(input, pkg, res, parser);
             default:
@@ -1729,6 +1733,12 @@
         return input.success(pkg);
     }
 
+    private static ParseResult<ParsingPackage> parseInstallConstraints(
+            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser);
+    }
+
     private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
             Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
         final int depth = parser.getDepth();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 3b25f28..8d94324 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -586,7 +586,10 @@
         }
 
         // Cannot embed activity across TaskFragments for activity result.
-        if (a.resultTo != null && a.resultTo.getTaskFragment() != this) {
+        // If the activity that started for result is finishing, it's likely that this start mode
+        // is used to place an activity in the same task. Since the finishing activity won't be
+        // able to get the results, so it's OK to embed in a different TaskFragment.
+        if (a.resultTo != null && !a.resultTo.finishing && a.resultTo.getTaskFragment() != this) {
             return EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
         }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
deleted file mode 100644
index e30f3d2..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm
-
-import android.content.pm.PackageManager
-import android.content.pm.UserInfo
-import android.os.Build
-import android.util.Log
-import com.android.server.testutils.any
-import com.android.server.testutils.spy
-import com.android.server.testutils.whenever
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mockito.doAnswer
-
-@RunWith(JUnit4::class)
-class DeletePackageHelperTest {
-
-    @Rule
-    @JvmField
-    val rule = MockSystemRule()
-
-    private lateinit var mPms: PackageManagerService
-    private lateinit var mUserManagerInternal: UserManagerInternal
-
-    @Before
-    @Throws(Exception::class)
-    fun setup() {
-        Log.i("system.out", "setup", Exception())
-        rule.system().stageNominalSystemState()
-        rule.system().stageScanExistingPackage(
-            "a.data.package", 1L, rule.system().dataAppDirectory)
-
-        mUserManagerInternal = rule.mocks().injector.userManagerInternal
-        whenever(mUserManagerInternal.getUserIds()).thenReturn(intArrayOf(0, 1))
-
-        mPms = createPackageManagerService()
-        doAnswer { false }.`when`(mPms).isPackageDeviceAdmin(any(), any())
-        doAnswer { null }.`when`(mPms).freezePackageForDelete(any(), any(), any(), any())
-    }
-
-    private fun createPackageManagerService(): PackageManagerService {
-        return spy(PackageManagerService(rule.mocks().injector,
-            false /*coreOnly*/,
-            false /*factoryTest*/,
-            MockSystem.DEFAULT_VERSION_INFO.fingerprint,
-            false /*isEngBuild*/,
-            false /*isUserDebugBuild*/,
-            Build.VERSION_CODES.CUR_DEVELOPMENT,
-            Build.VERSION.INCREMENTAL))
-    }
-
-    @Test
-    fun deleteSystemPackageFailsIfNotAdminAndNotProfile() {
-        val ps = mPms.mSettings.getPackageLPr("a.data.package")
-        whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
-        whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0))
-        whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1)
-
-        val dph = DeletePackageHelper(mPms)
-        val result = dph.deletePackageX("a.data.package", 1L, 1,
-            PackageManager.DELETE_SYSTEM_APP, false)
-
-        assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
-    }
-
-    @Test
-    fun deleteSystemPackageFailsIfProfileOfNonAdmin() {
-        val userId = 1
-        val parentId = 5
-        val ps = mPms.mSettings.getPackageLPr("a.data.package")
-        whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
-        whenever(mUserManagerInternal.getUserInfo(userId)).thenReturn(
-            UserInfo(userId, "test", UserInfo.FLAG_PROFILE))
-        whenever(mUserManagerInternal.getProfileParentId(userId)).thenReturn(parentId)
-        whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn(
-            UserInfo(userId, "testparent", 0))
-
-        val dph = DeletePackageHelper(mPms)
-        val result = dph.deletePackageX("a.data.package", 1L, userId,
-            PackageManager.DELETE_SYSTEM_APP, false)
-
-        assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
-    }
-
-    @Test
-    fun deleteSystemPackageSucceedsIfAdmin() {
-        val ps = mPms.mSettings.getPackageLPr("a.data.package")
-        whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
-        whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(
-            UserInfo(1, "test", UserInfo.FLAG_ADMIN))
-
-        val dph = DeletePackageHelper(mPms)
-        val result = dph.deletePackageX("a.data.package", 1L, 1,
-            PackageManager.DELETE_SYSTEM_APP, false)
-
-        assertThat(result).isEqualTo(PackageManager.DELETE_SUCCEEDED)
-    }
-
-    @Test
-    fun deleteSystemPackageSucceedsIfProfileOfAdmin() {
-        val userId = 1
-        val parentId = 5
-        val ps = mPms.mSettings.getPackageLPr("a.data.package")
-        whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
-        whenever(mUserManagerInternal.getUserInfo(userId)).thenReturn(
-            UserInfo(userId, "test", UserInfo.FLAG_PROFILE))
-        whenever(mUserManagerInternal.getProfileParentId(userId)).thenReturn(parentId)
-        whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn(
-            UserInfo(userId, "testparent", UserInfo.FLAG_ADMIN))
-
-        val dph = DeletePackageHelper(mPms)
-        val result = dph.deletePackageX("a.data.package", 1L, userId,
-            PackageManager.DELETE_SYSTEM_APP, false)
-
-        assertThat(result).isEqualTo(PackageManager.DELETE_SUCCEEDED)
-    }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index e9171c0c..92c7871 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -336,6 +336,59 @@
         assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
     }
 
+    /**
+     * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+     */
+    @Test
+    public void readPermissions_installConstraints_successful() throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <install-constraints-allowed package=\"com.android.apex1\" />\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getInstallConstraintsAllowlist())
+                .containsExactly("com.android.apex1");
+    }
+
+    /**
+     * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+     */
+    @Test
+    public void readPermissions_installConstraints_noPackage() throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <install-constraints-allowed/>\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+    }
+
+    /**
+     * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed}
+     * without {@link SystemConfig#ALLOW_VENDOR_APEX}.
+     */
+    @Test
+    public void readPermissions_installConstraints_noAppConfigs() throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <install-constraints-allowed package=\"com.android.apex1\" />\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+        readPermissions(folder,  /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
+
+        assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+    }
+
     @Test
     public void readApexPrivAppPermissions_addAllPermissions()
             throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 1096351..88eadfc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -32,6 +32,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
@@ -468,6 +469,10 @@
         newActivity.resultTo = activity;
         assertEquals(EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT,
                 newTaskFragment.isAllowedToEmbedActivity(newActivity));
+
+        // Allow embedding if the resultTo activity is finishing.
+        activity.finishing = true;
+        assertEquals(EMBEDDING_ALLOWED, newTaskFragment.isAllowedToEmbedActivity(newActivity));
     }
 
     @Test
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
index 41f9fae..6fc4b67 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -31,15 +31,14 @@
     public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
 
         logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
-        if (UsbPortHidl.isServicePresent(null)) {
-            logAndPrint(Log.INFO, null, "USB HAL HIDL present");
-            return new UsbPortHidl(portManager, pw);
-        }
         if (UsbPortAidl.isServicePresent(null)) {
             logAndPrint(Log.INFO, null, "USB HAL AIDL present");
             return new UsbPortAidl(portManager, pw);
         }
-
+        if (UsbPortHidl.isServicePresent(null)) {
+            logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+            return new UsbPortHidl(portManager, pw);
+        }
         return null;
     }
 }
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index d432341..948b11b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -477,6 +477,7 @@
   manifest_action["compatible-screens"]["screen"];
   manifest_action["supports-gl-texture"];
   manifest_action["restrict-update"];
+  manifest_action["install-constraints"]["fingerprint-prefix"];
   manifest_action["package-verifier"];
   manifest_action["meta-data"] = meta_data_action;
   manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);