Merge "Fix media overlaying ordering."
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bdd9ec7..e2690a9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2970,6 +2970,7 @@
 
   public final class AccessibilityManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
+    method public boolean hasAnyDirectConnection();
   }
 
   public class AccessibilityNodeInfo implements android.os.Parcelable {
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 0d6b199..9abce3a 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -146,4 +146,8 @@
     void onDoubleTapAndHold(int displayId);
 
     void setAnimationScale(float scale);
+
+    void setInstalledAndEnabledServices(in List<AccessibilityServiceInfo> infos);
+
+    List<AccessibilityServiceInfo> getInstalledAndEnabledServices();
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6c61d4bd..1e2b241 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10497,6 +10497,8 @@
             if (mDirectConnectionId == AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID) {
                 mDirectConnectionId = AccessibilityInteractionClient.addDirectConnection(
                         new AccessibilityInteractionConnection(ViewRootImpl.this));
+                // Notify listeners in the app process.
+                mAccessibilityManager.notifyAccessibilityStateChanged();
             }
             return mDirectConnectionId;
         }
@@ -10505,6 +10507,8 @@
             if (mDirectConnectionId != AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID) {
                 AccessibilityInteractionClient.removeConnection(mDirectConnectionId);
                 mDirectConnectionId = AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID;
+                // Notify listeners in the app process.
+                mAccessibilityManager.notifyAccessibilityStateChanged();
             }
         }
     }
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 227a8ef..e3ffc9d 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -117,6 +117,7 @@
     // Used to generate connection ids for direct app-process connections. Start sufficiently far
     // enough from the connection ids generated by AccessibilityManagerService.
     private static int sDirectConnectionIdCounter = 1 << 30;
+    private static int sDirectConnectionCount = 0;
 
     /** List of timestamps which indicate the latest time an a11y service receives a scroll event
         from a window, mapping from windowId -> timestamp. */
@@ -272,12 +273,18 @@
             DirectAccessibilityConnection directAccessibilityConnection =
                     new DirectAccessibilityConnection(connection);
             sConnectionCache.put(connectionId, directAccessibilityConnection);
+            sDirectConnectionCount++;
             // Do not use AccessibilityCache for this connection, since there is no corresponding
             // AccessibilityService to handle cache invalidation events.
             return connectionId;
         }
     }
 
+    /** Check if any {@link DirectAccessibilityConnection} is currently in the connection cache. */
+    public static boolean hasAnyDirectConnection() {
+        return sDirectConnectionCount > 0;
+    }
+
     /**
      * Gets a cached associated with the connection id if available.
      *
@@ -295,6 +302,9 @@
      */
     public static void removeConnection(int connectionId) {
         synchronized (sConnectionCache) {
+            if (getConnection(connectionId) instanceof DirectAccessibilityConnection) {
+                sDirectConnectionCount--;
+            }
             sConnectionCache.remove(connectionId);
             sCaches.remove(connectionId);
         }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 7528e2a..5433fa0 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -602,12 +602,21 @@
      */
     public boolean isEnabled() {
         synchronized (mLock) {
-            return mIsEnabled || (mAccessibilityPolicy != null
-                    && mAccessibilityPolicy.isEnabled(mIsEnabled));
+            return mIsEnabled || hasAnyDirectConnection()
+                    || (mAccessibilityPolicy != null && mAccessibilityPolicy.isEnabled(mIsEnabled));
         }
     }
 
     /**
+     * @see AccessibilityInteractionClient#hasAnyDirectConnection
+     * @hide
+     */
+    @TestApi
+    public boolean hasAnyDirectConnection() {
+        return AccessibilityInteractionClient.hasAnyDirectConnection();
+    }
+
+    /**
      * Returns if the touch exploration in the system is enabled.
      * <p>
      * <b>Note:</b> This query is used for dispatching hover events, such as
@@ -1942,8 +1951,13 @@
 
     /**
      * Notifies the registered {@link AccessibilityStateChangeListener}s.
+     *
+     * Note: this method notifies only the listeners of this single instance.
+     * AccessibilityManagerService is responsible for calling this method on all of
+     * its AccessibilityManager clients in order to notify all listeners.
+     * @hide
      */
-    private void notifyAccessibilityStateChanged() {
+    public void notifyAccessibilityStateChanged() {
         final boolean isEnabled;
         final ArrayMap<AccessibilityStateChangeListener, Handler> listeners;
         synchronized (mLock) {
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 1e06098..36fdcce4 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -108,4 +108,10 @@
     void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId);
 
     oneway void setAccessibilityWindowAttributes(int displayId, int windowId, int userId, in AccessibilityWindowAttributes attributes);
+
+    // Requires Manifest.permission.MANAGE_ACCESSIBILITY
+    boolean registerProxyForDisplay(IAccessibilityServiceClient proxy, int displayId);
+
+    // Requires Manifest.permission.MANAGE_ACCESSIBILITY
+    boolean unregisterProxyForDisplay(int displayId);
 }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index fc385a0..35d5948 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -26,6 +26,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteCallback;
+import android.os.RemoteException;
 
 import java.util.Collections;
 import java.util.List;
@@ -206,4 +207,14 @@
             int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {}
 
     public void setAnimationScale(float scale) {}
+
+    @Override
+    public void setInstalledAndEnabledServices(List<AccessibilityServiceInfo> infos)
+            throws RemoteException {
+    }
+
+    @Override
+    public List<AccessibilityServiceInfo> getInstalledAndEnabledServices() throws RemoteException {
+        return null;
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index c76f568..0fb6ff8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -103,14 +103,23 @@
     /**
      * Similar to {@link #addWindowLayoutInfoListener(Activity, Consumer)}, but takes a UI Context
      * as a parameter.
+     *
+     * Jetpack {@link androidx.window.layout.ExtensionWindowLayoutInfoBackend} makes sure all
+     * consumers related to the same {@link Context} gets updated {@link WindowLayoutInfo}
+     * together. However only the first registered consumer of a {@link Context} will actually
+     * invoke {@link #addWindowLayoutInfoListener(Context, Consumer)}.
+     * Here we enforce that {@link #addWindowLayoutInfoListener(Context, Consumer)} can only be
+     * called once for each {@link Context}.
      */
-    // TODO(b/204073440): Add @Override to hook the API in WM extensions library.
+    @Override
     public void addWindowLayoutInfoListener(@NonNull @UiContext Context context,
             @NonNull Consumer<WindowLayoutInfo> consumer) {
         if (mWindowLayoutChangeListeners.containsKey(context)
+                // In theory this method can be called on the same consumer with different context.
                 || mWindowLayoutChangeListeners.containsValue(consumer)) {
-            // Early return if the listener or consumer has been registered.
-            return;
+            throw new IllegalArgumentException(
+                    "Context or Consumer has already been registered for WindowLayoutInfo"
+                            + " callback.");
         }
         if (!context.isUiContext()) {
             throw new IllegalArgumentException("Context must be a UI Context, which should be"
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 2c766d8..b0b95f9 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 9d7b01c..49ef330 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -59,4 +59,5 @@
     <dimen name="large_dialog_width">348dp</dimen>
 
     <dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen>
+    <dimen name="qs_panel_padding_top_combined_headers">@dimen/qs_panel_padding_top</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 5dcbeb5..599bf30 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -68,6 +68,7 @@
     <dimen name="qs_security_footer_background_inset">0dp</dimen>
 
     <dimen name="qs_panel_padding_top">8dp</dimen>
+    <dimen name="qs_panel_padding_top_combined_headers">@dimen/qs_panel_padding_top</dimen>
 
     <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
     <dimen name="large_dialog_width">472dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8d49283..03040d6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -559,7 +559,8 @@
     <dimen name="qs_dual_tile_padding_horizontal">6dp</dimen>
     <dimen name="qs_panel_elevation">4dp</dimen>
     <dimen name="qs_panel_padding_bottom">@dimen/footer_actions_height</dimen>
-    <dimen name="qs_panel_padding_top">80dp</dimen>
+    <dimen name="qs_panel_padding_top">48dp</dimen>
+    <dimen name="qs_panel_padding_top_combined_headers">80dp</dimen>
 
     <dimen name="qs_data_usage_text_size">14sp</dimen>
     <dimen name="qs_data_usage_usage_text_size">36sp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 453072b..5d86ccd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -176,6 +176,8 @@
 
     @Override
     public void startAppearAnimation() {
+        setAlpha(1f);
+        setTranslationY(0);
         if (mAppearAnimator.isRunning()) {
             mAppearAnimator.cancel();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 632fcd1..353c369 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -538,7 +538,7 @@
     }
 
     /**
-     * Runs after a succsssful authentication only
+     * Runs after a successful authentication only
      */
     public void startDisappearAnimation(SecurityMode securitySelection) {
         mDisappearAnimRunning = true;
@@ -1063,6 +1063,7 @@
                 mPopup.dismiss();
                 mPopup = null;
             }
+            setupUserSwitcher();
         }
 
         @Override
@@ -1101,7 +1102,6 @@
                 return;
             }
 
-            mView.setAlpha(1f);
             mUserSwitcherViewGroup.setAlpha(0f);
             ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
                     1f);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 57058b7..d448f40 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -427,6 +427,7 @@
 
     public void startAppearAnimation() {
         if (mCurrentSecurityMode != SecurityMode.None) {
+            mView.setAlpha(1f);
             mView.startAppearAnimation(mCurrentSecurityMode);
             getCurrentSecurityController().startAppearAnimation();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 9cd149b..5694f6d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -18,7 +18,7 @@
 
 import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATIONS_FADE_IN_DURATION;
 import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATIONS_FADE_OUT_DURATION;
-import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATION_MARGIN;
+import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATION_MARGIN_DEFAULT;
 import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.SCOPED_COMPLICATIONS_LAYOUT;
 
 import android.animation.Animator;
@@ -67,7 +67,7 @@
         private final Parent mParent;
         @Complication.Category
         private final int mCategory;
-        private final int mMargin;
+        private final int mDefaultMargin;
 
         /**
          * Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding
@@ -75,7 +75,7 @@
          */
         ViewEntry(View view, ComplicationLayoutParams layoutParams,
                 TouchInsetManager.TouchInsetSession touchSession, int category, Parent parent,
-                int margin) {
+                int defaultMargin) {
             mView = view;
             // Views that are generated programmatically do not have a unique id assigned to them
             // at construction. A new id is assigned here to enable ConstraintLayout relative
@@ -86,7 +86,7 @@
             mTouchInsetSession = touchSession;
             mCategory = category;
             mParent = parent;
-            mMargin = margin;
+            mDefaultMargin = defaultMargin;
 
             touchSession.addViewToTracking(mView);
         }
@@ -195,18 +195,19 @@
                 }
 
                 if (!isRoot) {
+                    final int margin = mLayoutParams.getMargin(mDefaultMargin);
                     switch(direction) {
                         case ComplicationLayoutParams.DIRECTION_DOWN:
-                            params.setMargins(0, mMargin, 0, 0);
+                            params.setMargins(0, margin, 0, 0);
                             break;
                         case ComplicationLayoutParams.DIRECTION_UP:
-                            params.setMargins(0, 0, 0, mMargin);
+                            params.setMargins(0, 0, 0, margin);
                             break;
                         case ComplicationLayoutParams.DIRECTION_END:
-                            params.setMarginStart(mMargin);
+                            params.setMarginStart(margin);
                             break;
                         case ComplicationLayoutParams.DIRECTION_START:
-                            params.setMarginEnd(mMargin);
+                            params.setMarginEnd(margin);
                             break;
                     }
                 }
@@ -263,7 +264,7 @@
             private final ComplicationLayoutParams mLayoutParams;
             private final int mCategory;
             private Parent mParent;
-            private int mMargin;
+            private int mDefaultMargin;
 
             Builder(View view, TouchInsetManager.TouchInsetSession touchSession,
                     ComplicationLayoutParams lp, @Complication.Category int category) {
@@ -302,8 +303,8 @@
              * Sets the margin that will be applied in the direction the complication is laid out
              * towards.
              */
-            Builder setMargin(int margin) {
-                mMargin = margin;
+            Builder setDefaultMargin(int margin) {
+                mDefaultMargin = margin;
                 return this;
             }
 
@@ -312,7 +313,7 @@
              */
             ViewEntry build() {
                 return new ViewEntry(mView, mLayoutParams, mTouchSession, mCategory, mParent,
-                        mMargin);
+                        mDefaultMargin);
             }
         }
 
@@ -472,7 +473,7 @@
     }
 
     private final ConstraintLayout mLayout;
-    private final int mMargin;
+    private final int mDefaultMargin;
     private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>();
     private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>();
     private final TouchInsetManager.TouchInsetSession mSession;
@@ -483,12 +484,12 @@
     /** */
     @Inject
     public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout,
-            @Named(COMPLICATION_MARGIN) int margin,
+            @Named(COMPLICATION_MARGIN_DEFAULT) int defaultMargin,
             TouchInsetManager.TouchInsetSession session,
             @Named(COMPLICATIONS_FADE_IN_DURATION) int fadeInDuration,
             @Named(COMPLICATIONS_FADE_OUT_DURATION) int fadeOutDuration) {
         mLayout = layout;
-        mMargin = margin;
+        mDefaultMargin = defaultMargin;
         mSession = session;
         mFadeInDuration = fadeInDuration;
         mFadeOutDuration = fadeOutDuration;
@@ -537,7 +538,7 @@
         }
 
         final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, mSession, lp, category)
-                .setMargin(mMargin);
+                .setDefaultMargin(mDefaultMargin);
 
         // Add position group if doesn't already exist
         final int position = lp.getPosition();
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
index 8e8cb72..a21eb19 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -51,6 +51,8 @@
     private static final int FIRST_POSITION = POSITION_TOP;
     private static final int LAST_POSITION = POSITION_END;
 
+    private static final int MARGIN_UNSPECIFIED = 0xFFFFFFFF;
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "DIRECTION_" }, value = {
             DIRECTION_UP,
@@ -77,6 +79,8 @@
 
     private final int mWeight;
 
+    private final int mMargin;
+
     private final boolean mSnapToGuide;
 
     // Do not allow specifying opposite positions
@@ -106,7 +110,24 @@
      */
     public ComplicationLayoutParams(int width, int height, @Position int position,
             @Direction int direction, int weight) {
-        this(width, height, position, direction, weight, false);
+        this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, false);
+    }
+
+    /**
+     * Constructs a {@link ComplicationLayoutParams}.
+     * @param width The width {@link android.view.View.MeasureSpec} for the view.
+     * @param height The height {@link android.view.View.MeasureSpec} for the view.
+     * @param position The place within the parent container where the view should be positioned.
+     * @param direction The direction the view should be laid out from either the parent container
+     *                  or preceding view.
+     * @param weight The weight that should be considered for this view when compared to other
+     *               views. This has an impact on the placement of the view but not the rendering of
+     *               the view.
+     * @param margin The margin to apply between complications.
+     */
+    public ComplicationLayoutParams(int width, int height, @Position int position,
+            @Direction int direction, int weight, int margin) {
+        this(width, height, position, direction, weight, margin, false);
     }
 
     /**
@@ -127,6 +148,28 @@
      */
     public ComplicationLayoutParams(int width, int height, @Position int position,
             @Direction int direction, int weight, boolean snapToGuide) {
+        this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, snapToGuide);
+    }
+
+    /**
+     * Constructs a {@link ComplicationLayoutParams}.
+     * @param width The width {@link android.view.View.MeasureSpec} for the view.
+     * @param height The height {@link android.view.View.MeasureSpec} for the view.
+     * @param position The place within the parent container where the view should be positioned.
+     * @param direction The direction the view should be laid out from either the parent container
+     *                  or preceding view.
+     * @param weight The weight that should be considered for this view when compared to other
+     *               views. This has an impact on the placement of the view but not the rendering of
+     *               the view.
+     * @param margin The margin to apply between complications.
+     * @param snapToGuide When set to {@code true}, the dimension perpendicular to the direction
+     *                    will be automatically set to align with a predetermined guide for that
+     *                    side. For example, if the complication is aligned to the top end and
+     *                    direction is down, then the width of the complication will be set to span
+     *                    from the end of the parent to the guide.
+     */
+    public ComplicationLayoutParams(int width, int height, @Position int position,
+            @Direction int direction, int weight, int margin, boolean snapToGuide) {
         super(width, height);
 
         if (!validatePosition(position)) {
@@ -142,6 +185,8 @@
 
         mWeight = weight;
 
+        mMargin = margin;
+
         mSnapToGuide = snapToGuide;
     }
 
@@ -153,6 +198,7 @@
         mPosition = source.mPosition;
         mDirection = source.mDirection;
         mWeight = source.mWeight;
+        mMargin = source.mMargin;
         mSnapToGuide = source.mSnapToGuide;
     }
 
@@ -215,6 +261,14 @@
     }
 
     /**
+     * Returns the margin to apply between complications, or the given default if no margin is
+     * specified.
+     */
+    public int getMargin(int defaultMargin) {
+        return mMargin == MARGIN_UNSPECIFIED ? defaultMargin : mMargin;
+    }
+
+    /**
      * Returns whether the complication's dimension perpendicular to direction should be
      * automatically set.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewModule.java
index 11d89d2..c9fecc9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewModule.java
@@ -37,7 +37,7 @@
 @Module
 public abstract class ComplicationHostViewModule {
     public static final String SCOPED_COMPLICATIONS_LAYOUT = "scoped_complications_layout";
-    public static final String COMPLICATION_MARGIN = "complication_margin";
+    public static final String COMPLICATION_MARGIN_DEFAULT = "complication_margin_default";
     public static final String COMPLICATIONS_FADE_OUT_DURATION = "complications_fade_out_duration";
     public static final String COMPLICATIONS_FADE_IN_DURATION = "complications_fade_in_duration";
     public static final String COMPLICATIONS_RESTORE_TIMEOUT = "complication_restore_timeout";
@@ -58,7 +58,7 @@
     }
 
     @Provides
-    @Named(COMPLICATION_MARGIN)
+    @Named(COMPLICATION_MARGIN_DEFAULT)
     @DreamOverlayComponent.DreamOverlayScope
     static int providesComplicationPadding(@Main Resources resources) {
         return resources.getDimensionPixelSize(R.dimen.dream_overlay_complication_margin);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index 759d6ec..7d2ce51 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -59,11 +59,11 @@
     @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS)
     static ComplicationLayoutParams provideClockTimeLayoutParams() {
         return new ComplicationLayoutParams(0,
-            ViewGroup.LayoutParams.WRAP_CONTENT,
-            ComplicationLayoutParams.POSITION_TOP
-                    | ComplicationLayoutParams.POSITION_START,
-            ComplicationLayoutParams.DIRECTION_DOWN,
-            DREAM_CLOCK_TIME_COMPLICATION_WEIGHT);
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ComplicationLayoutParams.POSITION_TOP
+                        | ComplicationLayoutParams.POSITION_START,
+                ComplicationLayoutParams.DIRECTION_DOWN,
+                DREAM_CLOCK_TIME_COMPLICATION_WEIGHT);
     }
 
     /**
@@ -73,12 +73,12 @@
     @Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS)
     static ComplicationLayoutParams provideHomeControlsChipLayoutParams(@Main Resources res) {
         return new ComplicationLayoutParams(
-            res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
-            res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
-            ComplicationLayoutParams.POSITION_BOTTOM
-                    | ComplicationLayoutParams.POSITION_START,
-            ComplicationLayoutParams.DIRECTION_END,
-            DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
+                res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
+                res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
+                ComplicationLayoutParams.POSITION_BOTTOM
+                        | ComplicationLayoutParams.POSITION_START,
+                ComplicationLayoutParams.DIRECTION_END,
+                DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
     }
 
     /**
@@ -103,11 +103,12 @@
     @Named(DREAM_SMARTSPACE_LAYOUT_PARAMS)
     static ComplicationLayoutParams provideSmartspaceLayoutParams() {
         return new ComplicationLayoutParams(0,
-            ViewGroup.LayoutParams.WRAP_CONTENT,
-            ComplicationLayoutParams.POSITION_TOP
-                    | ComplicationLayoutParams.POSITION_START,
-            ComplicationLayoutParams.DIRECTION_DOWN,
-            DREAM_SMARTSPACE_COMPLICATION_WEIGHT,
-            true /*snapToGuide*/);
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ComplicationLayoutParams.POSITION_TOP
+                        | ComplicationLayoutParams.POSITION_START,
+                ComplicationLayoutParams.DIRECTION_DOWN,
+                DREAM_SMARTSPACE_COMPLICATION_WEIGHT,
+                0,
+                true /*snapToGuide*/);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 3789cbb..029cf68 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -142,6 +142,9 @@
         boolean isOldConfigTablet = mIsTablet;
         mIsTablet = isTablet(mContext);
         boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
+        if (mTaskbarDelegate.isInitialized()) {
+            mTaskbarDelegate.onConfigurationChanged(newConfig);
+        }
         // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
         if (largeScreenChanged && updateNavbarForTaskbar()) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 9e0c496..73fc21e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -40,7 +40,6 @@
 
 import android.app.StatusBarManager;
 import android.app.StatusBarManager.WindowVisibleState;
-import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -87,7 +86,7 @@
 @SysUISingleton
 public class TaskbarDelegate implements CommandQueue.Callbacks,
         OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
-        ComponentCallbacks, Dumpable {
+        Dumpable {
     private static final String TAG = TaskbarDelegate.class.getSimpleName();
 
     private final EdgeBackGestureHandler mEdgeBackGestureHandler;
@@ -225,7 +224,6 @@
         // Initialize component callback
         Display display = mDisplayManager.getDisplay(displayId);
         mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
-        mWindowContext.registerComponentCallbacks(this);
         mScreenPinningNotify = new ScreenPinningNotify(mWindowContext);
         // Set initial state for any listeners
         updateSysuiFlags();
@@ -233,6 +231,7 @@
         mLightBarController.setNavigationBar(mLightBarTransitionsController);
         mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
         mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
+        mEdgeBackGestureHandler.onConfigurationChanged(mContext.getResources().getConfiguration());
         mInitialized = true;
     }
 
@@ -247,10 +246,7 @@
         mNavBarHelper.destroy();
         mEdgeBackGestureHandler.onNavBarDetached();
         mScreenPinningNotify = null;
-        if (mWindowContext != null) {
-            mWindowContext.unregisterComponentCallbacks(this);
-            mWindowContext = null;
-        }
+        mWindowContext = null;
         mAutoHideController.setNavigationBar(null);
         mLightBarTransitionsController.destroy();
         mLightBarController.setNavigationBar(null);
@@ -267,8 +263,9 @@
     }
 
     /**
-     * Returns {@code true} if this taskBar is {@link #init(int)}. Returns {@code false} if this
-     * taskbar has not yet been {@link #init(int)} or has been {@link #destroy()}.
+     * Returns {@code true} if this taskBar is {@link #init(int)}.
+     * Returns {@code false} if this taskbar has not yet been {@link #init(int)}
+     * or has been {@link #destroy()}.
      */
     public boolean isInitialized() {
         return mInitialized;
@@ -460,15 +457,11 @@
         return mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
     }
 
-    @Override
     public void onConfigurationChanged(Configuration configuration) {
         mEdgeBackGestureHandler.onConfigurationChanged(configuration);
     }
 
     @Override
-    public void onLowMemory() {}
-
-    @Override
     public void showPinningEnterExitToast(boolean entering) {
         updateSysuiFlags();
         if (mScreenPinningNotify == null) {
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 b26b42c..a8799c7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -19,6 +19,7 @@
 
 import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -955,7 +956,7 @@
                 mStartingQuickstepRotation != rotation;
     }
 
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
         if (mStartingQuickstepRotation > -1) {
             updateDisabledForQuickstep(newConfig);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 184089f7..6517ff3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -105,6 +105,7 @@
     private final Rect mClippingRect = new Rect();
     private ViewGroup mMediaHostView;
     private boolean mShouldMoveMediaOnExpansion = true;
+    private boolean mUsingCombinedHeaders = false;
 
     public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -148,6 +149,10 @@
         }
     }
 
+    void setUsingCombinedHeaders(boolean usingCombinedHeaders) {
+        mUsingCombinedHeaders = usingCombinedHeaders;
+    }
+
     protected void setHorizontalContentContainerClipping() {
         mHorizontalContentContainer.setClipChildren(true);
         mHorizontalContentContainer.setClipToPadding(false);
@@ -371,7 +376,9 @@
 
     protected void updatePadding() {
         final Resources res = mContext.getResources();
-        int paddingTop = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
+        int paddingTop = res.getDimensionPixelSize(
+                mUsingCombinedHeaders ? R.dimen.qs_panel_padding_top_combined_headers
+                        : R.dimen.qs_panel_padding_top);
         int paddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
         setPaddingRelative(getPaddingStart(),
                 paddingTop,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 18bd6b7..f6db775 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
+import static com.android.systemui.flags.Flags.COMBINED_QS_HEADERS;
 import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
 import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
@@ -27,6 +28,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.media.MediaHostState;
@@ -79,7 +81,8 @@
             QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
             BrightnessSliderController.Factory brightnessSliderFactory,
             FalsingManager falsingManager,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            FeatureFlags featureFlags) {
         super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
                 metricsLogger, uiEventLogger, qsLogger, dumpManager);
         mTunerService = tunerService;
@@ -93,6 +96,7 @@
         mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
         mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mView.setUsingCombinedHeaders(featureFlags.isEnabled(COMBINED_QS_HEADERS));
     }
 
     @Override
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 bc943cf..d4fa1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -407,8 +407,9 @@
         } else if (mNotificationPanelViewController.isUnlockHintRunning()) {
             if (mBouncer != null) {
                 mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+            } else {
+                mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
             }
-            mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
         } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
             // Don't expand to the bouncer. Instead transition back to the lock screen (see
             // CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
@@ -416,8 +417,9 @@
         } else if (bouncerNeedsScrimming()) {
             if (mBouncer != null) {
                 mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+            } else {
+                mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
             }
-            mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
         } else if (mShowing && !hideBouncerOverDream) {
             if (!isWakeAndUnlocking()
                     && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
@@ -425,8 +427,9 @@
                     && !isUnlockCollapsing()) {
                 if (mBouncer != null) {
                     mBouncer.setExpansion(fraction);
+                } else {
+                    mBouncerInteractor.setExpansion(fraction);
                 }
-                mBouncerInteractor.setExpansion(fraction);
             }
             if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
                     && !mKeyguardStateController.canDismissLockScreen()
@@ -434,16 +437,18 @@
                     && !bouncerIsAnimatingAway()) {
                 if (mBouncer != null) {
                     mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
+                } else {
+                    mBouncerInteractor.show(/* isScrimmed= */false);
                 }
-                mBouncerInteractor.show(/* isScrimmed= */false);
             }
         } else if (!mShowing && isBouncerInTransit()) {
             // Keyguard is not visible anymore, but expansion animation was still running.
             // We need to hide the bouncer, otherwise it will be stuck in transit.
             if (mBouncer != null) {
                 mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+            } else {
+                mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
             }
-            mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
         } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
             // Panel expanded while pulsing but didn't translate the bouncer (because we are
             // unlocked.) Let's simply wake-up to dismiss the lock screen.
@@ -489,8 +494,9 @@
             mCentralSurfaces.hideKeyguard();
             if (mBouncer != null) {
                 mBouncer.show(true /* resetSecuritySelection */);
+            } else {
+                mBouncerInteractor.show(true);
             }
-            mBouncerInteractor.show(true);
         } else {
             mCentralSurfaces.showKeyguard();
             if (hideBouncerWhenShowing) {
@@ -531,8 +537,9 @@
     void hideBouncer(boolean destroyView) {
         if (mBouncer != null) {
             mBouncer.hide(destroyView);
+        } else {
+            mBouncerInteractor.hide();
         }
-        mBouncerInteractor.hide();
         if (mShowing) {
             // If we were showing the bouncer and then aborting, we need to also clear out any
             // potential actions unless we actually unlocked.
@@ -553,8 +560,9 @@
         if (mShowing && !isBouncerShowing()) {
             if (mBouncer != null) {
                 mBouncer.show(false /* resetSecuritySelection */, scrimmed);
+            } else {
+                mBouncerInteractor.show(scrimmed);
             }
-            mBouncerInteractor.show(scrimmed);
         }
         updateStates();
     }
@@ -590,9 +598,10 @@
                         if (mBouncer != null) {
                             mBouncer.setDismissAction(mAfterKeyguardGoneAction,
                                     mKeyguardGoneCancelAction);
+                        } else {
+                            mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
+                                    mKeyguardGoneCancelAction);
                         }
-                        mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
-                                mKeyguardGoneCancelAction);
                         mAfterKeyguardGoneAction = null;
                         mKeyguardGoneCancelAction = null;
                     }
@@ -605,17 +614,21 @@
                 if (afterKeyguardGone) {
                     // we'll handle the dismiss action after keyguard is gone, so just show the
                     // bouncer
-                    mBouncerInteractor.show(/* isScrimmed= */true);
-                    if (mBouncer != null) mBouncer.show(false /* resetSecuritySelection */);
+                    if (mBouncer != null) {
+                        mBouncer.show(false /* resetSecuritySelection */);
+                    } else {
+                        mBouncerInteractor.show(/* isScrimmed= */true);
+                    }
                 } else {
                     // after authentication success, run dismiss action with the option to defer
                     // hiding the keyguard based on the return value of the OnDismissAction
-                    mBouncerInteractor.setDismissAction(
-                            mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
-                    mBouncerInteractor.show(/* isScrimmed= */true);
                     if (mBouncer != null) {
                         mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
                                 mKeyguardGoneCancelAction);
+                    } else {
+                        mBouncerInteractor.setDismissAction(
+                                mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
+                        mBouncerInteractor.show(/* isScrimmed= */true);
                     }
                     // bouncer will handle the dismiss action, so we no longer need to track it here
                     mAfterKeyguardGoneAction = null;
@@ -719,8 +732,9 @@
     public void onFinishedGoingToSleep() {
         if (mBouncer != null) {
             mBouncer.onScreenTurnedOff();
+        } else {
+            mBouncerInteractor.onScreenTurnedOff();
         }
-        mBouncerInteractor.onScreenTurnedOff();
     }
 
     @Override
@@ -832,8 +846,9 @@
         if (bouncerIsShowing()) {
             if (mBouncer != null) {
                 mBouncer.startPreHideAnimation(finishRunnable);
+            } else {
+                mBouncerInteractor.startDisappearAnimation(finishRunnable);
             }
-            mBouncerInteractor.startDisappearAnimation(finishRunnable);
             mCentralSurfaces.onBouncerPreHideAnimation();
 
             // We update the state (which will show the keyguard) only if an animation will run on
@@ -1106,13 +1121,15 @@
             if (bouncerDismissible || !showing || remoteInputActive) {
                 if (mBouncer != null) {
                     mBouncer.setBackButtonEnabled(true);
+                } else {
+                    mBouncerInteractor.setBackButtonEnabled(true);
                 }
-                mBouncerInteractor.setBackButtonEnabled(true);
             } else {
                 if (mBouncer != null) {
                     mBouncer.setBackButtonEnabled(false);
+                } else {
+                    mBouncerInteractor.setBackButtonEnabled(false);
                 }
-                mBouncerInteractor.setBackButtonEnabled(false);
             }
         }
 
@@ -1278,8 +1295,9 @@
     public void notifyKeyguardAuthenticated(boolean strongAuth) {
         if (mBouncer != null) {
             mBouncer.notifyKeyguardAuthenticated(strongAuth);
+        } else {
+            mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
         }
-        mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
 
         if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) {
             resetAlternateAuth(false);
@@ -1296,8 +1314,9 @@
         } else {
             if (mBouncer != null) {
                 mBouncer.showMessage(message, colorState);
+            } else {
+                mBouncerInteractor.showMessage(message, colorState);
             }
-            mBouncerInteractor.showMessage(message, colorState);
         }
     }
 
@@ -1344,8 +1363,9 @@
     public void updateResources() {
         if (mBouncer != null) {
             mBouncer.updateResources();
+        } else {
+            mBouncerInteractor.updateResources();
         }
-        mBouncerInteractor.updateResources();
     }
 
     public void dump(PrintWriter pw) {
@@ -1430,9 +1450,9 @@
     public void updateKeyguardPosition(float x) {
         if (mBouncer != null) {
             mBouncer.updateKeyguardPosition(x);
+        } else {
+            mBouncerInteractor.setKeyguardPosition(x);
         }
-
-        mBouncerInteractor.setKeyguardPosition(x);
     }
 
     private static class DismissWithActionRequest {
@@ -1474,9 +1494,9 @@
     public boolean isBouncerInTransit() {
         if (mBouncer != null) {
             return mBouncer.inTransit();
+        } else {
+            return mBouncerInteractor.isInTransit();
         }
-
-        return mBouncerInteractor.isInTransit();
     }
 
     /**
@@ -1485,9 +1505,9 @@
     public boolean bouncerIsShowing() {
         if (mBouncer != null) {
             return mBouncer.isShowing();
+        } else {
+            return mBouncerInteractor.isFullyShowing();
         }
-
-        return mBouncerInteractor.isFullyShowing();
     }
 
     /**
@@ -1496,9 +1516,9 @@
     public boolean bouncerIsScrimmed() {
         if (mBouncer != null) {
             return mBouncer.isScrimmed();
+        } else {
+            return mBouncerInteractor.isScrimmed();
         }
-
-        return mBouncerInteractor.isScrimmed();
     }
 
     /**
@@ -1507,9 +1527,10 @@
     public boolean bouncerIsAnimatingAway() {
         if (mBouncer != null) {
             return mBouncer.isAnimatingAway();
+        } else {
+            return mBouncerInteractor.isAnimatingAway();
         }
 
-        return mBouncerInteractor.isAnimatingAway();
     }
 
     /**
@@ -1518,9 +1539,9 @@
     public boolean bouncerWillDismissWithAction() {
         if (mBouncer != null) {
             return mBouncer.willDismissWithAction();
+        } else {
+            return mBouncerInteractor.willDismissWithAction();
         }
-
-        return mBouncerInteractor.willDismissWithAction();
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index 2448f1a..849ac5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -297,10 +297,10 @@
     }
 
     /**
-     * Ensures margin is applied
+     * Ensures default margin is applied
      */
     @Test
-    public void testMargin() {
+    public void testDefaultMargin() {
         final int margin = 5;
         final ComplicationLayoutEngine engine =
                 new ComplicationLayoutEngine(mLayout, margin, mTouchSession, 0, 0);
@@ -373,6 +373,74 @@
     }
 
     /**
+     * Ensures complication margin is applied
+     */
+    @Test
+    public void testComplicationMargin() {
+        final int defaultMargin = 5;
+        final int complicationMargin = 10;
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, defaultMargin, mTouchSession, 0, 0);
+
+        final ViewInfo firstViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0,
+                        complicationMargin),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+
+        addComplication(engine, firstViewInfo);
+
+        final ViewInfo secondViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_START,
+                        0),
+                Complication.CATEGORY_SYSTEM,
+                mLayout);
+
+        addComplication(engine, secondViewInfo);
+
+        firstViewInfo.clearInvocations();
+        secondViewInfo.clearInvocations();
+
+        final ViewInfo thirdViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_START,
+                        1),
+                Complication.CATEGORY_SYSTEM,
+                mLayout);
+
+        addComplication(engine, thirdViewInfo);
+
+        // The first added view should now be underneath the second view.
+        verifyChange(firstViewInfo, false, lp -> {
+            assertThat(lp.topToBottom == thirdViewInfo.view.getId()).isTrue();
+            assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+            assertThat(lp.topMargin).isEqualTo(complicationMargin);
+        });
+
+        // The second view should be in underneath the third view.
+        verifyChange(secondViewInfo, false, lp -> {
+            assertThat(lp.endToStart == thirdViewInfo.view.getId()).isTrue();
+            assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+            assertThat(lp.getMarginEnd()).isEqualTo(defaultMargin);
+        });
+    }
+
+    /**
      * Ensures layout in a particular position updates.
      */
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
index 967b30d..cb7e47b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -97,6 +97,35 @@
     }
 
     /**
+     * Ensures unspecified margin uses default.
+     */
+    @Test
+    public void testUnspecifiedMarginUsesDefault() {
+        final ComplicationLayoutParams params = new ComplicationLayoutParams(
+                100,
+                100,
+                ComplicationLayoutParams.POSITION_TOP,
+                ComplicationLayoutParams.DIRECTION_DOWN,
+                3);
+        assertThat(params.getMargin(10) == 10).isTrue();
+    }
+
+    /**
+     * Ensures specified margin is used instead of default.
+     */
+    @Test
+    public void testSpecifiedMargin() {
+        final ComplicationLayoutParams params = new ComplicationLayoutParams(
+                100,
+                100,
+                ComplicationLayoutParams.POSITION_TOP,
+                ComplicationLayoutParams.DIRECTION_DOWN,
+                3,
+                10);
+        assertThat(params.getMargin(5) == 10).isTrue();
+    }
+
+    /**
      * Ensures ComplicationLayoutParams is properly duplicated on copy construction.
      */
     @Test
@@ -106,12 +135,36 @@
                 100,
                 ComplicationLayoutParams.POSITION_TOP,
                 ComplicationLayoutParams.DIRECTION_DOWN,
+                3,
+                10);
+        final ComplicationLayoutParams copy = new ComplicationLayoutParams(params);
+
+        assertThat(copy.getDirection() == params.getDirection()).isTrue();
+        assertThat(copy.getPosition() == params.getPosition()).isTrue();
+        assertThat(copy.getWeight() == params.getWeight()).isTrue();
+        assertThat(copy.getMargin(0) == params.getMargin(1)).isTrue();
+        assertThat(copy.height == params.height).isTrue();
+        assertThat(copy.width == params.width).isTrue();
+    }
+
+    /**
+     * Ensures ComplicationLayoutParams is properly duplicated on copy construction with unspecified
+     * margin.
+     */
+    @Test
+    public void testCopyConstructionWithUnspecifiedMargin() {
+        final ComplicationLayoutParams params = new ComplicationLayoutParams(
+                100,
+                100,
+                ComplicationLayoutParams.POSITION_TOP,
+                ComplicationLayoutParams.DIRECTION_DOWN,
                 3);
         final ComplicationLayoutParams copy = new ComplicationLayoutParams(params);
 
         assertThat(copy.getDirection() == params.getDirection()).isTrue();
         assertThat(copy.getPosition() == params.getPosition()).isTrue();
         assertThat(copy.getWeight() == params.getWeight()).isTrue();
+        assertThat(copy.getMargin(1) == params.getMargin(1)).isTrue();
         assertThat(copy.height == params.height).isTrue();
         assertThat(copy.width == params.width).isTrue();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 5eb9a98..e539705 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -6,6 +6,7 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.media.MediaHost
 import com.android.systemui.media.MediaHostState
 import com.android.systemui.plugins.FalsingManager
@@ -52,6 +53,7 @@
     @Mock private lateinit var tile: QSTile
     @Mock private lateinit var otherTile: QSTile
     @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+    @Mock private lateinit var featureFlags: FeatureFlags
 
     private lateinit var controller: QSPanelController
 
@@ -82,7 +84,8 @@
             brightnessControllerFactory,
             brightnessSliderFactory,
             falsingManager,
-            statusBarKeyguardViewManager
+            statusBarKeyguardViewManager,
+            featureFlags
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 2db58be..7c930b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -159,6 +159,32 @@
     }
 
     @Test
+    fun testTopPadding_notCombinedHeaders() {
+        qsPanel.setUsingCombinedHeaders(false)
+        val padding = 10
+        val paddingCombined = 100
+        context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
+        context.orCreateTestableResources.addOverride(
+                R.dimen.qs_panel_padding_top_combined_headers, paddingCombined)
+
+        qsPanel.updatePadding()
+        assertThat(qsPanel.paddingTop).isEqualTo(padding)
+    }
+
+    @Test
+    fun testTopPadding_combinedHeaders() {
+        qsPanel.setUsingCombinedHeaders(true)
+        val padding = 10
+        val paddingCombined = 100
+        context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
+        context.orCreateTestableResources.addOverride(
+                R.dimen.qs_panel_padding_top_combined_headers, paddingCombined)
+
+        qsPanel.updatePadding()
+        assertThat(qsPanel.paddingTop).isEqualTo(paddingCombined)
+    }
+
+    @Test
     fun testSetSquishinessFraction_noCrash() {
         qsPanel.addView(qsPanel.mTileLayout as View, 0)
         qsPanel.addView(FrameLayout(context))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitorTest.kt
index 1531f88..16e2441 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitorTest.kt
@@ -148,7 +148,7 @@
             smallIcon = notification.smallIcon.bitmap.allocationByteCount,
             largeIcon = notification.getLargeIcon().bitmap.allocationByteCount,
             extras = 4092,
-            bigPicture = 960000,
+            bigPicture = bigPicture.bitmap.allocationByteCount,
             extender = 0,
             style = "BigPictureStyle",
             styleIcon = bigPictureIcon.bitmap.allocationByteCount,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 5454978..8c18f58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.flags.Flags.MODERN_BOUNCER;
+
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -525,4 +527,11 @@
         mBouncerExpansionCallback.onVisibilityChanged(false);
         verify(mCentralSurfaces).setBouncerShowingOverDream(false);
     }
+
+    @Test
+    public void flag_off_DoesNotCallBouncerInteractor() {
+        when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(false);
+        mStatusBarKeyguardViewManager.hideBouncer(false);
+        verify(mBouncerInteractor, never()).hide();
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 799c759..1df382f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -464,6 +464,16 @@
     }
 
     @Override
+    public void setInstalledAndEnabledServices(List<AccessibilityServiceInfo> infos) {
+        return;
+    }
+
+    @Override
+    public List<AccessibilityServiceInfo> getInstalledAndEnabledServices() {
+        return null;
+    }
+
+    @Override
     public void setAttributionTag(String attributionTag) {
         mAttributionTag = attributionTag;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1efbb0a..365068d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -277,6 +277,7 @@
     final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>();
 
     private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock);
+    private final ProxyManager mProxyManager;
     private final AccessibilityTraceManager mTraceManager;
     private final CaptioningManagerImpl mCaptioningManagerImpl;
 
@@ -396,7 +397,8 @@
             AccessibilityWindowManager a11yWindowManager,
             AccessibilityDisplayListener a11yDisplayListener,
             MagnificationController magnificationController,
-            @Nullable AccessibilityInputFilter inputFilter) {
+            @Nullable AccessibilityInputFilter inputFilter,
+            ProxyManager proxyManager) {
         mContext = context;
         mPowerManager =  (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
@@ -412,6 +414,7 @@
         mMagnificationController = magnificationController;
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
         mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
+        mProxyManager = proxyManager;
         if (inputFilter != null) {
             mInputFilter = inputFilter;
             mHasInputFilter = true;
@@ -445,6 +448,7 @@
                 new MagnificationScaleProvider(mContext));
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
         mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
+        mProxyManager = new ProxyManager(mLock);
         init();
     }
 
@@ -3602,6 +3606,34 @@
     }
 
     @Override
+    public boolean registerProxyForDisplay(IAccessibilityServiceClient client, int displayId)
+            throws RemoteException {
+        mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
+        if (client == null) {
+            return false;
+        }
+        if (displayId < 0) {
+            throw new IllegalArgumentException("The display id " + displayId + " is invalid.");
+        }
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("The default display cannot be proxy-ed.");
+        }
+        if (!isTrackedDisplay(displayId)) {
+            throw new IllegalArgumentException("The display " + displayId + " does not exist or is"
+                    + " not tracked by accessibility.");
+        }
+
+        mProxyManager.registerProxy(client, displayId);
+        return true;
+    }
+
+    @Override
+    public boolean unregisterProxyForDisplay(int displayId) throws RemoteException {
+        mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
+        return mProxyManager.unregisterProxy(displayId);
+    }
+
+    @Override
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
         synchronized (mLock) {
@@ -3830,6 +3862,21 @@
         return mA11yDisplayListener.getValidDisplayList();
     }
 
+
+    /**
+     *  Returns {@code true} if the display id is in the list of currently valid logical displays
+     *  being tracked by a11y.
+     */
+    private boolean isTrackedDisplay(int displayId) {
+        final ArrayList<Display> displays = getValidDisplayList();
+        for (Display display : displays) {
+            if (display.getDisplayId() == displayId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * A Utility class to handle display state.
      */
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
new file mode 100644
index 0000000..fb0b8f3
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -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.server.accessibility;
+import android.accessibilityservice.IAccessibilityServiceClient;
+
+/**
+ * Manages proxy connections.
+ *
+ * Currently this acts similarly to UiAutomationManager as a global manager, though ideally each
+ * proxy connection will belong to a separate user state.
+ *
+ * TODO(241117292): Remove or cut down during simultaneous user refactoring.
+ */
+public class ProxyManager {
+    private final Object mLock;
+
+    ProxyManager(Object lock) {
+        mLock = lock;
+    }
+
+    /**
+     * TODO: Create the proxy service connection.
+     */
+    public void registerProxy(IAccessibilityServiceClient client, int displayId) {
+    }
+
+    /**
+     * TODO: Unregister the proxy service connection based on display id.
+     */
+    public boolean unregisterProxy(int displayId) {
+        return true;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 77fb360..8510bd3 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -14691,7 +14691,7 @@
 
     @GuardedBy("this")
     private boolean isUsageHistoryEnabled() {
-        return false;
+        return mConstants.RECORD_USAGE_HISTORY;
     }
 
     @GuardedBy("this")
@@ -14793,6 +14793,8 @@
         public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
         public static final String KEY_BATTERY_CHARGED_DELAY_MS =
                 "battery_charged_delay_ms";
+        public static final String KEY_RECORD_USAGE_HISTORY =
+                "record_usage_history";
 
         private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
         private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 1_000;
@@ -14805,6 +14807,7 @@
         private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
         private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
         private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
+        private static final boolean DEFAULT_RECORD_USAGE_HISTORY = false;
 
         public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
         /* Do not set default value for KERNEL_UID_READERS_THROTTLE_TIME. Need to trigger an
@@ -14820,6 +14823,7 @@
         public int MAX_HISTORY_FILES;
         public int MAX_HISTORY_BUFFER; /*Bytes*/
         public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
+        public boolean RECORD_USAGE_HISTORY = DEFAULT_RECORD_USAGE_HISTORY;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -14895,6 +14899,9 @@
                                 DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
                                 : DEFAULT_MAX_HISTORY_BUFFER_KB)
                         * 1024;
+                RECORD_USAGE_HISTORY = mParser.getBoolean(
+                        KEY_RECORD_USAGE_HISTORY, DEFAULT_RECORD_USAGE_HISTORY);
+
                 updateBatteryChargedDelayMsLocked();
 
                 onChange();
@@ -14960,6 +14967,8 @@
             pw.println(MAX_HISTORY_BUFFER/1024);
             pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("=");
             pw.println(BATTERY_CHARGED_DELAY_MS);
+            pw.print(KEY_RECORD_USAGE_HISTORY); pw.print("=");
+            pw.println(RECORD_USAGE_HISTORY);
         }
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 99950a0..cd0096b 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -602,9 +602,12 @@
         synchronized (mUserTrustState) {
             wasTrusted = (mUserTrustState.get(userId) == TrustState.TRUSTED);
             wasTrustable = (mUserTrustState.get(userId) == TrustState.TRUSTABLE);
+            boolean isAutomotive = getContext().getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_AUTOMOTIVE);
             boolean renewingTrust = wasTrustable && (
                     (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0);
-            boolean canMoveToTrusted = alreadyUnlocked || isFromUnlock || renewingTrust;
+            boolean canMoveToTrusted =
+                    alreadyUnlocked || isFromUnlock || renewingTrust || isAutomotive;
             boolean upgradingTrustForCurrentUser = (userId == mCurrentUser);
 
             if (trustedByAtLeastOneAgent && wasTrusted) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 51d78e1..0f09252 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -132,6 +132,7 @@
     @Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
     @Mock private MagnificationController mMockMagnificationController;
     @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
+    @Mock private ProxyManager mProxyManager;
 
     @Rule
     public final TestableContext mTestableContext = new TestableContext(
@@ -184,7 +185,8 @@
                 mMockA11yWindowManager,
                 mMockA11yDisplayListener,
                 mMockMagnificationController,
-                mInputFilter);
+                mInputFilter,
+                mProxyManager);
 
         final AccessibilityUserState userState = new AccessibilityUserState(
                 mA11yms.getCurrentUserIdLocked(), mTestableContext, mA11yms);
@@ -278,6 +280,49 @@
 
     @SmallTest
     @Test
+    public void testRegisterProxy() throws Exception {
+        mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
+        verify(mProxyManager).registerProxy(mMockServiceClient, TEST_DISPLAY);
+    }
+
+
+    @SmallTest
+    @Test
+    public void testRegisterProxyWithoutPermission() throws Exception {
+        doThrow(SecurityException.class).when(mMockSecurityPolicy)
+                .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
+        try {
+            mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
+            Assert.fail();
+        } catch (SecurityException expected) {
+        }
+        verify(mProxyManager, never()).registerProxy(mMockServiceClient, TEST_DISPLAY);
+    }
+
+    @SmallTest
+    @Test
+    public void testRegisterProxyForDefaultDisplay() throws Exception {
+        try {
+            mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY);
+            Assert.fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        verify(mProxyManager, never()).registerProxy(mMockServiceClient, Display.DEFAULT_DISPLAY);
+    }
+
+    @SmallTest
+    @Test
+    public void testRegisterProxyForInvalidDisplay() throws Exception {
+        try {
+            mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY);
+            Assert.fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        verify(mProxyManager, never()).registerProxy(mMockServiceClient, Display.INVALID_DISPLAY);
+    }
+
+    @SmallTest
+    @Test
     public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
         final AccessibilityUserState userState = mA11yms.mUserStates.get(
                 mA11yms.getCurrentUserIdLocked());