Merge "Update documentation."
diff --git a/core/api/current.txt b/core/api/current.txt
index f126fbf9e..9514447 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2994,6 +2994,7 @@
   }
 
   public final class AccessibilityGestureEvent implements android.os.Parcelable {
+    ctor public AccessibilityGestureEvent(int, int, @NonNull java.util.List<android.view.MotionEvent>);
     method public int describeContents();
     method @NonNull public static String gestureIdToString(int);
     method public int getDisplayId();
@@ -3016,6 +3017,7 @@
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
     method @NonNull public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
     method @NonNull public final java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getSystemActions();
+    method @NonNull public final android.accessibilityservice.TouchInteractionController getTouchInteractionController(int);
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
     method @NonNull public final android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
     method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
@@ -3250,6 +3252,31 @@
     method public boolean willContinue();
   }
 
+  public final class TouchInteractionController {
+    method public void addListener(@Nullable java.util.concurrent.Executor, @NonNull android.accessibilityservice.TouchInteractionController.Listener);
+    method public int getDisplayId();
+    method public int getMaxPointerCount();
+    method public int getState();
+    method public void performClick();
+    method public void performLongClickAndStartDrag();
+    method public void removeAllListeners();
+    method public boolean removeListener(@NonNull android.accessibilityservice.TouchInteractionController.Listener);
+    method public void requestDelegating();
+    method public void requestDragging(int);
+    method public void requestTouchExploration();
+    method @NonNull public static String stateToString(int);
+    field public static final int STATE_CLEAR = 0; // 0x0
+    field public static final int STATE_DELEGATING = 4; // 0x4
+    field public static final int STATE_DRAGGING = 3; // 0x3
+    field public static final int STATE_TOUCH_EXPLORING = 2; // 0x2
+    field public static final int STATE_TOUCH_INTERACTING = 1; // 0x1
+  }
+
+  public static interface TouchInteractionController.Listener {
+    method public void onMotionEvent(@NonNull android.view.MotionEvent);
+    method public void onStateChanged(int);
+  }
+
 }
 
 package android.accounts {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 596b9f15..1c3c7ea 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -318,6 +318,10 @@
     method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
   }
 
+  public class SystemConfigManager {
+    method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String);
+  }
+
 }
 
 package android.os.storage {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3046061..bc34c50 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8779,7 +8779,6 @@
   public class SystemConfigManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
-    method @NonNull public java.util.List<java.lang.String> getEnabledComponentOverrides(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public int[] getSystemPermissionUids(@NonNull String);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 226deba..3267473 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -114,6 +114,7 @@
     method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
     method public static void resumeAppSwitches() throws android.os.RemoteException;
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopBackgroundUsersOnSwitch(int);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean);
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
@@ -128,6 +129,9 @@
     field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
     field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4
     field public static final int PROCESS_STATE_TOP = 2; // 0x2
+    field public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1; // 0xffffffff
+    field public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0; // 0x0
+    field public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1; // 0x1
   }
 
   public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 768ec38..3c9b232 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -150,7 +150,12 @@
     private final int mDisplayId;
     private List<MotionEvent> mMotionEvents = new ArrayList<>();
 
-    /** @hide */
+/**
+ * Constructs an AccessibilityGestureEvent to be dispatched to an accessibility service.
+ * @param gestureId the id number of the gesture.
+ * @param displayId the display on which this gesture was performed.
+ * @param motionEvents the motion events that lead to this gesture.
+ */
     public AccessibilityGestureEvent(
             int gestureId, int displayId, @NonNull List<MotionEvent> motionEvents) {
         mGestureId = gestureId;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 61d2b4b..0f852b4 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -53,6 +53,7 @@
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.SurfaceView;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
@@ -65,6 +66,7 @@
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -582,6 +584,10 @@
         /** Magnification changed callbacks for different displays */
         void onMagnificationChanged(int displayId, @NonNull Region region,
                 float scale, float centerX, float centerY);
+        /** Callbacks for receiving motion events. */
+        void onMotionEvent(MotionEvent event);
+        /** Callback for tuch state changes. */
+        void onTouchStateChanged(int displayId, int state);
         void onSoftKeyboardShowModeChanged(int showMode);
         void onPerformGestureResult(int sequence, boolean completedSuccessfully);
         void onFingerprintCapturingGesturesChanged(boolean active);
@@ -720,6 +726,12 @@
     /** List of magnification controllers, mapping from displayId -> MagnificationController. */
     private final SparseArray<MagnificationController> mMagnificationControllers =
             new SparseArray<>(0);
+    /**
+     * List of touch interaction controllers, mapping from displayId -> TouchInteractionController.
+     */
+    private final SparseArray<TouchInteractionController> mTouchInteractionControllers =
+            new SparseArray<>(0);
+
     private SoftKeyboardController mSoftKeyboardController;
     private final SparseArray<AccessibilityButtonController> mAccessibilityButtonControllers =
             new SparseArray<>(0);
@@ -1194,6 +1206,10 @@
         getFingerprintGestureController().onGesture(gesture);
     }
 
+    int getConnectionId() {
+        return mConnectionId;
+    }
+
     /**
      * Used to control and query the state of display magnification.
      */
@@ -2210,6 +2226,16 @@
             }
 
             @Override
+            public void onMotionEvent(MotionEvent event) {
+                AccessibilityService.this.onMotionEvent(event);
+            }
+
+            @Override
+            public void onTouchStateChanged(int displayId, int state) {
+                AccessibilityService.this.onTouchStateChanged(displayId, state);
+            }
+
+            @Override
             public void onSoftKeyboardShowModeChanged(int showMode) {
                 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
             }
@@ -2372,6 +2398,21 @@
         }
 
         @Override
+        public void onMotionEvent(MotionEvent event) {
+            final Message message = PooledLambda.obtainMessage(
+                            Callbacks::onMotionEvent, mCallback, event);
+            mCaller.sendMessage(message);
+        }
+
+        @Override
+        public void onTouchStateChanged(int displayId, int state) {
+            final Message message = PooledLambda.obtainMessage(Callbacks::onTouchStateChanged,
+                    mCallback,
+                    displayId, state);
+            mCaller.sendMessage(message);
+        }
+
+        @Override
         public void executeMessage(Message message) {
             switch (message.what) {
                 case DO_ON_ACCESSIBILITY_EVENT: {
@@ -2523,7 +2564,7 @@
                     }
                     return;
                 }
-                default :
+                default:
                     Log.w(LOG_TAG, "Unknown message type " + message.what);
             }
         }
@@ -2748,4 +2789,47 @@
             }
         }
     }
+
+    /**
+     * Returns the touch interaction controller for the specified logical display, which may be used
+     * to detect gestures and otherwise control touch interactions. If
+     * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled the
+     * controller's methods will have no effect.
+     *
+     * @param displayId The logical display id, use {@link Display#DEFAULT_DISPLAY} for default
+     *                      display.
+     * @return the TouchExploration controller
+     */
+    @NonNull
+    public final TouchInteractionController getTouchInteractionController(int displayId) {
+        synchronized (mLock) {
+            TouchInteractionController controller = mTouchInteractionControllers.get(displayId);
+            if (controller == null) {
+                controller = new TouchInteractionController(this, mLock, displayId);
+                mTouchInteractionControllers.put(displayId, controller);
+            }
+            return controller;
+        }
+    }
+
+    void onMotionEvent(MotionEvent event) {
+        TouchInteractionController controller;
+        synchronized (mLock) {
+            int displayId = event.getDisplayId();
+            controller = mTouchInteractionControllers.get(displayId);
+        }
+        if (controller != null) {
+            controller.onMotionEvent(event);
+        }
+    }
+
+    void onTouchStateChanged(int displayId, int state) {
+        TouchInteractionController controller;
+        synchronized (mLock) {
+            controller = mTouchInteractionControllers.get(displayId);
+        }
+        if (controller != null) {
+            controller.onStateChanged(state);
+        }
+    }
 }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 58b0d19..651c50f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -22,6 +22,7 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.accessibilityservice.AccessibilityGestureEvent;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 
 /**
  * Top-level interface to an accessibility service component.
@@ -44,6 +45,10 @@
 
     void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY);
 
+    void onMotionEvent(in MotionEvent event);
+
+    void onTouchStateChanged(int displayId, int state);
+
     void onSoftKeyboardShowModeChanged(int showMode);
 
     void onPerformGestureResult(int sequence, boolean completedSuccessfully);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 6c360e5..81457eb 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -122,4 +122,16 @@
 
     oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
         int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
+
+    void setServiceDetectsGesturesEnabled(int displayId, boolean mode);
+
+    void requestTouchExploration(int displayId);
+
+    void requestDragging(int displayId, int pointerId);
+
+    void requestDelegating(int displayId);
+
+    void onDoubleTap(int displayId);
+
+    void onDoubleTapAndHold(int displayId);
 }
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
new file mode 100644
index 0000000..d9be49a
--- /dev/null
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This class allows a service to handle touch exploration and the detection of specialized
+ * accessibility gestures. The service receives motion events and can match those motion events
+ * against the gestures it supports. The service can also request the framework enter three other
+ * states of operation for the duration of this interaction. Upon entering any of these states the
+ * framework will take over and the service will not receive motion events until the start of a new
+ * interaction. The states are as follows:
+ *
+ * <ul>
+ *   <li>The service can tell the framework that this interaction is touch exploration. The user is
+ *       trying to explore the screen rather than manipulate it. The framework will then convert the
+ *       motion events to hover events to support touch exploration.
+ *   <li>The service can tell the framework that this interaction is a dragging interaction where
+ *       two fingers are used to execute a one-finger gesture such as scrolling the screen. The
+ *       service must specify which of the two fingers should be passed through to rest of the input
+ *       pipeline.
+ *   <li>Finally, the service can request that the framework delegate this interaction, meaning pass
+ *       it through to the rest of the input pipeline as-is.
+ * </ul>
+ *
+ * When {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE } is enabled, this
+ * controller will receive all motion events received by the framework for the specified display
+ * when not touch-exploring or delegating. If the service classifies this interaction as touch
+ * exploration or delegating the framework will stop sending motion events to the service for the
+ * duration of this interaction. If the service classifies this interaction as a dragging
+ * interaction the framework will send motion events to the service to allow the service to
+ * determine if the interaction still qualifies as dragging or if it has become a delegating
+ * interaction. If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE } is disabled
+ * this controller will not receive any motion events because touch interactions are being passed
+ * through to the input pipeline unaltered.
+ * Note that {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE }
+ * requires setting {@link android.R.attr#canRequestTouchExplorationMode} as well.
+ */
+public final class TouchInteractionController {
+    /** The state where the user is not touching the screen. */
+    public static final int STATE_CLEAR = 0;
+    /**
+     * The state where the user is touching the screen and the service is receiving motion events.
+     */
+    public static final int STATE_TOUCH_INTERACTING = 1;
+    /**
+     * The state where the user is explicitly exploring the screen. The service is not receiving
+     * motion events.
+     */
+    public static final int STATE_TOUCH_EXPLORING = 2;
+    /**
+     * The state where the user is dragging with two fingers. The service is not receiving motion
+     * events. The selected finger is being dispatched to the rest of the input pipeline to execute
+     * the drag.
+     */
+    public static final int STATE_DRAGGING = 3;
+    /**
+     * The user is performing a gesture which is being passed through to the input pipeline as-is.
+     * The service is not receiving motion events.
+     */
+    public static final int STATE_DELEGATING = 4;
+
+    @IntDef({
+        STATE_CLEAR,
+        STATE_TOUCH_INTERACTING,
+        STATE_TOUCH_EXPLORING,
+        STATE_DRAGGING,
+        STATE_DELEGATING
+    })
+    private @interface State {}
+
+    // The maximum number of pointers that can be touching the screen at once. (See MAX_POINTER_ID
+    // in frameworks/native/include/input/Input.h)
+    private static final int MAX_POINTER_COUNT = 32;
+
+    private final AccessibilityService mService;
+    private final Object mLock;
+    private final int mDisplayId;
+    private boolean mServiceDetectsGestures;
+    /** Map of listeners to executors. Lazily created when adding the first listener. */
+    private ArrayMap<Listener, Executor> mListeners;
+
+    // The current state of the display.
+    private int mState = STATE_CLEAR;
+
+    TouchInteractionController(
+            @NonNull AccessibilityService service, @NonNull Object lock, int displayId) {
+        mDisplayId = displayId;
+        mLock = lock;
+        mService = service;
+    }
+
+    /**
+     * Adds the specified change listener to the list of motion event listeners. The callback will
+     * run using on the specified {@link Executor}', or on the service's main thread if the
+     * Executor is {@code null}.
+     * @param listener the listener to add, must be non-null
+     * @param executor the executor for this callback, or {@code null} to execute on the service's
+     *     main thread
+     */
+    public void addListener(@Nullable Executor executor, @NonNull Listener listener) {
+        synchronized (mLock) {
+            if (mListeners == null) {
+                mListeners = new ArrayMap<>();
+            }
+            mListeners.put(listener, executor);
+            if (mListeners.size() == 1) {
+                setServiceDetectsGestures(true);
+            }
+        }
+    }
+
+    /**
+     * Removes the specified listener from the list of motion event listeners.
+     *
+     * @param listener the listener to remove, must be non-null
+     * @return {@code true} if the listener was removed, {@code false} otherwise
+     */
+    public boolean removeListener(@NonNull Listener listener) {
+        if (mListeners == null) {
+            return false;
+        }
+        synchronized (mLock) {
+            boolean result = mListeners.remove(listener) != null;
+            if (result && mListeners.size() == 0) {
+                setServiceDetectsGestures(false);
+            }
+            return result;
+        }
+    }
+
+    /**
+     * Removes all listeners and returns control of touch interactions to the framework.
+     */
+    public void removeAllListeners() {
+        if (mListeners != null) {
+            synchronized (mLock) {
+                mListeners.clear();
+                setServiceDetectsGestures(false);
+            }
+        }
+    }
+
+    /**
+     * Dispatches motion events to any registered listeners. This should be called on the service's
+     * main thread.
+     */
+    void onMotionEvent(MotionEvent event) {
+        final ArrayMap<Listener, Executor> entries;
+        synchronized (mLock) {
+            // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
+            // modification.
+            entries = new ArrayMap<>(mListeners);
+        }
+        for (int i = 0, count = entries.size(); i < count; i++) {
+            final Listener listener = entries.keyAt(i);
+            final Executor executor = entries.valueAt(i);
+            if (executor != null) {
+                executor.execute(() -> listener.onMotionEvent(event));
+            } else {
+                // We're already on the main thread, just run the listener.
+                listener.onMotionEvent(event);
+            }
+        }
+    }
+
+    /**
+     * Dispatches motion events to any registered listeners. This should be called on the service's
+     * main thread.
+     */
+    void onStateChanged(@State int state) {
+        mState = state;
+        final ArrayMap<Listener, Executor> entries;
+        synchronized (mLock) {
+            // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
+            // modification.
+            entries = new ArrayMap<>(mListeners);
+        }
+        for (int i = 0, count = entries.size(); i < count; i++) {
+            final Listener listener = entries.keyAt(i);
+            final Executor executor = entries.valueAt(i);
+            if (executor != null) {
+                executor.execute(() -> listener.onStateChanged(state));
+            } else {
+                // We're already on the main thread, just run the listener.
+                listener.onStateChanged(state);
+            }
+        }
+    }
+
+    /**
+     * When {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled, this
+     * controller will receive all motion events received by the framework for the specified display
+     * when not touch-exploring, delegating, or dragging. This allows the service to detect its own
+     * gestures, and use its own logic to judge when the framework should start touch-exploring,
+     * delegating, or dragging. If {@link
+     * AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE } is disabled this flag has no
+     * effect.
+     *
+     * @see AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE
+     */
+    private void setServiceDetectsGestures(boolean mode) {
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance()
+                        .getConnection(mService.getConnectionId());
+        if (connection != null) {
+            try {
+                connection.setServiceDetectsGesturesEnabled(mDisplayId, mode);
+                mServiceDetectsGestures = mode;
+            } catch (RemoteException re) {
+                throw new RuntimeException(re);
+            }
+        }
+    }
+
+    /**
+     * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at
+     * least one listener has been added for this display this function tells the framework to
+     * initiate touch exploration. Touch exploration will continue for the duration of this
+     * interaction.
+     */
+    public void requestTouchExploration() {
+        checkState();
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance()
+                        .getConnection(mService.getConnectionId());
+        if (connection != null) {
+            try {
+                connection.requestTouchExploration(mDisplayId);
+            } catch (RemoteException re) {
+                throw new RuntimeException(re);
+            }
+        }
+    }
+
+    /**
+     * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
+     * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
+     * one listener has been added, this function tells the framework to initiate a dragging
+     * interaction using the specified pointer. The pointer's movements will be passed through to
+     * the rest of the input pipeline. Dragging is often used to perform two-finger scrolling.
+     *
+     * @param pointerId the pointer to be passed through to the rest of the input pipeline. If the
+     *            pointer id is valid but not actually present on the screen it will be ignored.
+     * @throws IllegalArgumentException if the pointer id is outside of the allowed range.
+     */
+    public void requestDragging(int pointerId) {
+        checkState();
+        if (pointerId < 0 || pointerId > MAX_POINTER_COUNT) {
+            throw new IllegalArgumentException("Invalid pointer id: " + pointerId);
+        }
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance()
+                        .getConnection(mService.getConnectionId());
+        if (connection != null) {
+            try {
+                connection.requestDragging(mDisplayId, pointerId);
+            } catch (RemoteException re) {
+                throw new RuntimeException(re);
+            }
+        }
+    }
+
+    /**
+     * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
+     * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
+     * one listener has been added, this function tells the framework to initiate a delegating
+     * interaction. Motion events will be passed through as-is to the rest of the input pipeline for
+     * the duration of this interaction.
+     */
+    public void requestDelegating() {
+        checkState();
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance()
+                        .getConnection(mService.getConnectionId());
+        if (connection != null) {
+            try {
+                connection.requestDelegating(mDisplayId);
+            } catch (RemoteException re) {
+                throw new RuntimeException(re);
+            }
+        }
+    }
+
+    /**
+     * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
+     * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
+     * one listener has been added, this function tells the framework to perform a click.
+     * The framework will first try to perform
+     * {@link AccessibilityNodeInfo.AccessibilityAction#ACTION_CLICK} on the item with
+     * accessibility focus. If that fails, the framework will simulate a click using motion events
+     * on the last location to have accessibility focus.
+     */
+    public void performClick() {
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance()
+                        .getConnection(mService.getConnectionId());
+        if (connection != null) {
+            try {
+                connection.onDoubleTap(mDisplayId);
+            } catch (RemoteException re) {
+                throw new RuntimeException(re);
+            }
+        }
+    }
+
+    /**
+     * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
+     * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
+     * one listener has been added, this function tells the framework to perform a long click.
+     * The framework will simulate a long click using motion events on the last location with
+     * accessibility focus and will delegate any movements to the rest of the input pipeline. This
+     * allows a user to double-tap and hold to trigger a drag and then execute that drag by moving
+     * their finger.
+     */
+    public void performLongClickAndStartDrag() {
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance()
+                        .getConnection(mService.getConnectionId());
+        if (connection != null) {
+            try {
+                connection.onDoubleTapAndHold(mDisplayId);
+            } catch (RemoteException re) {
+                throw new RuntimeException(re);
+            }
+        }
+    }
+
+    private void checkState() {
+        if (!mServiceDetectsGestures || mListeners.size() == 0) {
+            throw new IllegalStateException(
+                    "State transitions are not allowed without first adding a listener.");
+        }
+        if (mState != STATE_TOUCH_INTERACTING) {
+            throw new IllegalStateException(
+                    "State transitions are not allowed in " + stateToString(mState));
+        }
+    }
+
+    /** @return the maximum number of pointers that this display will accept. */
+    public int getMaxPointerCount() {
+        return MAX_POINTER_COUNT;
+    }
+
+    /** @return the display id associated with this controller. */
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    /**
+     * @return the current state of this controller.
+     * @see TouchInteractionController#STATE_CLEAR
+     * @see TouchInteractionController#STATE_DELEGATING
+     * @see TouchInteractionController#STATE_DRAGGING
+     * @see TouchInteractionController#STATE_TOUCH_EXPLORING
+     */
+    public int getState() {
+        synchronized (mLock) {
+            return mState;
+        }
+    }
+
+    /** Returns a string representation of the specified state. */
+    @NonNull
+    public static String stateToString(int state) {
+        switch (state) {
+            case STATE_CLEAR:
+                return "STATE_CLEAR";
+            case STATE_TOUCH_INTERACTING:
+                return "STATE_TOUCH_INTERACTING";
+            case STATE_TOUCH_EXPLORING:
+                return "STATE_TOUCH_EXPLORING";
+            case STATE_DRAGGING:
+                return "STATE_DRAGGING";
+            case STATE_DELEGATING:
+                return "STATE_DELEGATING";
+            default:
+                return "Unknown state: " + state;
+        }
+    }
+
+    /** Listeners allow services to receive motion events and state change updates. */
+    public interface Listener {
+        /**
+         * Called when the framework has sent a motion event to the service.
+         *
+         * @param event the event being passed to the service.
+         */
+        void onMotionEvent(@NonNull MotionEvent event);
+
+        /**
+         * Called when the state of motion event dispatch for this display has changed.
+         *
+         * @param state the new state of motion event dispatch.
+         * @see TouchInteractionController#STATE_CLEAR
+         * @see TouchInteractionController#STATE_DELEGATING
+         * @see TouchInteractionController#STATE_DRAGGING
+         * @see TouchInteractionController#STATE_TOUCH_EXPLORING
+         */
+        void onStateChanged(@State int state);
+    }
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 365493de..d77227b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4082,6 +4082,56 @@
     }
 
     /**
+     * Uses the value defined by the platform.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1;
+
+    /**
+     * Overrides value defined by the platform and stop background users on switch.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1;
+
+    /**
+     * Overrides value defined by the platform and don't stop background users on switch.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0;
+
+    /** @hide */
+    @IntDef(prefix = { "STOP_BG_USERS_ON_SWITCH_" }, value = {
+            STOP_BG_USERS_ON_SWITCH_DEFAULT,
+            STOP_BG_USERS_ON_SWITCH_TRUE,
+            STOP_BG_USERS_ON_SWITCH_FALSE
+    })
+    public @interface StopBgUsersOnSwitch {}
+
+    /**
+     * Sets whether background users should be stopped when the current user is switched.
+     *
+     * <p>Should only be used on tests.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+    public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+        try {
+            getService().setStopBackgroundUsersOnSwitch(value);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Starts a profile.
      * To be used with non-managed profiles, managed profiles should use
      * {@link UserManager#requestQuietModeEnabled}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0881db0..7dbcd5f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -686,6 +688,11 @@
             @Nullable VoiceInteractionManagerProvider provider);
 
     /**
+     * Sets whether background users should be stopped when the current user is switched.
+     */
+    public abstract void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value);
+
+    /**
      * Provides the interface to communicate between voice interaction manager service and
      * ActivityManagerService.
      */
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 658c130..de79a64 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -349,6 +349,7 @@
     @UnsupportedAppUsage
     boolean switchUser(int userid);
     @UnsupportedAppUsage
+    void setStopBackgroundUsersOnSwitch(int value);
     boolean removeTask(int taskId);
     @UnsupportedAppUsage
     void registerProcessObserver(in IProcessObserver observer);
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 65f71d0..828b171 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -49,6 +49,7 @@
 import android.view.Display;
 import android.view.InputEvent;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -1542,6 +1543,15 @@
                     return false;
                 }
 
+                public void onMotionEvent(MotionEvent event) {
+                    /* do nothing */
+                }
+
+                @Override
+                public void onTouchStateChanged(int displayId, int state) {
+                    /* do nothing */
+                }
+
                 @Override
                 public void onAccessibilityEvent(AccessibilityEvent event) {
                     final OnAccessibilityEventListener listener;
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 2a344f6..ad3de25 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -103,7 +103,7 @@
      * are unparcelled, mParcelledData willbe set to null.
      */
     @UnsupportedAppUsage
-    Parcel mParcelledData = null;
+    volatile Parcel mParcelledData = null;
 
     /**
      * Whether {@link #mParcelledData} was generated by native code or not.
@@ -182,13 +182,56 @@
      * @param b a Bundle to be copied.
      */
     BaseBundle(BaseBundle b) {
-        copyInternal(b, false);
+        this(b, /* deep */ false);
     }
 
     /**
-     * Special constructor that does not initialize the bundle.
+     * Constructs a {@link BaseBundle} containing a copy of {@code from}.
+     *
+     * @param from The bundle to be copied.
+     * @param deep Whether is a deep or shallow copy.
+     *
+     * @hide
      */
-    BaseBundle(boolean doInit) {
+    BaseBundle(BaseBundle from, boolean deep) {
+        synchronized (from) {
+            mClassLoader = from.mClassLoader;
+
+            if (from.mMap != null) {
+                if (!deep) {
+                    mMap = new ArrayMap<>(from.mMap);
+                } else {
+                    final ArrayMap<String, Object> fromMap = from.mMap;
+                    final int n = fromMap.size();
+                    mMap = new ArrayMap<>(n);
+                    for (int i = 0; i < n; i++) {
+                        mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
+                    }
+                }
+            } else {
+                mMap = null;
+            }
+
+            final Parcel parcelledData;
+            if (from.mParcelledData != null) {
+                if (from.isEmptyParcel()) {
+                    parcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
+                    mParcelledByNative = false;
+                } else {
+                    parcelledData = Parcel.obtain();
+                    parcelledData.appendFrom(from.mParcelledData, 0,
+                            from.mParcelledData.dataSize());
+                    parcelledData.setDataPosition(0);
+                    mParcelledByNative = from.mParcelledByNative;
+                }
+            } else {
+                parcelledData = null;
+                mParcelledByNative = false;
+            }
+
+            // Keep as last statement to ensure visibility of other fields
+            mParcelledData = parcelledData;
+        }
     }
 
     /**
@@ -323,8 +366,8 @@
             } else {
                 mMap.erase();
             }
-            mParcelledData = null;
             mParcelledByNative = false;
+            mParcelledData = null;
             return;
         }
 
@@ -358,8 +401,8 @@
             if (recycleParcel) {
                 recycleParcel(parcelledData);
             }
-            mParcelledData = null;
             mParcelledByNative = false;
+            mParcelledData = null;
         }
         if (DEBUG) {
             Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
@@ -501,44 +544,7 @@
         mMap.clear();
     }
 
-    void copyInternal(BaseBundle from, boolean deep) {
-        synchronized (from) {
-            if (from.mParcelledData != null) {
-                if (from.isEmptyParcel()) {
-                    mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
-                    mParcelledByNative = false;
-                } else {
-                    mParcelledData = Parcel.obtain();
-                    mParcelledData.appendFrom(from.mParcelledData, 0,
-                            from.mParcelledData.dataSize());
-                    mParcelledData.setDataPosition(0);
-                    mParcelledByNative = from.mParcelledByNative;
-                }
-            } else {
-                mParcelledData = null;
-                mParcelledByNative = false;
-            }
-
-            if (from.mMap != null) {
-                if (!deep) {
-                    mMap = new ArrayMap<>(from.mMap);
-                } else {
-                    final ArrayMap<String, Object> fromMap = from.mMap;
-                    final int N = fromMap.size();
-                    mMap = new ArrayMap<>(N);
-                    for (int i = 0; i < N; i++) {
-                        mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
-                    }
-                }
-            } else {
-                mMap = null;
-            }
-
-            mClassLoader = from.mClassLoader;
-        }
-    }
-
-    Object deepCopyValue(Object value) {
+    private Object deepCopyValue(Object value) {
         if (value == null) {
             return null;
         }
@@ -570,7 +576,7 @@
         return value;
     }
 
-    ArrayList deepcopyArrayList(ArrayList from) {
+    private ArrayList deepcopyArrayList(ArrayList from) {
         final int N = from.size();
         ArrayList out = new ArrayList(N);
         for (int i=0; i<N; i++) {
@@ -1717,9 +1723,9 @@
         if (length < 0) {
             throw new RuntimeException("Bad length in parcel: " + length);
         } else if (length == 0) {
+            mParcelledByNative = false;
             // Empty Bundle or end of data.
             mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
-            mParcelledByNative = false;
             return;
         } else if (length % 4 != 0) {
             throw new IllegalStateException("Bundle length is not aligned by 4: " + length);
@@ -1757,8 +1763,8 @@
                 + ": " + length + " bundle bytes starting at " + offset);
         p.setDataPosition(0);
 
-        mParcelledData = p;
         mParcelledByNative = isNativeBundle;
+        mParcelledData = p;
     }
 
     /** {@hide} */
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b677b69..b069fb3 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -53,7 +53,7 @@
  *
  * <p>Most developers will not implement this class directly, instead using the
  * <a href="{@docRoot}guide/components/aidl.html">aidl</a> tool to describe the desired
- * interface, having it generate the appropriate Binder subclass.  You can,
+ * interface, having it generate the appropriate Binder subclass. You can,
  * however, derive directly from Binder to implement your own custom RPC
  * protocol or simply instantiate a raw Binder object directly to use as a
  * token that can be shared across processes.
@@ -63,17 +63,17 @@
  * To use this correctly, you must be doing so within the context of a top-level
  * application component (a {@link android.app.Service}, {@link android.app.Activity},
  * or {@link android.content.ContentProvider}) that lets the system know your process
- * should remain running.</p>
+ * should remain running.
  *
  * <p>You must keep in mind the situations in which your process
  * could go away, and thus require that you later re-create a new Binder and re-attach
- * it when the process starts again.  For example, if you are using this within an
+ * it when the process starts again. For example, if you are using this within an
  * {@link android.app.Activity}, your activity's process may be killed any time the
  * activity is not started; if the activity is later re-created you will need to
  * create a new Binder and hand it back to the correct place again; you need to be
  * aware that your process may be started for another reason (for example to receive
  * a broadcast) that will not involve re-creating the activity and thus run its code
- * to create a new Binder.</p>
+ * to create a new Binder.
  *
  * @see IBinder
  */
@@ -94,14 +94,15 @@
     /**
      * Value to represents that a calling work source is not set.
      *
-     * This constatnt needs to be kept in sync with IPCThreadState::kUnsetWorkSource.
+     * <p>This constant needs to be kept in sync with IPCThreadState::kUnsetWorkSource.
      *
      * @hide
      */
     public static final int UNSET_WORKSOURCE = -1;
 
     /**
-     * Control whether dump() calls are allowed.
+     * Control whether {@link #dump(FileDescriptor, PrintWriter, String[]) dump()}
+     * calls are allowed.
      */
     private static volatile String sDumpDisabled = null;
 
@@ -188,7 +189,7 @@
         sObserver = observer;
     }
 
-    /** {@hide} */
+    /** @hide */
     static volatile boolean sWarnOnBlocking = false;
 
     /**
@@ -207,8 +208,8 @@
     /**
      * Allow blocking calls on the given interface, overriding the requested
      * value of {@link #setWarnOnBlocking(boolean)}.
-     * <p>
-     * This should only be rarely called when you are <em>absolutely sure</em>
+     *
+     * <p>This should only be rarely called when you are <em>absolutely sure</em>
      * the remote interface is a built-in system component that can never be
      * upgraded. In particular, this <em>must never</em> be called for
      * interfaces hosted by package that could be upgraded or replaced,
@@ -258,7 +259,9 @@
             ThreadLocal.withInitial(() -> sWarnOnBlocking);
 
     /**
-     * Allow blocking calls for the current thread.  See {@link #allowBlocking}.
+     * Allow blocking calls for the current thread.
+     *
+     * @see {@link #allowBlocking}.
      *
      * @hide
      */
@@ -267,7 +270,9 @@
     }
 
     /**
-     * Reset the current thread to the default blocking behavior.  See {@link #defaultBlocking}.
+     * Reset the current thread to the default blocking behavior.
+     *
+     * @see {@link #defaultBlocking}.
      *
      * @hide
      */
@@ -286,10 +291,10 @@
 
     /**
      * Return the ID of the process that sent you the current transaction
-     * that is being processed.  This pid can be used with higher-level
+     * that is being processed. This PID can be used with higher-level
      * system services to determine its identity and check permissions.
      * If the current thread is not currently executing an incoming transaction,
-     * then its own pid is returned.
+     * then its own PID is returned.
      *
      * Warning: oneway transactions do not receive PID.
      */
@@ -297,11 +302,11 @@
     public static final native int getCallingPid();
 
     /**
-     * Return the Linux uid assigned to the process that sent you the
-     * current transaction that is being processed.  This uid can be used with
+     * Return the Linux UID assigned to the process that sent you the
+     * current transaction that is being processed. This UID can be used with
      * higher-level system services to determine its identity and check
-     * permissions.  If the current thread is not currently executing an
-     * incoming transaction, then its own uid is returned.
+     * permissions. If the current thread is not currently executing an
+     * incoming transaction, then its own UID is returned.
      */
     @CriticalNative
     public static final native int getCallingUid();
@@ -316,11 +321,11 @@
     public static final native boolean isDirectlyHandlingTransaction();
 
     /**
-     * Return the Linux uid assigned to the process that sent the transaction
+     * Return the Linux UID assigned to the process that sent the transaction
      * currently being processed.
      *
      * @throws IllegalStateException if the current thread is not currently
-     *        executing an incoming transaction.
+     * executing an incoming transaction.
      */
     public static final int getCallingUidOrThrow() {
         if (!isDirectlyHandlingTransaction()) {
@@ -332,18 +337,20 @@
 
     /**
      * Return the UserHandle assigned to the process that sent you the
-     * current transaction that is being processed.  This is the user
-     * of the caller.  It is distinct from {@link #getCallingUid()} in that a
+     * current transaction that is being processed. This is the user
+     * of the caller. It is distinct from {@link #getCallingUid()} in that a
      * particular user will have multiple distinct apps running under it each
-     * with their own uid.  If the current thread is not currently executing an
+     * with their own UID. If the current thread is not currently executing an
      * incoming transaction, then its own UserHandle is returned.
+     *
+     * @see UserHandle
      */
     public static final @NonNull UserHandle getCallingUserHandle() {
         return UserHandle.of(UserHandle.getUserId(getCallingUid()));
     }
 
     /**
-     * Reset the identity of the incoming IPC on the current thread.  This can
+     * Reset the identity of the incoming IPC on the current thread. This can
      * be useful if, while handling an incoming call, you will be calling
      * on interfaces of other objects that may be local to your process and
      * need to do permission checks on the calls coming into them (so they
@@ -376,10 +383,10 @@
 
     /**
      * Convenience method for running the provided action enclosed in
-     * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}
+     * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}.
      *
-     * Any exception thrown by the given action will be caught and rethrown after the call to
-     * {@link #restoreCallingIdentity}
+     * <p>Any exception thrown by the given action will be caught and
+     * rethrown after the call to {@link #restoreCallingIdentity}.
      *
      * @hide
      */
@@ -400,10 +407,10 @@
 
     /**
      * Convenience method for running the provided action enclosed in
-     * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result
+     * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result.
      *
-     * Any exception thrown by the given action will be caught and rethrown after the call to
-     * {@link #restoreCallingIdentity}
+     * <p>Any exception thrown by the given action will be caught and rethrown after
+     * the call to {@link #restoreCallingIdentity}.
      *
      * @hide
      */
@@ -428,12 +435,13 @@
      *
      * <p>The StrictMode settings are kept in two places: a Java-level
      * threadlocal for libcore/Dalvik, and a native threadlocal (set
-     * here) for propagation via Binder calls.  This is a little
+     * here) for propagation via Binder calls. This is a little
      * unfortunate, but necessary to break otherwise more unfortunate
      * dependencies either of Dalvik on Android, or Android
      * native-only code on Dalvik.
      *
      * @see StrictMode
+     *
      * @hide
      */
     @CriticalNative
@@ -443,6 +451,7 @@
      * Gets the current native thread-local StrictMode policy mask.
      *
      * @see #setThreadStrictModePolicy
+     *
      * @hide
      */
     @CriticalNative
@@ -459,7 +468,7 @@
      * reasons, we only support one UID. This UID represents the original user responsible for the
      * binder calls.
      *
-     * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after setting the
+     * <p>{@link #restoreCallingWorkSource(long)} must always be called after setting the
      * worksource.
      *
      * <p>A typical use case would be
@@ -477,16 +486,16 @@
      *
      * @param workSource The original UID responsible for the binder call.
      * @return token to restore original work source.
-     **/
+     */
     @CriticalNative
     public static final native long setCallingWorkSourceUid(int workSource);
 
     /**
      * Returns the work source set by the caller.
      *
-     * Unlike {@link Binder#getCallingUid()}, this result of this method cannot be trusted. The
+     * <p>Unlike {@link #getCallingUid()}, this result of this method cannot be trusted. The
      * caller can set the value to whatever they want. Only use this value if you trust the calling
-     * uid.
+     * UID.
      *
      * @return The original UID responsible for the binder transaction.
      */
@@ -499,7 +508,7 @@
      * <p>The work source will be propagated for future outgoing binder transactions
      * executed on this thread.
      *
-     * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after clearing the
+     * <p>{@link #restoreCallingWorkSource(long)} must always be called after clearing the
      * worksource.
      *
      * <p>A typical use case would be
@@ -513,13 +522,13 @@
      * </pre>
      *
      * @return token to restore original work source.
-     **/
+     */
     @CriticalNative
     public static final native long clearCallingWorkSource();
 
     /**
      * Restores the work source on this thread using a token returned by
-     * {@link #setCallingWorkSourceUid(int) or {@link clearCallingWorkSource()}.
+     * {@link #setCallingWorkSourceUid(int)} or {@link #clearCallingWorkSource()}.
      *
      * <p>A typical use case would be
      * <pre>
@@ -530,7 +539,7 @@
      *   Binder.restoreCallingWorkSource(token);
      * }
      * </pre>
-     **/
+     */
     @CriticalNative
     public static final native void restoreCallingWorkSource(long token);
 
@@ -553,7 +562,7 @@
      * Use a VINTF-stability binder w/o VINTF requirements. Should be called
      * on a binder before it is sent out of process.
      *
-     * This must be called before the object is sent to another process.
+     * <p>This must be called before the object is sent to another process.
      *
      * @hide
      */
@@ -561,7 +570,7 @@
 
     /**
      * Flush any Binder commands pending in the current thread to the kernel
-     * driver.  This can be
+     * driver. This can be
      * useful to call before performing an operation that may block for a long
      * time, to ensure that any pending object references have been released
      * in order to prevent the process from holding on to objects longer than
@@ -570,7 +579,7 @@
     public static final native void flushPendingCommands();
 
     /**
-     * Add the calling thread to the IPC thread pool.  This function does
+     * Add the calling thread to the IPC thread pool. This function does
      * not return until the current process is exiting.
      */
     public static final void joinThreadPool() {
@@ -579,6 +588,7 @@
 
     /**
      * Returns true if the specified interface is a proxy.
+     *
      * @hide
      */
     public static final boolean isProxy(IInterface iface) {
@@ -588,6 +598,7 @@
     /**
      * Call blocks until the number of executing binder threads is less
      * than the maximum number of binder threads allowed for this process.
+     *
      * @hide
      */
     public static final native void blockUntilThreadAvailable();
@@ -595,7 +606,7 @@
     /**
      * Default constructor just initializes the object.
      *
-     * If you're creating a Binder token (a Binder object without an attached interface),
+     * <p>If you're creating a Binder token (a Binder object without an attached interface),
      * you should use {@link #Binder(String)} instead.
      */
     public Binder() {
@@ -605,7 +616,7 @@
     /**
      * Constructor for creating a raw Binder object (token) along with a descriptor.
      *
-     * The descriptor of binder objects usually specifies the interface they are implementing.
+     * <p>The descriptor of binder objects usually specifies the interface they are implementing.
      * In case of binder tokens, no interface is implemented, and the descriptor can be used
      * as a sort of tag to help identify the binder token. This will help identify remote
      * references to these objects more easily when debugging.
@@ -614,7 +625,7 @@
      * Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
      * help identify them.
      */
-    public Binder(@Nullable String descriptor)  {
+    public Binder(@Nullable String descriptor) {
         mObject = getNativeBBinderHolder();
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
 
@@ -631,9 +642,9 @@
 
     /**
      * Convenience method for associating a specific interface with the Binder.
-     * After calling, queryLocalInterface() will be implemented for you
-     * to return the given owner IInterface when the corresponding
-     * descriptor is requested.
+     * After calling, {@link #queryLocalInterface(String) queryLocalInterface()}
+     * will be implemented for you to return the given owner IInterface when
+     * the corresponding descriptor is requested.
      */
     public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
         mOwner = owner;
@@ -666,8 +677,8 @@
     }
 
     /**
-     * Use information supplied to attachInterface() to return the
-     * associated IInterface if it matches the requested
+     * Use information supplied to {@link #attachInterface attachInterface()}
+     * to return the associated {@link IInterface} if it matches the requested
      * descriptor.
      */
     public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
@@ -678,14 +689,15 @@
     }
 
     /**
-     * Control disabling of dump calls in this process.  This is used by the system
+     * Control disabling of dump calls in this process. This is used by the system
      * process watchdog to disable incoming dump calls while it has detecting the system
-     * is hung and is reporting that back to the activity controller.  This is to
+     * is hung and is reporting that back to the activity controller. This is to
      * prevent the controller from getting hung up on bug reports at this point.
-     * @hide
      *
      * @param msg The message to show instead of the dump; if null, dumps are
      * re-enabled.
+     *
+     * @hide
      */
     public static void setDumpDisabled(String msg) {
         sDumpDisabled = msg;
@@ -694,7 +706,8 @@
     /**
      * Listener to be notified about each proxy-side binder call.
      *
-     * See {@link setProxyTransactListener}.
+     * @see {@link #setProxyTransactListener}.
+     *
      * @hide
      */
     @SystemApi
@@ -702,7 +715,8 @@
         /**
          * Called before onTransact.
          *
-         * @return an object that will be passed back to #onTransactEnded (or null).
+         * @return an object that will be passed back to {@link #onTransactEnded} (or null).,
+         *
          * @hide
          */
         @Nullable
@@ -713,15 +727,15 @@
         /**
          * Called before onTransact.
          *
-         * @return an object that will be passed back to #onTransactEnded (or null).
+         * @return an object that will be passed back to {@link #onTransactEnded} (or null).
          */
         @Nullable
         Object onTransactStarted(@NonNull IBinder binder, int transactionCode);
 
         /**
-         * Called after onTranact (even when an exception is thrown).
+         * Called after onTransact (even when an exception is thrown).
          *
-         * @param session The object return by #onTransactStarted.
+         * @param session The object return by {@link #onTransactStarted}.
          */
         void onTransactEnded(@Nullable Object session);
     }
@@ -732,16 +746,17 @@
      * <li>By default, this listener will propagate the worksource if the outgoing call happens on
      * the same thread as the incoming binder call.
      * <li>Custom attribution can be done by calling {@link ThreadLocalWorkSource#setUid(int)}.
+     *
      * @hide
      */
     public static class PropagateWorkSourceTransactListener implements ProxyTransactListener {
         @Override
         public Object onTransactStarted(IBinder binder, int transactionCode) {
-           // Note that {@link Binder#getCallingUid()} is already set to the UID of the current
-           // process when this method is called.
-           //
-           // We use ThreadLocalWorkSource instead. It also allows feature owners to set
-           // {@link ThreadLocalWorkSource#set(int) manually to attribute resources to a UID.
+            // Note that {@link #getCallingUid()} is already set to the UID of the current
+            // process when this method is called.
+            //
+            // We use {@link ThreadLocalWorkSource} instead. It also allows feature owners to set
+            // {@link ThreadLocalWorkSource#set(int)} manually to attribute resources to a UID.
             int uid = ThreadLocalWorkSource.getUid();
             if (uid != ThreadLocalWorkSource.UID_NONE) {
                 return Binder.setCallingWorkSourceUid(uid);
@@ -770,6 +785,7 @@
      * <li>The listener is called on the critical path of the binder transaction so be careful about
      * performance.
      * <li>Never execute another binder transaction inside the listener.
+     *
      * @hide
      */
     @SystemApi
@@ -778,7 +794,7 @@
     }
 
     /**
-     * Default implementation is a stub that returns false.  You will want
+     * Default implementation is a stub that returns false. You will want
      * to override this to do the appropriate unmarshalling of transactions.
      *
      * <p>If you want to call this, call transact().
@@ -786,15 +802,14 @@
      * <p>Implementations that are returning a result should generally use
      * {@link Parcel#writeNoException() Parcel.writeNoException} and
      * {@link Parcel#writeException(Exception) Parcel.writeException} to propagate
-     * exceptions back to the caller.</p>
+     * exceptions back to the caller.
      *
-     * @param code The action to perform.  This should
-     * be a number between {@link #FIRST_CALL_TRANSACTION} and
-     * {@link #LAST_CALL_TRANSACTION}.
+     * @param code The action to perform. This should be a number between
+     * {@link #FIRST_CALL_TRANSACTION} and {@link #LAST_CALL_TRANSACTION}.
      * @param data Marshalled data being received from the caller.
      * @param reply If the caller is expecting a result back, it should be marshalled
      * in to here.
-     * @param flags Additional operation flags.  Either 0 for a normal
+     * @param flags Additional operation flags. Either 0 for a normal
      * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
      *
      * @return Return true on a successful call; returning false is generally used to
@@ -856,10 +871,12 @@
      * Resolves a transaction code to a human readable name.
      *
      * <p>Default implementation is a stub that returns null.
+     *
      * <p>AIDL generated code will return the original method name.
      *
      * @param transactionCode The code to resolve.
      * @return A human readable name.
+     *
      * @hide
      */
     public @Nullable String getTransactionName(int transactionCode) {
@@ -925,7 +942,7 @@
      * Print the object's state into the given stream.
      *
      * @param fd The raw file descriptor that the dump is being sent to.
-     * @param fout The file to which you should dump your state.  This will be
+     * @param fout The file to which you should dump your state. This will be
      * closed for you after you return.
      * @param args additional arguments to the dump request.
      */
@@ -941,6 +958,7 @@
      * @param callback Callback through which to interact with the invoking shell.
      * @param resultReceiver Called when the command has finished executing, with the result code.
      * @throws RemoteException
+     *
      * @hide
      */
     public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@@ -958,7 +976,8 @@
      *
      * <p class="caution">Note: no permission checking is done before calling this method; you must
      * apply any security checks as appropriate for the command being executed.
-     * Consider using {@link ShellCommand} to help in the implementation.</p>
+     * Consider using {@link ShellCommand} to help in the implementation.
+     *
      * @hide
      */
     public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@@ -1013,7 +1032,7 @@
      * System services can implement this method to implement ADB shell commands.
      *
      * <p>A system binder service can implement it to handle shell commands on ADB. For example,
-     * the Job Scheduler service implements it to handle <code>adb shell cmd jobscheduler</code>.
+     * the Job Scheduler service implements it to handle {@code adb shell cmd jobscheduler}.
      *
      * <p>Commands are only executable by ADB shell; i.e. only {@link Process#SHELL_UID} and
      * {@link Process#ROOT_UID} can call them.
@@ -1022,8 +1041,8 @@
      * @param out standard output
      * @param err standard error
      * @param args arguments passed to the command. Can be empty. The first argument is typically
-     *             a subcommand, such as {@code run} for {@code adb shell cmd jobscheduler run}.
-     * @return the status code returned from the <code>cmd</code> command.
+     * a subcommand, such as {@code run} for {@code adb shell cmd jobscheduler run}.
+     * @return the status code returned from the {@code cmd} command.
      *
      * @hide
      */
@@ -1051,7 +1070,7 @@
     public final native void setExtension(@Nullable IBinder extension);
 
     /**
-     * Default implementation rewinds the parcels and calls onTransact.  On
+     * Default implementation rewinds the parcels and calls onTransact. On
      * the remote side, transact calls into the binder to do the IPC.
      */
     public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
@@ -1083,7 +1102,7 @@
 
     static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) {
         if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) {
-            // Trying to send > 800k, this is way too much
+            // Trying to send > 800k, this is way too much.
             StringBuilder sb = new StringBuilder();
             sb.append(msg);
             sb.append(": on ");
@@ -1107,7 +1126,7 @@
     private static native long getNativeBBinderHolder();
 
     /**
-     * By default, we use the calling uid since we can always trust it.
+     * By default, we use the calling UID since we can always trust it.
      */
     private static volatile BinderInternal.WorkSourceProvider sWorkSourceProvider =
             (x) -> Binder.getCallingUid();
@@ -1122,6 +1141,7 @@
      * <li>The callback is called on the critical path of the binder transaction so be careful about
      * performance.
      * <li>Never execute another binder transaction inside the callback.
+     *
      * @hide
      */
     public static void setWorkSourceProvider(BinderInternal.WorkSourceProvider workSourceProvider) {
@@ -1131,12 +1151,12 @@
         sWorkSourceProvider = workSourceProvider;
     }
 
-    // Entry point from android_util_Binder.cpp's onTransact
+    // Entry point from android_util_Binder.cpp's onTransact.
     @UnsupportedAppUsage
     private boolean execTransact(int code, long dataObj, long replyObj,
             int flags) {
         // At that point, the parcel request headers haven't been parsed so we do not know what
-        // WorkSource the caller has set. Use calling uid as the default.
+        // {@link WorkSource} the caller has set. Use calling UID as the default.
         final int callingUid = Binder.getCallingUid();
         final long origWorkSource = ThreadLocalWorkSource.setUid(callingUid);
         try {
@@ -1154,17 +1174,18 @@
                 observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
         Parcel data = Parcel.obtain(dataObj);
         Parcel reply = Parcel.obtain(replyObj);
-        // theoretically, we should call transact, which will call onTransact,
+        // Theoretically, we should call transact, which will call onTransact,
         // but all that does is rewind it, and we just got these from an IPC,
         // so we'll just call it directly.
         boolean res;
         // Log any exceptions as warnings, don't silently suppress them.
-        // If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
+        // If the call was {@link IBinder#FLAG_ONEWAY} then these exceptions
+        // disappear into the ether.
         final boolean tracingEnabled = Binder.isTracingEnabled();
         try {
             final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
             if (heavyHitterWatcher != null) {
-                // Notify the heavy hitter watcher, if it's enabled
+                // Notify the heavy hitter watcher, if it's enabled.
                 heavyHitterWatcher.onTransaction(callingUid, getClass(), code);
             }
             if (tracingEnabled) {
@@ -1197,7 +1218,7 @@
                     Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
                 }
             } else {
-                // Clear the parcel before writing the exception
+                // Clear the parcel before writing the exception.
                 reply.setDataSize(0);
                 reply.setDataPosition(0);
                 reply.writeException(e);
@@ -1209,7 +1230,7 @@
             }
             if (observer != null) {
                 // The parcel RPC headers have been called during onTransact so we can now access
-                // the worksource uid from the parcel.
+                // the worksource UID from the parcel.
                 final int workSourceUid = sWorkSourceProvider.resolveWorkSourceUid(
                         data.readCallingWorkSourceUid());
                 observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
@@ -1220,9 +1241,9 @@
         data.recycle();
 
         // Just in case -- we are done with the IPC, so there should be no more strict
-        // mode violations that have gathered for this thread.  Either they have been
+        // mode violations that have gathered for this thread. Either they have been
         // parceled and are now in transport off to the caller, or we are returning back
-        // to the main transaction loop to wait for another incoming transaction.  Either
+        // to the main transaction loop to wait for another incoming transaction. Either
         // way, strict mode begone!
         StrictMode.clearGatheredViolations();
         return res;
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 92eb7a5..b2bbfd6 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -102,6 +102,18 @@
     }
 
     /**
+     * Constructs a {@link Bundle} containing a copy of {@code from}.
+     *
+     * @param from The bundle to be copied.
+     * @param deep Whether is a deep or shallow copy.
+     *
+     * @hide
+     */
+    Bundle(Bundle from, boolean deep) {
+        super(from, deep);
+    }
+
+    /**
      * If {@link #mParcelledData} is not null, copy the HAS FDS bit from it because it's fast.
      * Otherwise (if {@link #mParcelledData} is already null), leave {@link #FLAG_HAS_FDS_KNOWN}
      * unset, because scanning a map is slower.  We'll do it lazily in
@@ -167,13 +179,6 @@
     }
 
     /**
-     * Constructs a Bundle without initializing it.
-     */
-    Bundle(boolean doInit) {
-        super(doInit);
-    }
-
-    /**
      * Make a Bundle for a single key/value pair.
      *
      * @hide
@@ -260,9 +265,7 @@
      * are referenced as-is and not copied in any way.
      */
     public Bundle deepCopy() {
-        Bundle b = new Bundle(false);
-        b.copyInternal(this, true);
-        return b;
+        return new Bundle(this, /* deep */ true);
     }
 
     /**
@@ -324,28 +327,10 @@
      */
     public boolean hasFileDescriptors() {
         if ((mFlags & FLAG_HAS_FDS_KNOWN) == 0) {
-            boolean fdFound = false;    // keep going until we find one or run out of data
-
-            if (mParcelledData != null) {
-                if (mParcelledData.hasFileDescriptors()) {
-                    fdFound = true;
-                }
-            } else {
-                // It's been unparcelled, so we need to walk the map
-                for (int i=mMap.size()-1; i>=0; i--) {
-                    Object obj = mMap.valueAt(i);
-                    if (Parcel.hasFileDescriptors(obj)) {
-                        fdFound = true;
-                        break;
-                    }
-                }
-            }
-
-            if (fdFound) {
-                mFlags |= FLAG_HAS_FDS;
-            } else {
-                mFlags &= ~FLAG_HAS_FDS;
-            }
+            Parcel p = mParcelledData;
+            mFlags = (Parcel.hasFileDescriptors((p != null) ? p : mMap))
+                    ? mFlags | FLAG_HAS_FDS
+                    : mFlags & ~FLAG_HAS_FDS;
             mFlags |= FLAG_HAS_FDS_KNOWN;
         }
         return (mFlags & FLAG_HAS_FDS) != 0;
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index d83d94a..15e3ce2 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.content.ComponentName;
+
 /**
   * Binder interface to query SystemConfig in the system server.
   * {@hide}
@@ -44,5 +46,5 @@
     /**
      * @see SystemConfigManager#getEnabledComponentOverrides
      */
-    List<String> getEnabledComponentOverrides(String packageName);
+    List<ComponentName> getEnabledComponentOverrides(String packageName);
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index dd0cb8c..d1e6716 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -747,57 +747,70 @@
     }
 
     /**
-     * Check if the object used in {@link #readValue(ClassLoader)} / {@link #writeValue(Object)}
-     * has file descriptors.
+     * Check if the object has file descriptors.
+     *
+     * <p>Objects supported are {@link Parcel} and objects that can be passed to {@link
+     * #writeValue(Object)}}
      *
      * <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method
      * for that.
      *
-     * @throws IllegalArgumentException if you provide any object not supported by above methods.
-     *         Most notably, if you pass {@link Parcel}, this method will throw, for that check
-     *         {@link Parcel#hasFileDescriptors()}
+     * @throws IllegalArgumentException if you provide any object not supported by above methods
+     *     (including if the unsupported object is inside a nested container).
      *
      * @hide
      */
     public static boolean hasFileDescriptors(Object value) {
-        if (value instanceof LazyValue) {
-            return ((LazyValue) value).hasFileDescriptors();
-        } else if (value instanceof Parcelable) {
-            if ((((Parcelable) value).describeContents()
-                    & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+        if (value instanceof Parcel) {
+            Parcel parcel = (Parcel) value;
+            if (parcel.hasFileDescriptors()) {
                 return true;
             }
-        } else if (value instanceof Parcelable[]) {
-            Parcelable[] array = (Parcelable[]) value;
-            for (int n = array.length - 1; n >= 0; n--) {
-                Parcelable p = array[n];
-                if (p != null && ((p.describeContents()
-                        & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
+        } else if (value instanceof LazyValue) {
+            LazyValue lazy = (LazyValue) value;
+            if (lazy.hasFileDescriptors()) {
+                return true;
+            }
+        } else if (value instanceof Parcelable) {
+            Parcelable parcelable = (Parcelable) value;
+            if ((parcelable.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+                return true;
+            }
+        } else if (value instanceof ArrayMap<?, ?>) {
+            ArrayMap<?, ?> map = (ArrayMap<?, ?>) value;
+            for (int i = 0, n = map.size(); i < n; i++) {
+                if (hasFileDescriptors(map.keyAt(i))
+                        || hasFileDescriptors(map.valueAt(i))) {
+                    return true;
+                }
+            }
+        } else if (value instanceof Map<?, ?>) {
+            Map<?, ?> map = (Map<?, ?>) value;
+            for (Map.Entry<?, ?> entry : map.entrySet()) {
+                if (hasFileDescriptors(entry.getKey())
+                        || hasFileDescriptors(entry.getValue())) {
+                    return true;
+                }
+            }
+        } else if (value instanceof List<?>) {
+            List<?> list = (List<?>) value;
+            for (int i = 0, n = list.size(); i < n; i++) {
+                if (hasFileDescriptors(list.get(i))) {
                     return true;
                 }
             }
         } else if (value instanceof SparseArray<?>) {
             SparseArray<?> array = (SparseArray<?>) value;
-            for (int n = array.size() - 1; n >= 0; n--) {
-                Object object = array.valueAt(n);
-                if (object instanceof Parcelable) {
-                    Parcelable p = (Parcelable) object;
-                    if (p != null && (p.describeContents()
-                            & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
-                        return true;
-                    }
+            for (int i = 0, n = array.size(); i < n; i++) {
+                if (hasFileDescriptors(array.valueAt(i))) {
+                    return true;
                 }
             }
-        } else if (value instanceof ArrayList<?>) {
-            ArrayList<?> array = (ArrayList<?>) value;
-            for (int n = array.size() - 1; n >= 0; n--) {
-                Object object = array.get(n);
-                if (object instanceof Parcelable) {
-                    Parcelable p = (Parcelable) object;
-                    if (p != null && ((p.describeContents()
-                            & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
-                        return true;
-                    }
+        } else if (value instanceof Object[]) {
+            Object[] array = (Object[]) value;
+            for (int i = 0, n = array.length; i < n; i++) {
+                if (hasFileDescriptors(array[i])) {
+                    return true;
                 }
             }
         } else {
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index 7b55e710..f4edcb1 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -156,10 +156,15 @@
     }
 
     /**
-     * Constructs a PersistableBundle without initializing it.
+     * Constructs a {@link PersistableBundle} containing a copy of {@code from}.
+     *
+     * @param from The bundle to be copied.
+     * @param deep Whether is a deep or shallow copy.
+     *
+     * @hide
      */
-    PersistableBundle(boolean doInit) {
-        super(doInit);
+    PersistableBundle(PersistableBundle from, boolean deep) {
+        super(from, deep);
     }
 
     /**
@@ -190,9 +195,7 @@
      * are referenced as-is and not copied in any way.
      */
     public PersistableBundle deepCopy() {
-        PersistableBundle b = new PersistableBundle(false);
-        b.copyInternal(this, true);
-        return b;
+        return new PersistableBundle(this, /* deep */ true);
     }
 
     /**
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index a6316df..cde2063 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -17,10 +17,10 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.content.ComponentName;
 import android.content.Context;
 import android.util.ArraySet;
 import android.util.Log;
@@ -138,9 +138,9 @@
      * @return The enabled component
      * {@hide}
      */
-    @SystemApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @NonNull
-    public List<String> getEnabledComponentOverrides(@NonNull String packageName) {
+    public List<ComponentName> getEnabledComponentOverrides(@NonNull String packageName) {
         try {
             return mInterface.getEnabledComponentOverrides(packageName);
         } catch (RemoteException e) {
diff --git a/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS b/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS
new file mode 100644
index 0000000..cb521c8
--- /dev/null
+++ b/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS
@@ -0,0 +1,8 @@
+ewol@google.com
+hackbod@google.com
+jsharkey@google.com
+narayan@google.com
+patb@google.com
+svetoslavganov@google.com
+yamasani@google.com
+zhanghai@google.com
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index d39b56d..5b9d69c 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -191,20 +191,6 @@
     public static final int LISTEN_SIGNAL_STRENGTHS                         = 0x00000100;
 
     /**
-     * Listen for changes of the network signal strengths (cellular) always reported from modem,
-     * even in some situations such as the screen of the device is off.
-     *
-     * @see #onSignalStrengthsChanged
-     *
-     * @hide
-     * @deprecated Use TelephonyManager#setSignalStrengthUpdateRequest
-     * instead.
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-    public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH          = 0x00000200;
-
-    /**
      * Listen for changes to observed cell info.
      *
      * Listening to this event requires the {@link Manifest.permission#READ_PHONE_STATE} and
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index bedad73..cb1cff9 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -1041,10 +1041,6 @@
             eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
         }
 
-        if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
-            eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
-        }
-
         if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
             eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index df69ed0..66fcf6c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2432,7 +2432,7 @@
     <permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"
         android:protectionLevel="signature" />
 
-    <!-- Allows listen permission to always reported signal strength.
+    <!-- Allows listen permission to always reported system signal strength.
          @hide Used internally. -->
     <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH"
         android:protectionLevel="signature" />
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 3045d7d5..c241e36 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -160,15 +160,27 @@
 
     public void takeScreenshot(int displayId, RemoteCallback callback) {}
 
-    public void setTouchExplorationPassthroughRegion(int displayId, Region region) {}
-
-    public void setGestureDetectionPassthroughRegion(int displayId, Region region) {}
-
     public void setFocusAppearance(int strokeWidth, int color) {}
 
     public void logTrace(long timestamp, String where, String callingParams, int processId,
             long threadId, int callingUid, Bundle callingStack) {}
 
+    public void setGestureDetectionPassthroughRegion(int displayId, Region region) {}
+
+    public void setTouchExplorationPassthroughRegion(int displayId, Region region) {}
+
+    public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {}
+
+    public void requestTouchExploration(int displayId) {}
+
+    public void requestDragging(int displayId, int pointerId) {}
+
+    public void requestDelegating(int displayId) {}
+
+    public void onDoubleTap(int displayId) {}
+
+    public void onDoubleTapAndHold(int displayId) {}
+
     public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
             int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {}
 }
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 73c2e8b..8e3d726 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -132,11 +132,12 @@
         "kotlinx-coroutines-android",
         "kotlinx-coroutines-core",
         "iconloader_base",
-        "jsr330",
         "protolog-lib",
         "WindowManager-Shell-proto",
+        "dagger2",
         "jsr330",
     ],
     kotlincflags: ["-Xjvm-default=enable"],
     manifest: "AndroidManifest.xml",
+    plugins: ["dagger2-compiler"],
 }
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 9fe0247..7dc2f31 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -65,25 +65,28 @@
     <LinearLayout
         android:id="@+id/top_end_container"
         android:layout_gravity="top|end"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal">
+
         <ImageButton
             android:id="@+id/settings"
             android:layout_width="@dimen/pip_action_size"
             android:layout_height="@dimen/pip_action_size"
             android:contentDescription="@string/pip_phone_settings"
+            android:layout_gravity="top|start"
             android:gravity="center"
             android:src="@drawable/pip_ic_settings"
             android:background="?android:selectableItemBackgroundBorderless" />
 
         <ImageButton
-            android:id="@+id/dismiss"
+            android:id="@+id/enter_split"
             android:layout_width="@dimen/pip_action_size"
             android:layout_height="@dimen/pip_action_size"
-            android:contentDescription="@string/pip_phone_close"
+            android:layout_gravity="top|start"
             android:gravity="center"
-            android:src="@drawable/pip_ic_close_white"
+            android:contentDescription="@string/pip_phone_enter_split"
+            android:src="@drawable/pip_expand"
             android:background="?android:selectableItemBackgroundBorderless" />
     </LinearLayout>
 
@@ -97,4 +100,14 @@
         android:padding="@dimen/pip_resize_handle_padding"
         android:src="@drawable/pip_resize_handle"
         android:background="?android:selectableItemBackgroundBorderless" />
+
+    <ImageButton
+        android:id="@+id/dismiss"
+        android:layout_width="@dimen/pip_action_size"
+        android:layout_height="@dimen/pip_action_size"
+        android:contentDescription="@string/pip_phone_close"
+        android:layout_gravity="top|end"
+        android:gravity="center"
+        android:src="@drawable/pip_ic_close_white"
+        android:background="?android:selectableItemBackgroundBorderless" />
 </FrameLayout>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index d0e4f7a..0cdaa20 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -15,6 +15,10 @@
     limitations under the License.
 -->
 <resources>
+    <!-- Determines whether the shell features all run on another thread. This is to be overrided
+         by the resources of the app using the Shell library. -->
+    <bool name="config_enableShellMainThread">false</bool>
+
     <!-- Animation duration for PIP when entering. -->
     <integer name="config_pipEnterAnimationDuration">425</integer>
 
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 764854a..c88fc16 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -24,6 +24,9 @@
     <!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
     <string name="pip_phone_settings">Settings</string>
 
+    <!-- Label for the PIP enter split button [CHAR LIMIT=NONE] -->
+    <string name="pip_phone_enter_split">Enter split screen</string>
+
     <!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
     <string name="pip_menu_title">Menu</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalMainThread.java
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
copy to libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalMainThread.java
index 7292b9e..9ac7a12 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalMainThread.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,20 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dagger;
+package com.android.wm.shell.common.annotations;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 
-import javax.inject.Scope;
+import javax.inject.Qualifier;
 
 /**
- * Scope annotation for singleton items within the WMComponent.
+ * Annotates a method or qualifies a provider that runs on the main-thread of the process using
+ * this library.
  */
+@Qualifier
 @Documented
 @Retention(RUNTIME)
-@Scope
-public @interface WMSingleton {
+public @interface ExternalMainThread {
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt
new file mode 100644
index 0000000..1cd69ed
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt
@@ -0,0 +1,13 @@
+The dagger modules in this directory can be included by the host SysUI using the Shell library for
+explicity injection of Shell components. Apps using this library are not required to use these
+dagger modules for setup, but it is recommended for them to include them as needed.
+
+The modules are currently inherited as such:
+
++- WMShellBaseModule (common shell features across SysUI)
+   |
+   +- WMShellModule (handheld)
+   |
+   +- TvPipModule (tv pip)
+      |
+      +- TvWMShellModule (tv)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 3be1d3c..711a0ac 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
 
 import android.content.Context;
 import android.os.Handler;
 
-import com.android.systemui.dagger.WMSingleton;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayController;
@@ -44,6 +43,7 @@
 import com.android.wm.shell.pip.tv.TvPipMenuController;
 import com.android.wm.shell.pip.tv.TvPipNotificationController;
 import com.android.wm.shell.pip.tv.TvPipTransition;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -161,13 +161,14 @@
             PipTransitionController pipTransitionController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<LegacySplitScreenController> splitScreenOptional,
+            Optional<SplitScreenController> newSplitScreenOptional,
             DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
                 syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
                 tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
-                pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
-                shellTaskOrganizer, mainExecutor);
+                pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index dbdc460..6997d60 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
 
 import android.animation.AnimationHandler;
 import android.content.Context;
 import android.view.IWindowManager;
 
-import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.dagger.WMSingleton;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index b85ceefb..a564949 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
 
 import android.app.ActivityTaskManager;
 import android.content.Context;
@@ -27,8 +27,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.launcher3.icons.IconProvider;
-import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.dagger.WMSingleton;
 import com.android.wm.shell.RootDisplayAreaOrganizer;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellCommandHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/wmshell/WMShellConcurrencyModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index 61f50b5..5c205f9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
 
 import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
@@ -24,18 +24,18 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.Trace;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
-import com.android.systemui.dagger.WMSingleton;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
+import com.android.wm.shell.common.annotations.ExternalMainThread;
 import com.android.wm.shell.common.annotations.ShellAnimationThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.R;
 
 import dagger.Module;
 import dagger.Provides;
@@ -61,13 +61,26 @@
     // Shell Concurrency - Components used for managing threading in the Shell and SysUI
     //
 
+
+    /**
+     * Provide a SysUI main-thread Handler.
+     *
+     * Prefer the Main Executor when possible.
+     */
+    @Provides
+    @ExternalMainThread
+    public static Handler provideMainHandler() {
+        return new Handler(Looper.getMainLooper());
+    }
+
     /**
      * Provide a SysUI main-thread Executor.
      */
     @WMSingleton
     @Provides
-    @Main
-    public static ShellExecutor provideSysUIMainExecutor(@Main Handler sysuiMainHandler) {
+    @ExternalMainThread
+    public static ShellExecutor provideSysUIMainExecutor(
+            @ExternalMainThread Handler sysuiMainHandler) {
         return new HandlerExecutor(sysuiMainHandler);
     }
 
@@ -78,7 +91,8 @@
     @WMSingleton
     @Provides
     @ShellMainThread
-    public static Handler provideShellMainHandler(Context context, @Main Handler sysuiMainHandler) {
+    public static Handler provideShellMainHandler(Context context,
+            @ExternalMainThread Handler sysuiMainHandler) {
         if (enableShellMainThread(context)) {
              HandlerThread mainThread = new HandlerThread("wmshell.main", THREAD_PRIORITY_DISPLAY);
              mainThread.start();
@@ -99,7 +113,8 @@
     @Provides
     @ShellMainThread
     public static ShellExecutor provideShellMainExecutor(Context context,
-            @ShellMainThread Handler mainHandler, @Main ShellExecutor sysuiMainExecutor) {
+            @ShellMainThread Handler mainHandler,
+            @ExternalMainThread ShellExecutor sysuiMainExecutor) {
         if (enableShellMainThread(context)) {
             return new HandlerExecutor(mainHandler);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index a7c5ad2..ec70147 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
 
 import android.animation.AnimationHandler;
 import android.content.Context;
 import android.os.Handler;
 import android.view.IWindowManager;
 
-import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.dagger.WMSingleton;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.apppairs.AppPairsController;
@@ -57,6 +55,7 @@
 import com.android.wm.shell.pip.phone.PipController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
 import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
 import com.android.wm.shell.transition.Transitions;
@@ -217,14 +216,15 @@
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             PipTransitionController pipTransitionController,
             Optional<LegacySplitScreenController> splitScreenOptional,
+            Optional<SplitScreenController> newSplitScreenOptional,
             DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
                 syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
                 menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
-                pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
-                shellTaskOrganizer, mainExecutor);
+                pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
     }
 
     @WMSingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMSingleton.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMSingleton.java
index 7292b9e..7f45c38 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMSingleton.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dagger;
+package com.android.wm.shell.dagger;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b6e5804..6cc5f09 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -77,6 +77,7 @@
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
@@ -126,7 +127,8 @@
     private final int mExitAnimationDuration;
     private final int mCrossFadeAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
-    private final Optional<LegacySplitScreenController> mSplitScreenOptional;
+    private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
+    private final Optional<SplitScreenController> mSplitScreenOptional;
     protected final ShellTaskOrganizer mTaskOrganizer;
     protected final ShellExecutor mMainExecutor;
 
@@ -252,7 +254,8 @@
             @NonNull PipAnimationController pipAnimationController,
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
             @NonNull PipTransitionController pipTransitionController,
-            Optional<LegacySplitScreenController> splitScreenOptional,
+            Optional<LegacySplitScreenController> legacySplitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             @NonNull DisplayController displayController,
             @NonNull PipUiEventLogger pipUiEventLogger,
             @NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -274,6 +277,7 @@
         mPipAnimationController = pipAnimationController;
         mPipUiEventLoggerLogger = pipUiEventLogger;
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+        mLegacySplitScreenOptional = legacySplitScreenOptional;
         mSplitScreenOptional = splitScreenOptional;
         mTaskOrganizer = shellTaskOrganizer;
         mMainExecutor = mainExecutor;
@@ -373,8 +377,11 @@
      *   activity render it's final configuration while the Task is still in PiP.
      * - setWindowingMode to undefined at the end of transition
      * @param animationDurationMs duration in millisecond for the exiting PiP transition
+     * @param requestEnterSplit whether the enterSplit button is pressed on PiP or not.
+     *                             Indicate the user wishes to directly put PiP into split screen
+     *                             mode.
      */
-    public void exitPip(int animationDurationMs) {
+    public void exitPip(int animationDurationMs, boolean requestEnterSplit) {
         if (!mPipTransitionState.isInPip()
                 || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
                 || mToken == null) {
@@ -387,7 +394,7 @@
                 PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
-        final int direction = syncWithSplitScreenBounds(destinationBounds)
+        final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
                 ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
                 : TRANSITION_DIRECTION_LEAVE_PIP;
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -396,7 +403,7 @@
         // We set to fullscreen here for now, but later it will be set to UNDEFINED for
         // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
         wct.setActivityWindowingMode(mToken,
-                direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+                direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
                         ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                         : WINDOWING_MODE_FULLSCREEN);
         wct.setBounds(mToken, destinationBounds);
@@ -435,7 +442,7 @@
         wct.setWindowingMode(mToken, getOutPipWindowingMode());
         // Simply reset the activity mode set prior to the animation running.
         wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
-        mSplitScreenOptional.ifPresent(splitScreen -> {
+        mLegacySplitScreenOptional.ifPresent(splitScreen -> {
             if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
                 wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
             }
@@ -1165,6 +1172,7 @@
             @PipAnimationController.TransitionDirection int direction,
             @PipAnimationController.AnimationType int type) {
         final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
+        final boolean isPipTopLeft = isPipTopLeft();
         mPipBoundsState.setBounds(destinationBounds);
         if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
             removePipImmediately();
@@ -1210,10 +1218,10 @@
                             null /* callback */, false /* withStartDelay */);
                 });
             } else {
-                applyFinishBoundsResize(wct, direction);
+                applyFinishBoundsResize(wct, direction, isPipTopLeft);
             }
         } else {
-            applyFinishBoundsResize(wct, direction);
+            applyFinishBoundsResize(wct, direction, isPipTopLeft);
         }
 
         finishResizeForMenu(destinationBounds);
@@ -1241,7 +1249,11 @@
         } else if (isOutPipDirection(direction)) {
             // If we are animating to fullscreen or split screen, then we need to reset the
             // override bounds on the task to ensure that the task "matches" the parent's bounds.
-            taskBounds = null;
+            if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+                taskBounds = destinationBounds;
+            } else {
+                taskBounds = null;
+            }
             applyWindowingModeChangeOnExit(wct, direction);
         } else {
             // Just a resize in PIP
@@ -1261,8 +1273,20 @@
      * applying it.
      */
     public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
-            @PipAnimationController.TransitionDirection int direction) {
-        mTaskOrganizer.applyTransaction(wct);
+            @PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
+        if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+            mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
+        } else {
+            mTaskOrganizer.applyTransaction(wct);
+        }
+    }
+
+    private boolean isPipTopLeft() {
+        final Rect topLeft = new Rect();
+        final Rect bottomRight = new Rect();
+        mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+
+        return topLeft.contains(mPipBoundsState.getBounds());
     }
 
     /**
@@ -1347,18 +1371,27 @@
     }
 
     /**
-     * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split
-     * screen.
+     * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
+     * bounds if PiP is going to split screen.
      *
      * @param destinationBoundsOut contain the updated destination bounds if applicable
      * @return {@code true} if destinationBounds is altered for split screen
      */
-    private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
-        if (!mSplitScreenOptional.isPresent()) {
+    private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
+        if (enterSplit && mSplitScreenOptional.isPresent()) {
+            final Rect topLeft = new Rect();
+            final Rect bottomRight = new Rect();
+            mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+            final boolean isPipTopLeft = isPipTopLeft();
+            destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
+            return true;
+        }
+
+        if (!mLegacySplitScreenOptional.isPresent()) {
             return false;
         }
 
-        LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get();
+        LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
         if (!legacySplitScreen.isDividerVisible()) {
             // fail early if system is not in split screen mode
             return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index ae8c1b6..5687f4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -95,6 +95,11 @@
          * Called when the PIP requested to show the menu.
          */
         void onPipShowMenu();
+
+        /**
+         * Called when the PIP requested to enter Split.
+         */
+        void onEnterSplit();
     }
 
     private final Matrix mMoveTransform = new Matrix();
@@ -458,6 +463,10 @@
         mListeners.forEach(Listener::onPipDismiss);
     }
 
+    void onEnterSplit() {
+        mListeners.forEach(Listener::onEnterSplit);
+    }
+
     /**
      * @return the best set of actions to show in the PiP menu.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 47a8c67..69ae45d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -151,7 +151,7 @@
                         result = true;
                         break;
                     case AccessibilityNodeInfo.ACTION_EXPAND:
-                        mMotionHelper.expandLeavePip();
+                        mMotionHelper.expandLeavePip(false /* skipAnimation */);
                         result = true;
                         break;
                     default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 9d3baac..05f33b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -482,7 +482,8 @@
                     false /* fromShelfAdjustment */,
                     wct /* windowContainerTransaction */);
             if (wct != null) {
-                mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME);
+                mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME,
+                        false /* wasPipTopLeft */);
             }
         };
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 3eeba6e..0644657 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -18,8 +18,6 @@
 
 import android.content.Context;
 import android.graphics.Rect;
-import android.util.Log;
-import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -34,6 +32,7 @@
     protected ViewGroup mViewRoot;
     protected ViewGroup mTopEndContainer;
     protected View mDragHandle;
+    protected View mEnterSplitButton;
     protected View mSettingsButton;
     protected View mDismissButton;
 
@@ -44,14 +43,13 @@
      * Bind the necessary views.
      */
     public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
-            View settingsButton, View dismissButton) {
+            View enterSplitButton, View settingsButton, View dismissButton) {
         mViewRoot = viewRoot;
         mTopEndContainer = topEndContainer;
         mDragHandle = dragHandle;
+        mEnterSplitButton = enterSplitButton;
         mSettingsButton = settingsButton;
         mDismissButton = dismissButton;
-
-        bindInitialViewState();
     }
 
     /**
@@ -72,22 +70,4 @@
             v.setLayoutParams(params);
         }
     }
-
-    /** Calculate the initial state of the menu icons. Called when the menu is first created. */
-    private void bindInitialViewState() {
-        if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
-                || mSettingsButton == null || mDismissButton == null) {
-            Log.e(TAG, "One of the required views is null.");
-            return;
-        }
-        // The menu view layout starts out with the settings button aligned at the top|end of the
-        // view group next to the dismiss button. On phones, the settings button should be aligned
-        // to the top|start of the view, so move it to parent view group to then align it to the
-        // top|start of the menu.
-        mTopEndContainer.removeView(mSettingsButton);
-        mViewRoot.addView(mSettingsButton);
-
-        setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
-        setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 8ef2b6b..7bbebe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -99,7 +99,7 @@
     private static final float MENU_BACKGROUND_ALPHA = 0.3f;
     private static final float DISABLED_ACTION_ALPHA = 0.54f;
 
-    private static final boolean ENABLE_RESIZE_HANDLE = false;
+    private static final boolean ENABLE_ENTER_SPLIT = false;
 
     private int mMenuState;
     private boolean mAllowMenuTimeout = true;
@@ -139,7 +139,7 @@
     protected View mViewRoot;
     protected View mSettingsButton;
     protected View mDismissButton;
-    protected View mResizeHandle;
+    protected View mEnterSplitButton;
     protected View mTopEndContainer;
     protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
 
@@ -177,14 +177,23 @@
             }
         });
 
-        mResizeHandle = findViewById(R.id.resize_handle);
-        mResizeHandle.setAlpha(0);
+        mEnterSplitButton = findViewById(R.id.enter_split);
+        mEnterSplitButton.setAlpha(0);
+        mEnterSplitButton.setOnClickListener(v -> {
+            if (mMenuContainer.getAlpha() != 0) {
+                enterSplit();
+            }
+        });
+
+        findViewById(R.id.resize_handle).setAlpha(0);
+
         mActionsGroup = findViewById(R.id.actions_group);
         mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
                 R.dimen.pip_between_action_padding_land);
         mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
         mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
-                mResizeHandle, mSettingsButton, mDismissButton);
+                findViewById(R.id.resize_handle), mEnterSplitButton, mSettingsButton,
+                mDismissButton);
         mDismissFadeOutDurationMs = context.getResources()
                 .getInteger(R.integer.config_pipExitAnimationDuration);
 
@@ -268,14 +277,13 @@
                     mSettingsButton.getAlpha(), 1f);
             ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
                     mDismissButton.getAlpha(), 1f);
-            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
-                    mResizeHandle.getAlpha(),
-                    ENABLE_RESIZE_HANDLE && showResizeHandle ? 1f : 0f);
+            ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+                    mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
             if (menuState == MENU_STATE_FULL) {
                 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
-                        resizeAnim);
+                        enterSplitAnim);
             } else {
-                mMenuContainerAnimator.playTogether(resizeAnim);
+                mMenuContainerAnimator.playTogether(enterSplitAnim);
             }
             mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
             mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -328,7 +336,7 @@
         mMenuContainer.setAlpha(0f);
         mSettingsButton.setAlpha(0f);
         mDismissButton.setAlpha(0f);
-        mResizeHandle.setAlpha(0f);
+        mEnterSplitButton.setAlpha(0f);
     }
 
     void pokeMenu() {
@@ -368,9 +376,10 @@
                     mSettingsButton.getAlpha(), 0f);
             ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
                     mDismissButton.getAlpha(), 0f);
-            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
-                    mResizeHandle.getAlpha(), 0f);
-            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
+            ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+                    mEnterSplitButton.getAlpha(), 0f);
+            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
+                    enterSplitAnim);
             mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
             mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType));
             mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
@@ -522,6 +531,14 @@
         }
     }
 
+    private void enterSplit() {
+        // Do not notify menu visibility when hiding the menu, the controller will do this when it
+        // handles the message
+        hideMenu(mController::onEnterSplit, false /* notifyMenuVisibility */, true /* resize */,
+                ANIM_TYPE_HIDE);
+    }
+
+
     private void showSettings() {
         final Pair<ComponentName, Integer> topPipActivityInfo =
                 PipUtils.getTopPipActivity(mContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index c42750d..96fd59f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -337,22 +337,29 @@
      * Resizes the pinned stack back to unknown windowing mode, which could be freeform or
      *      * fullscreen depending on the display area's windowing mode.
      */
-    void expandLeavePip() {
-        expandLeavePip(false /* skipAnimation */);
+    void expandLeavePip(boolean skipAnimation) {
+        expandLeavePip(skipAnimation, false /* enterSplit */);
+    }
+
+    /**
+     * Resizes the pinned task to split-screen mode.
+     */
+    void expandIntoSplit() {
+        expandLeavePip(false, true /* enterSplit */);
     }
 
     /**
      * Resizes the pinned stack back to unknown windowing mode, which could be freeform or
      * fullscreen depending on the display area's windowing mode.
      */
-    void expandLeavePip(boolean skipAnimation) {
+    private void expandLeavePip(boolean skipAnimation, boolean enterSplit) {
         if (DEBUG) {
             Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
         cancelPhysicsAnimation();
         mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
-        mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION);
+        mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION, enterSplit);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 9f2f6a5..570fd5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -139,7 +139,12 @@
 
         @Override
         public void onPipExpand() {
-            mMotionHelper.expandLeavePip();
+            mMotionHelper.expandLeavePip(false /* skipAnimation */);
+        }
+
+        @Override
+        public void onEnterSplit() {
+            mMotionHelper.expandIntoSplit();
         }
 
         @Override
@@ -899,7 +904,7 @@
                     // Expand to fullscreen if this is a double tap
                     // the PiP should be frozen until the transition ends
                     setTouchEnabled(false);
-                    mMotionHelper.expandLeavePip();
+                    mMotionHelper.expandLeavePip(false /* skipAnimation */);
                 }
             } else if (mMenuState != MENU_STATE_FULL) {
                 if (mPipBoundsState.isStashed()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index a2e9b64..00083d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -219,7 +219,7 @@
     public void movePipToFullscreen() {
         if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState));
 
-        mPipTaskOrganizer.exitPip(mResizeAnimationDuration);
+        mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
         onPipDisappeared();
     }
 
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 14a6574..dbd0ff8 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
@@ -160,11 +160,25 @@
         return moveToSideStage(task, sideStagePosition);
     }
 
+    public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
+            WindowContainerTransaction wct) {
+        final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
+        if (task == null) {
+            throw new IllegalArgumentException("Unknown taskId" + taskId);
+        }
+        return moveToSideStage(task, sideStagePosition, wct);
+    }
+
     public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
             @SplitPosition int sideStagePosition) {
         return mStageCoordinator.moveToSideStage(task, sideStagePosition);
     }
 
+    public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+            @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
+        return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
+    }
+
     public boolean removeFromSideStage(int taskId) {
         return mStageCoordinator.removeFromSideStage(taskId);
     }
@@ -182,6 +196,11 @@
                 leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
     }
 
+    public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
+        moveToSideStage(taskId,
+                leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+    }
+
     public void exitSplitScreen(int toTopTaskId, int exitReason) {
         mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
     }
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 3345613..a94a081 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
@@ -271,6 +271,11 @@
     boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
             @SplitPosition int sideStagePosition) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
+        return moveToSideStage(task, sideStagePosition, wct);
+    }
+
+    boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+            @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         setSideStagePosition(sideStagePosition, wct);
         mSideStage.evictAllChildren(evictWct);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 0270093..0172cf32 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -50,6 +50,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -75,7 +76,8 @@
     @Mock private PipTransitionController mMockPipTransitionController;
     @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
     @Mock private PipUiEventLogger mMockPipUiEventLogger;
-    @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen;
+    @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
+    @Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
     @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
     private TestShellExecutor mMainExecutor;
     private PipBoundsState mPipBoundsState;
@@ -99,8 +101,9 @@
                 mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
                 mPipBoundsAlgorithm, mMockPhonePipMenuController,
                 mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
-                mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
-                mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+                mMockPipTransitionController, mMockOptionalLegacySplitScreen,
+                mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
+                mMockShellTaskOrganizer, mMainExecutor));
         mMainExecutor.flushAll();
         preparePipTaskOrg();
     }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 50117ce..cae2d0b 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2677,21 +2677,30 @@
                 // DENSITY_ANY is now dealt with. We should look to
                 // pick a density bucket and potentially scale it.
                 // Any density is potentially useful
-                // because the system will scale it.  Always prefer
-                // scaling down.
+                // because the system will scale it.  Scaling down
+                // is generally better than scaling up.
                 int h = thisDensity;
                 int l = otherDensity;
                 bool bImBigger = true;
                 if (l > h) {
-                    std::swap(l, h);
+                    int t = h;
+                    h = l;
+                    l = t;
                     bImBigger = false;
                 }
 
+                if (requestedDensity >= h) {
+                    // requested value higher than both l and h, give h
+                    return bImBigger;
+                }
                 if (l >= requestedDensity) {
                     // requested value lower than both l and h, give l
                     return !bImBigger;
+                }
+                // saying that scaling down is 2x better than up
+                if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) {
+                    return !bImBigger;
                 } else {
-                    // otherwise give h
                     return bImBigger;
                 }
             }
diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp
index f5fd0f2..b54915f 100644
--- a/libs/androidfw/tests/Config_test.cpp
+++ b/libs/androidfw/tests/Config_test.cpp
@@ -27,19 +27,15 @@
 
 static ResTable_config selectBest(const ResTable_config& target,
                                   const Vector<ResTable_config>& configs) {
-  Vector<ResTable_config> matchedConfigs;
+  ResTable_config bestConfig;
+  memset(&bestConfig, 0, sizeof(bestConfig));
   const size_t configCount = configs.size();
   for (size_t i = 0; i < configCount; i++) {
     const ResTable_config& thisConfig = configs[i];
-    if (thisConfig.match(target)) {
-      matchedConfigs.add(thisConfig);
+    if (!thisConfig.match(target)) {
+      continue;
     }
-  }
 
-  ResTable_config bestConfig = matchedConfigs[0];
-  const size_t matchingConfigCount = matchedConfigs.size();
-  for (size_t i = 1; i < matchingConfigCount; i++) {
-    const ResTable_config& thisConfig = configs[i];
     if (thisConfig.isBetterThan(bestConfig, &target)) {
       bestConfig = thisConfig;
     }
@@ -79,9 +75,6 @@
   configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20));
   ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
 
-  configs.add(buildDensityConfig(int(ResTable_config::DENSITY_XHIGH) - 1));
-  ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
-
   expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH);
   configs.add(expectedBest);
   ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 2636ab2..8dcdc98 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -953,7 +953,7 @@
     Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
     android::content::AttributionSourceState attributionSource;
     attributionSource.readFromParcel(parcel);
-    sp<MediaPlayer> mp = new MediaPlayer(attributionSource);
+    sp<MediaPlayer> mp = sp<MediaPlayer>::make(attributionSource);
     if (mp == NULL) {
         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
         return;
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index f0b59d8..1f10e5d 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<androidx.constraintlayout.widget.ConstraintLayout
+<androidx.constraintlayout.motion.widget.MotionLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/split_shade_status_bar"
@@ -26,7 +26,8 @@
     android:paddingLeft="@dimen/qs_panel_padding"
     android:paddingRight="@dimen/qs_panel_padding"
     android:visibility="gone"
-    android:theme="@style/Theme.SystemUI.QuickSettings.Header">
+    android:theme="@style/Theme.SystemUI.QuickSettings.Header"
+    app:layoutDescription="@xml/combined_qs_header_scene">
 
     <androidx.constraintlayout.widget.Guideline
         android:layout_width="wrap_content"
@@ -39,76 +40,65 @@
         android:id="@+id/clock"
         android:layout_width="wrap_content"
         android:layout_height="0dp"
-        app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
         android:gravity="start|center_vertical"
         android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
         android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.QS.Status"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/date"
     />
 
     <com.android.systemui.statusbar.policy.DateView
         android:id="@+id/date"
         android:layout_width="wrap_content"
         android:layout_height="0dp"
-        app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
         android:layout_gravity="start|center_vertical"
         android:gravity="center_vertical"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.QS.Status"
         app:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
-        app:layout_constraintStart_toEndOf="@id/clock"
-        app:layout_constraintEnd_toStartOf="@id/carrier_group"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintHorizontal_bias="0"
     />
 
     <include
         android:id="@+id/carrier_group"
         layout="@layout/qs_carrier_group"
+        app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+        android:minHeight="@dimen/split_shade_header_min_height"
+        app:layout_constraintWidth_min="48dp"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
         app:layout_constrainedWidth="true"
         android:layout_gravity="end|center_vertical"
         android:layout_marginStart="8dp"
-        android:focusable="false"
-        android:minHeight="@dimen/split_shade_header_min_height"
-        android:minWidth="48dp"
         app:layout_constraintStart_toEndOf="@id/date"
         app:layout_constraintEnd_toStartOf="@id/statusIcons"
-        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintTop_toTopOf="@id/clock"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintHorizontal_bias="1"
     />
 
     <com.android.systemui.statusbar.phone.StatusIconContainer
         android:id="@+id/statusIcons"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
         app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
         android:paddingEnd="@dimen/signal_cluster_battery_padding"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
         app:layout_constraintStart_toEndOf="@id/carrier_group"
         app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
-        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintTop_toTopOf="@id/clock"
         app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintHorizontal_bias="1"
     />
 
     <com.android.systemui.battery.BatteryMeterView
         android:id="@+id/batteryRemainingIcon"
         android:layout_width="wrap_content"
-        android:layout_height="0dp"
+        android:layout_height="48dp"
         app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
         app:textAppearance="@style/TextAppearance.QS.Status"
         app:layout_constraintStart_toEndOf="@id/statusIcons"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintTop_toTopOf="@id/clock"
         app:layout_constraintBottom_toBottomOf="parent"
     />
 
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</androidx.constraintlayout.motion.widget.MotionLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 5176d96..6f5d7be 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -18,7 +18,7 @@
 -->
 
 <!-- This is the status bar window. -->
-<com.android.systemui.statusbar.phone.StatusBarWindowView
+<com.android.systemui.statusbar.window.StatusBarWindowView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:sysui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -35,4 +35,4 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@drawable/system_bar_background" />
-</com.android.systemui.statusbar.phone.StatusBarWindowView>
+</com.android.systemui.statusbar.window.StatusBarWindowView>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 96433e5..fc6b99a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -577,9 +577,6 @@
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">false</bool>
 
-    <!-- Determines whether the shell features all run on another thread. -->
-    <bool name="config_enableShellMainThread">false</bool>
-
     <!-- Default udfps icon. Same path as ic_fingerprint.xml -->
     <string name="config_udfpsIcon" translatable="false">
         M25.5,16.3283C28.47,14.8433 31.9167,14 35.5834,14C39.2501,14 42.6968,14.8433 45.6668,16.3283
diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
new file mode 100644
index 0000000..d61e4a9
--- /dev/null
+++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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.
+  -->
+<MotionScene
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <Transition
+        android:id="@+id/header_transition"
+        app:constraintSetEnd="@id/qs_header_constraint"
+        app:constraintSetStart="@id/qqs_header_constraint">
+        <KeyFrameSet>
+            <KeyPosition
+                app:keyPositionType="pathRelative"
+                app:percentX="0"
+                app:framePosition="50"
+                app:motionTarget="@id/date" />
+        </KeyFrameSet>
+    </Transition>
+
+    <Transition
+        android:id="@+id/split_header_transition"
+        app:constraintSetStart="@id/split_header_constraint"
+        app:constraintSetEnd="@id/split_header_constraint"/>
+
+    <!--
+        Placeholder ConstraintSet. They are populated in the controller for this class.
+        This is needed because there's no easy way to just refer to a `ConstraintSet` file. The
+        options are either a layout file or inline the ConstraintSets.
+     -->
+    <ConstraintSet android:id="@id/qqs_header_constraint"/>
+
+    <ConstraintSet android:id="@id/qs_header_constraint"/>
+
+    <ConstraintSet android:id="@id/split_header_constraint" />
+
+</MotionScene>
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index fdf73d5..3d7b549 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -17,66 +17,45 @@
 
 <ConstraintSet
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/qqs_header_constraint"
+>
 
     <Constraint
-        android:id="@+id/clock"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/date"
-        app:layout_constraintHorizontal_bias="0"
-        app:layout_constraintHorizontal_chainStyle="packed"
-    />
+        android:id="@+id/clock">
+        <Layout
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/date"
+            app:layout_constraintHorizontal_bias="0"
+            app:layout_constraintHorizontal_chainStyle="packed"
+        />
+    </Constraint>
 
     <Constraint
-        android:id="@+id/date"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_constraintStart_toEndOf="@id/clock"
-        app:layout_constraintEnd_toStartOf="@id/carrier_group"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintHorizontal_bias="0"
-    />
+        android:id="@+id/date">
+        <Layout
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            app:layout_constraintStart_toEndOf="@id/clock"
+            app:layout_constraintEnd_toStartOf="@id/carrier_group"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintHorizontal_bias="0"
+        />
+    </Constraint>
 
     <Constraint
-        android:id="@+id/carrier_group"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        app:layout_constrainedWidth="true"
-        android:layout_gravity="end|center_vertical"
-        android:layout_marginStart="8dp"
-        app:layout_constraintStart_toEndOf="@id/date"
-        app:layout_constraintEnd_toStartOf="@id/statusIcons"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        android:visibility="invisible"
-        app:layout_constraintHorizontal_bias="1"
-    />
+        android:id="@+id/carrier_group">
+        <CustomAttribute
+            app:attributeName="alpha"
+            app:customFloatValue="0"
+        />
+    </Constraint>
 
-    <Constraint
-        android:id="@+id/statusIcons"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_constraintStart_toEndOf="@id/carrier_group"
-        app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintHorizontal_bias="1"
-    />
 
-    <Constraint
-        android:id="@+id/batteryRemainingIcon"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_constraintStart_toEndOf="@id/statusIcons"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintHorizontal_bias="1"
-    />
 
 </ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index 72e518e..6a0ab86 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -17,63 +17,46 @@
 
 <ConstraintSet
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/qs_header_constraint"
+>
 
     <Constraint
-        android:id="@+id/clock"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/date"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/carrier_group"
-        app:layout_constraintHorizontal_bias="0"
-    />
+        android:id="@+id/clock">
+        <Layout
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/date"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/carrier_group"
+            app:layout_constraintHorizontal_bias="0"
+        />
+    </Constraint>
 
     <Constraint
-        android:id="@+id/date"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toTopOf="@id/clock"
-        app:layout_constraintHorizontal_bias="0"
-    />
+        android:id="@+id/date">
+        <Layout
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/clock"
+            app:layout_constraintHorizontal_bias="0"
+        />
+        <Motion
+            app:motionStagger="0.5"
+        />
+    </Constraint>
 
     <Constraint
-        android:id="@+id/carrier_group"
-        android:layout_width="0dp"
-        android:layout_height="48dp"
-        app:layout_constrainedWidth="true"
-        android:layout_gravity="end|center_vertical"
-        android:layout_marginStart="8dp"
-        app:layout_constraintStart_toEndOf="@id/clock"
-        app:layout_constraintEnd_toStartOf="@id/statusIcons"
-        app:layout_constraintTop_toTopOf="@id/clock"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintHorizontal_bias="1"
-    />
+        android:id="@+id/carrier_group">
+        <CustomAttribute
+            app:attributeName="alpha"
+            app:customFloatValue="1"
+        />
+    </Constraint>
 
-    <Constraint
-        android:id="@+id/statusIcons"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        app:layout_constraintStart_toEndOf="@id/carrier_group"
-        app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
-        app:layout_constraintTop_toTopOf="@id/clock"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintHorizontal_bias="1"
-    />
-
-    <Constraint
-        android:id="@+id/batteryRemainingIcon"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        app:layout_constraintStart_toEndOf="@id/statusIcons"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="@id/clock"
-        app:layout_constraintBottom_toBottomOf="parent"
-    />
 
 </ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/split_header.xml b/packages/SystemUI/res/xml/split_header.xml
index a3ee1e2..44d42a0 100644
--- a/packages/SystemUI/res/xml/split_header.xml
+++ b/packages/SystemUI/res/xml/split_header.xml
@@ -17,61 +17,41 @@
 
 <ConstraintSet
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/split_header_constraint">
 
     <Constraint
-        android:id="@+id/clock"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/date"
-    />
+        android:id="@+id/clock">
+        <Layout
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/date"
+        />
+    </Constraint>
 
     <Constraint
-        android:id="@+id/date"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_constraintStart_toEndOf="@id/clock"
-        app:layout_constraintEnd_toStartOf="@id/carrier_group"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintHorizontal_bias="0"
-    />
+        android:id="@+id/date">
+        <Layout
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            app:layout_constraintStart_toEndOf="@id/clock"
+            app:layout_constraintEnd_toStartOf="@id/carrier_group"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintHorizontal_bias="0"
+        />
+    </Constraint>
 
     <Constraint
-        android:id="@+id/carrier_group"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        app:layout_constrainedWidth="true"
-        android:layout_gravity="end|center_vertical"
-        android:layout_marginStart="8dp"
-        app:layout_constraintStart_toEndOf="@id/date"
-        app:layout_constraintEnd_toStartOf="@id/statusIcons"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintHorizontal_bias="1"
-    />
+        android:id="@+id/carrier_group">
+        <PropertySet
+            android:alpha="1"
+            app:customFloatValue="1"
+        />
+    </Constraint>
 
-    <Constraint
-        android:id="@+id/statusIcons"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_constraintStart_toEndOf="@id/carrier_group"
-        app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-    />
-
-    <Constraint
-        android:id="@+id/batteryRemainingIcon"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_constraintStart_toEndOf="@id/statusIcons"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-    />
 
 </ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 2f5a18e..7a39029 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -105,7 +105,6 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -129,6 +128,7 @@
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index b7398d8..dacc169 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.biometrics
 
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
 import android.content.Context
 import android.graphics.PixelFormat
 import android.graphics.PorterDuff
@@ -33,6 +35,8 @@
 import android.view.LayoutInflater
 import android.view.Surface
 import android.view.View
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
 import android.view.WindowManager
 import androidx.annotation.RawRes
 import com.airbnb.lottie.LottieAnimationView
@@ -42,6 +46,7 @@
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
 
@@ -56,9 +61,10 @@
     private val layoutInflater: LayoutInflater,
     fingerprintManager: FingerprintManager?,
     private val windowManager: WindowManager,
-    @Main mainExecutor: DelayableExecutor,
+    overviewProxyService: OverviewProxyService,
     displayManager: DisplayManager,
-    @Main handler: Handler
+    @Main mainExecutor: DelayableExecutor,
+    @Main private val handler: Handler
 ) {
     @VisibleForTesting
     val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager
@@ -74,15 +80,33 @@
         BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
     ) { onOrientationChanged() }
 
+    @VisibleForTesting
+    val overviewProxyListener = object : OverviewProxyService.OverviewProxyListener {
+        override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
+            overlayView?.let { view ->
+                handler.postDelayed({ updateOverlayVisibility(view) }, 500)
+            }
+        }
+    }
+
+    private val animationDuration =
+        context.resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
+
+    private var overlayHideAnimator: ViewPropertyAnimator? = null
+
     private var overlayView: View? = null
         set(value) {
             field?.let { oldView ->
                 windowManager.removeView(oldView)
                 orientationListener.disable()
             }
+            overlayHideAnimator?.cancel()
+            overlayHideAnimator = null
+
             field = value
             field?.let { newView ->
                 windowManager.addView(newView, overlayViewParams)
+                updateOverlayVisibility(newView)
                 orientationListener.enable()
             }
         }
@@ -90,11 +114,8 @@
     private val overlayViewParams = WindowManager.LayoutParams(
         WindowManager.LayoutParams.WRAP_CONTENT,
         WindowManager.LayoutParams.WRAP_CONTENT,
-        WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
-        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+        WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
+        Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
         PixelFormat.TRANSLUCENT
     ).apply {
         title = TAG
@@ -121,6 +142,7 @@
 
             override fun hide(sensorId: Int) = mainExecutor.execute { overlayView = null }
         })
+        overviewProxyService.addCallback(overviewProxyListener)
     }
 
     private fun onOrientationChanged() {
@@ -176,6 +198,33 @@
         overlayViewParams.x = x
         overlayViewParams.y = y
     }
+
+    private fun updateOverlayVisibility(view: View) {
+        if (view != overlayView) {
+            return
+        }
+
+        // hide after a few seconds if the sensor is oriented down and there are
+        // large overlapping system bars
+        if ((context.display?.rotation == Surface.ROTATION_270) &&
+            windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar()) {
+            overlayHideAnimator = view.animate()
+                .alpha(0f)
+                .setStartDelay(3_000)
+                .setDuration(animationDuration)
+                .setListener(object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator) {
+                        view.visibility = View.GONE
+                        overlayHideAnimator = null
+                    }
+                })
+        } else {
+            overlayHideAnimator?.cancel()
+            overlayHideAnimator = null
+            view.alpha = 1f
+            view.visibility = View.VISIBLE
+        }
+    }
 }
 
 @BiometricOverlayConstants.ShowReason
@@ -200,6 +249,9 @@
 private fun Display.isPortrait(): Boolean =
     rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
 
+private fun WindowInsets.hasBigNavigationBar(): Boolean =
+    getInsets(WindowInsets.Type.navigationBars()).bottom >= 70
+
 private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
     fun update() {
         val c = context.getColor(R.color.biometric_dialog_accent)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 778aff3..b484029 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -583,7 +583,7 @@
 
         mCoreLayoutParams = new WindowManager.LayoutParams(
                 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
-                getCoreLayoutParamFlags(),
+                Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
                 PixelFormat.TRANSLUCENT);
         mCoreLayoutParams.setTitle(TAG);
         mCoreLayoutParams.setFitInsetsTypes(0);
@@ -616,13 +616,6 @@
         }
     }
 
-    private int getCoreLayoutParamFlags() {
-        return WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-    }
-
     @Nullable
     private FingerprintSensorPropertiesInternal findFirstUdfps() {
         for (FingerprintSensorPropertiesInternal props :
@@ -685,7 +678,7 @@
         final int paddingX = animation != null ? animation.getPaddingX() : 0;
         final int paddingY = animation != null ? animation.getPaddingY() : 0;
 
-        mCoreLayoutParams.flags = getCoreLayoutParamFlags();
+        mCoreLayoutParams.flags = Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS;
         if (animation != null && animation.listenForTouchesOutsideView()) {
             mCoreLayoutParams.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index 322584c..6989547 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -31,6 +31,7 @@
 import android.os.UserManager;
 import android.util.DisplayMetrics;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
@@ -46,6 +47,13 @@
     public static final int CREDENTIAL_PATTERN = 2;
     public static final int CREDENTIAL_PASSWORD = 3;
 
+    /** Base set of layout flags for fingerprint overlay widgets. */
+    public static final int FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS =
+            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD})
     @interface CredentialType {}
@@ -55,11 +63,6 @@
                 / DisplayMetrics.DENSITY_DEFAULT);
     }
 
-    static float pixelsToDp(Context context, float pixels) {
-        return pixels / ((float) context.getResources().getDisplayMetrics().densityDpi
-                / DisplayMetrics.DENSITY_DEFAULT);
-    }
-
     static void notifyAccessibilityContentChanged(AccessibilityManager am, ViewGroup view) {
         if (!am.isEnabled()) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4cecb39..dcb44cc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -73,6 +73,7 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
 import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
+import com.android.systemui.statusbar.window.StatusBarWindowModule;
 import com.android.systemui.tuner.dagger.TunerModule;
 import com.android.systemui.user.UserModule;
 import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
@@ -116,6 +117,7 @@
             SettingsUtilModule.class,
             SmartRepliesInflationModule.class,
             StatusBarPolicyModule.class,
+            StatusBarWindowModule.class,
             SysUIConcurrencyModule.class,
             TunerModule.class,
             UserModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 634349e..d8b7742 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -20,13 +20,14 @@
 
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.tv.TvWMComponent;
-import com.android.systemui.wmshell.TvWMShellModule;
-import com.android.systemui.wmshell.WMShellModule;
+import com.android.wm.shell.dagger.TvWMShellModule;
+import com.android.wm.shell.dagger.WMShellModule;
 import com.android.wm.shell.ShellCommandHandler;
 import com.android.wm.shell.ShellInit;
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.dagger.WMSingleton;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 1343bdb..7770d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -44,6 +44,7 @@
 import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.VariableDateView;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
 
 import java.util.List;
 
@@ -312,6 +313,10 @@
     }
 
     private void updateAnimators() {
+        if (mUseCombinedQSHeader) {
+            mTranslationAnimator = null;
+            return;
+        }
         updateAlphaAnimator();
         int offset = mTopViewMeasureHeight;
 
@@ -324,6 +329,10 @@
     }
 
     private void updateAlphaAnimator() {
+        if (mUseCombinedQSHeader) {
+            mAlphaAnimator = null;
+            return;
+        }
         TouchAnimator.Builder builder = new TouchAnimator.Builder()
                 .addFloat(mSecurityHeaderView, "alpha", 0, 1)
                 // These views appear on expanding down
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index bb697c3..74ea19f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -67,11 +67,13 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowModule;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 7e4db03..d5a0467 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -26,7 +26,7 @@
 import android.widget.FrameLayout
 import com.android.systemui.R
 import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
-import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowController
 import javax.inject.Inject
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index dcf8e73..5a27329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -31,8 +31,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.phone.StatusBarWindowController
 import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.Assert
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.time.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index 80577ee..7cdf69d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.shared.system.InputChannelCompat
 import com.android.systemui.shared.system.InputMonitorCompat
-import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowController
 import javax.inject.Inject
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index f068a8e..00b54e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -47,6 +47,7 @@
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index fbe59a2..e66ad61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 09bcb69..0b3e040 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -56,6 +56,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.tuner.TunerService;
 
 import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index e3d38b8..5305ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -37,6 +37,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
 import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.Objects;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index bac4234..c814622 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -17,8 +17,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.view.View
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.motion.widget.MotionLayout
 import com.android.systemui.R
 import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.battery.BatteryMeterView
@@ -39,6 +38,11 @@
     batteryMeterViewController: BatteryMeterViewController
 ) {
 
+    companion object {
+        private val HEADER_TRANSITION_ID = R.id.header_transition
+        private val SPLIT_HEADER_TRANSITION_ID = R.id.split_header_transition
+    }
+
     private val combinedHeaders = featureFlags.useCombinedQSHeaders()
     // TODO(b/194178072) Handle RSSI hiding when multi carrier
     private val iconManager: StatusBarIconController.IconManager
@@ -59,6 +63,7 @@
             }
             field = value
             updateVisibility()
+            updatePosition()
         }
 
     var splitShadeMode = false
@@ -68,6 +73,7 @@
             }
             field = value
             updateVisibility()
+            updateConstraints()
         }
 
     var shadeExpandedFraction = -1f
@@ -83,13 +89,22 @@
             if (visible && field != value) {
                 field = value
                 updateVisibility()
+                updatePosition()
             }
         }
 
-    private val constraintSplit = ConstraintSet()
-            .apply { load(statusBar.context, R.xml.split_header) }
-    private val constraintQQS = ConstraintSet().apply { load(statusBar.context, R.xml.qqs_header) }
-    private val constraintQS = ConstraintSet().apply { load(statusBar.context, R.xml.qs_header) }
+    init {
+        if (statusBar is MotionLayout) {
+            val context = statusBar.context
+            val resources = statusBar.resources
+            statusBar.getConstraintSet(R.id.qqs_header_constraint)
+                    .load(context, resources.getXml(R.xml.qqs_header))
+            statusBar.getConstraintSet(R.id.qs_header_constraint)
+                    .load(context, resources.getXml(R.xml.qs_header))
+            statusBar.getConstraintSet(R.id.split_header_constraint)
+                    .load(context, resources.getXml(R.xml.split_header))
+        }
+    }
 
     init {
         batteryMeterViewController.init()
@@ -104,6 +119,8 @@
         qsCarrierGroupController = qsCarrierGroupControllerBuilder
                 .setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
                 .build()
+        updateVisibility()
+        updateConstraints()
     }
 
     private fun updateVisibility() {
@@ -118,20 +135,25 @@
             statusBar.visibility = visibility
             visible = visibility == View.VISIBLE
         }
-        updateConstraints()
     }
 
     private fun updateConstraints() {
         if (!combinedHeaders) {
             return
         }
-        statusBar as ConstraintLayout
+        statusBar as MotionLayout
         if (splitShadeMode) {
-            constraintSplit.applyTo(statusBar)
-        } else if (qsExpandedFraction == 1f) {
-            constraintQS.applyTo(statusBar)
+            statusBar.setTransition(SPLIT_HEADER_TRANSITION_ID)
         } else {
-            constraintQQS.applyTo(statusBar)
+            statusBar.setTransition(HEADER_TRANSITION_ID)
+            statusBar.transitionToStart()
+            updatePosition()
+        }
+    }
+
+    private fun updatePosition() {
+        if (statusBar is MotionLayout && !splitShadeMode && visible) {
+            statusBar.setProgress(qsExpandedFraction)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0f0810d..315ef77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -229,6 +229,8 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
@@ -467,7 +469,7 @@
     private AuthRippleController mAuthRippleController;
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     protected NotificationShadeWindowController mNotificationShadeWindowController;
-    protected StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowController mStatusBarWindowController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @VisibleForTesting
     DozeServiceHost mDozeServiceHost;
@@ -701,6 +703,7 @@
             NotificationsController notificationsController,
             LightBarController lightBarController,
             AutoHideController autoHideController,
+            StatusBarWindowController statusBarWindowController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             PulseExpansionHandler pulseExpansionHandler,
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
@@ -802,6 +805,7 @@
         mNotificationsController = notificationsController;
         mLightBarController = lightBarController;
         mAutoHideController = autoHideController;
+        mStatusBarWindowController = statusBarWindowController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mPulseExpansionHandler = pulseExpansionHandler;
         mWakeUpCoordinator = notificationWakeUpCoordinator;
@@ -1588,7 +1592,6 @@
                 .getNotificationShadeWindowViewController();
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowViewController.setupExpandedStatusBar();
-        mStatusBarWindowController = statusBarComponent.getStatusBarWindowController();
         mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
         statusBarComponent.getLockIconViewController().init();
         mStackScrollerController = statusBarComponent.getNotificationStackScrollLayoutController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index ca877af..6eeae7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 
 import javax.inject.Inject;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index 418f588..e06605e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -29,7 +29,6 @@
 import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
 import com.android.systemui.statusbar.phone.StatusBarDemoMode;
 import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
-import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
@@ -82,12 +81,6 @@
     NotificationShadeWindowViewController getNotificationShadeWindowViewController();
 
     /**
-     * Creates a StatusBarWindowViewController.
-     */
-    @StatusBarScope
-    StatusBarWindowController getStatusBarWindowController();
-
-    /**
      * Creates a NotificationPanelViewController.
      */
     @StatusBarScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 4f43426..8f57faa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -108,6 +108,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
@@ -146,6 +147,7 @@
             NotificationsController notificationsController,
             LightBarController lightBarController,
             AutoHideController autoHideController,
+            StatusBarWindowController statusBarWindowController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             PulseExpansionHandler pulseExpansionHandler,
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
@@ -249,6 +251,7 @@
                 notificationsController,
                 lightBarController,
                 autoHideController,
+                statusBarWindowController,
                 keyguardUpdateMonitor,
                 pulseExpansionHandler,
                 notificationWakeUpCoordinator,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 31cc823..1225813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -39,8 +39,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.phone.StatusBarWindowController
 import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.time.SystemClock
 import java.io.FileDescriptor
 import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index b26aab86..700b58a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.window;
 
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
@@ -34,7 +34,6 @@
 import android.util.Log;
 import android.view.Gravity;
 import android.view.IWindowManager;
-import android.view.LayoutInflater;
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
@@ -47,6 +46,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 
 import java.util.Optional;
 
@@ -78,6 +78,7 @@
     @Inject
     public StatusBarWindowController(
             Context context,
+            @StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView,
             WindowManager windowManager,
             IWindowManager iWindowManager,
             StatusBarContentInsetsProvider contentInsetsProvider,
@@ -86,7 +87,7 @@
         mWindowManager = windowManager;
         mIWindowManager = iWindowManager;
         mContentInsetsProvider = contentInsetsProvider;
-        mStatusBarWindowView = createWindowView(mContext);
+        mStatusBarWindowView = statusBarWindowView;
         mLaunchAnimationContainer = mStatusBarWindowView.findViewById(
                 R.id.status_bar_launch_animation_container);
         mLpChanged = new WindowManager.LayoutParams();
@@ -294,14 +295,4 @@
             mLpChanged.privateFlags &= ~PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
         }
     }
-
-    private ViewGroup createWindowView(Context context) {
-        ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(
-                R.layout.super_status_bar, /* root= */ null);
-        if (view == null) {
-            throw new IllegalStateException(
-                    "R.layout.super_status_bar could not be properly inflated");
-        }
-        return view;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
new file mode 100644
index 0000000..874217a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
@@ -0,0 +1,47 @@
+package com.android.systemui.statusbar.window
+
+import android.view.LayoutInflater
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
+
+/** Module providing dependencies related to the status bar window. */
+@Module
+abstract class StatusBarWindowModule {
+    /**
+     * Provides a [StatusBarWindowView].
+     *
+     * Only [StatusBarWindowController] should inject the view.
+     */
+    @Module
+    companion object {
+        @JvmStatic
+        @Provides
+        @SysUISingleton
+        @InternalWindowView
+        fun providesStatusBarWindowView(layoutInflater: LayoutInflater): StatusBarWindowView {
+            return layoutInflater.inflate(
+                R.layout.super_status_bar,
+                /* root= */null
+            ) as StatusBarWindowView?
+                ?: throw IllegalStateException(
+                    "R.layout.super_status_bar could not be properly inflated"
+                )
+        }
+    }
+
+    /**
+     * We want [StatusBarWindowView] to be provided to [StatusBarWindowController]'s constructor via
+     * dagger so that we can provide a fake window view when testing the controller. However, we wan
+     * want *only* the controller to be able to inject the window view.
+     *
+     * This protected qualifier annotation achieves this. [StatusBarWindowView] can only be injected
+     * if it's annotated with [InternalWindowView], and only classes inside this [statusbar.window]
+     * package can access the annotation.
+     */
+    @Retention(AnnotationRetention.BINARY)
+    @Qualifier
+    protected annotation class InternalWindowView
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
index eb8c7ad..06cc96e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.window;
 
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
@@ -36,7 +36,7 @@
 public class StatusBarWindowView extends FrameLayout {
 
     public static final String TAG = "PhoneStatusBarWindowView";
-    public static final boolean DEBUG = StatusBar.DEBUG;
+    public static final boolean DEBUG = false;
 
     private int mLeftInset = 0;
     private int mRightInset = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
index f678513..8370615 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
@@ -17,8 +17,8 @@
 package com.android.systemui.tv;
 
 import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.dagger.WMSingleton;
-import com.android.systemui.wmshell.TvWMShellModule;
+import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.dagger.TvWMShellModule;
 
 import dagger.Subcomponent;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index 5fee7fb..c98a504 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.biometrics
 
+import android.animation.Animator
+import android.graphics.Insets
 import android.graphics.Rect
 import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
 import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
@@ -33,7 +35,9 @@
 import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
 import android.view.DisplayInfo
 import android.view.LayoutInflater
+import android.view.Surface
 import android.view.View
+import android.view.ViewPropertyAnimator
 import android.view.WindowInsets
 import android.view.WindowManager
 import android.view.WindowMetrics
@@ -41,6 +45,7 @@
 import com.airbnb.lottie.LottieAnimationView
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Before
@@ -53,6 +58,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.anyLong
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
@@ -81,6 +88,8 @@
     @Mock
     lateinit var displayManager: DisplayManager
     @Mock
+    lateinit var overviewProxyService: OverviewProxyService
+    @Mock
     lateinit var handler: Handler
     @Captor
     lateinit var overlayCaptor: ArgumentCaptor<View>
@@ -91,45 +100,62 @@
 
     @Before
     fun setup() {
+        context.addMockSystemService(DisplayManager::class.java, displayManager)
+        context.addMockSystemService(WindowManager::class.java, windowManager)
+
         `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
         `when`(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
             .thenReturn(mock(LottieAnimationView::class.java))
+        with(mock(ViewPropertyAnimator::class.java)) {
+            `when`(sidefpsView.animate()).thenReturn(this)
+            `when`(alpha(anyFloat())).thenReturn(this)
+            `when`(setStartDelay(anyLong())).thenReturn(this)
+            `when`(setDuration(anyLong())).thenReturn(this)
+            `when`(setListener(any())).thenAnswer {
+                (it.arguments[0] as Animator.AnimatorListener)
+                    .onAnimationEnd(mock(Animator::class.java))
+                this
+            }
+        }
         `when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
-                listOf(
-                        FingerprintSensorPropertiesInternal(
-                                SENSOR_ID,
-                                SensorProperties.STRENGTH_STRONG,
-                                5 /* maxEnrollmentsPerUser */,
-                                listOf() /* componentInfo */,
-                                FingerprintSensorProperties.TYPE_POWER_BUTTON,
-                                true /* resetLockoutRequiresHardwareAuthToken */
-                        )
+            listOf(
+                FingerprintSensorPropertiesInternal(
+                    SENSOR_ID,
+                    SensorProperties.STRENGTH_STRONG,
+                    5 /* maxEnrollmentsPerUser */,
+                    listOf() /* componentInfo */,
+                    FingerprintSensorProperties.TYPE_POWER_BUTTON,
+                    true /* resetLockoutRequiresHardwareAuthToken */
                 )
-        )
-        `when`(windowManager.defaultDisplay).thenReturn(
-                Display(
-                        DisplayManagerGlobal.getInstance(),
-                        DISPLAY_ID,
-                        DisplayInfo(),
-                        DEFAULT_DISPLAY_ADJUSTMENTS
-                )
+            )
         )
         `when`(windowManager.maximumWindowMetrics).thenReturn(
             WindowMetrics(Rect(0, 0, 800, 800), WindowInsets.CONSUMED)
         )
+    }
+
+    private fun testWithDisplay(initInfo: DisplayInfo.() -> Unit = {}, block: () -> Unit) {
+        val displayInfo = DisplayInfo()
+        displayInfo.initInfo()
+        val dmGlobal = mock(DisplayManagerGlobal::class.java)
+        val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
+        `when`(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
+        `when`(windowManager.defaultDisplay).thenReturn(display)
 
         sideFpsController = SidefpsController(
-                mContext, layoutInflater, fingerprintManager, windowManager, executor,
-                displayManager, handler
+            context.createDisplayContext(display), layoutInflater, fingerprintManager,
+            windowManager, overviewProxyService, displayManager, executor, handler
         )
 
         overlayController = ArgumentCaptor.forClass(ISidefpsController::class.java).apply {
             verify(fingerprintManager).setSidefpsController(capture())
         }.value
+
+        block()
     }
 
     @Test
-    fun testSubscribesToOrientationChangesWhenShowingOverlay() {
+    fun testSubscribesToOrientationChangesWhenShowingOverlay() = testWithDisplay {
         overlayController.show(SENSOR_ID, REASON_UNKNOWN)
         executor.runAllReady()
 
@@ -141,7 +167,7 @@
     }
 
     @Test
-    fun testShowsAndHides() {
+    fun testShowsAndHides() = testWithDisplay {
         overlayController.show(SENSOR_ID, REASON_UNKNOWN)
         executor.runAllReady()
 
@@ -156,7 +182,7 @@
     }
 
     @Test
-    fun testShowsOnce() {
+    fun testShowsOnce() = testWithDisplay {
         repeat(5) {
             overlayController.show(SENSOR_ID, REASON_UNKNOWN)
             executor.runAllReady()
@@ -167,7 +193,7 @@
     }
 
     @Test
-    fun testHidesOnce() {
+    fun testHidesOnce() = testWithDisplay {
         overlayController.show(SENSOR_ID, REASON_UNKNOWN)
         executor.runAllReady()
 
@@ -181,10 +207,64 @@
     }
 
     @Test
-    fun testIgnoredForKeyguard() {
-        overlayController.show(SENSOR_ID, REASON_AUTH_KEYGUARD)
+    fun testIgnoredForKeyguard() = testWithDisplay {
+        testIgnoredFor(REASON_AUTH_KEYGUARD)
+    }
+
+    private fun testIgnoredFor(reason: Int) {
+        overlayController.show(SENSOR_ID, reason)
+
         executor.runAllReady()
 
         verify(windowManager, never()).addView(any(), any())
     }
+
+    @Test
+    fun showsWithTaskbar() = testWithDisplay({ rotation = Surface.ROTATION_0 }) {
+        hidesWithTaskbar(visible = true)
+    }
+
+    @Test
+    fun showsWithTaskbar90() = testWithDisplay({ rotation = Surface.ROTATION_90 }) {
+        hidesWithTaskbar(visible = true)
+    }
+
+    @Test
+    fun showsWithTaskbar180() = testWithDisplay({ rotation = Surface.ROTATION_180 }) {
+        hidesWithTaskbar(visible = true)
+    }
+
+    @Test
+    fun showsWithTaskbarCollapsedDown() = testWithDisplay({ rotation = Surface.ROTATION_270 }) {
+        `when`(windowManager.currentWindowMetrics).thenReturn(
+            WindowMetrics(Rect(0, 0, 800, 800), insetsForSmallNavbar())
+        )
+        hidesWithTaskbar(visible = true)
+    }
+
+    @Test
+    fun hidesWithTaskbarDown() = testWithDisplay({ rotation = Surface.ROTATION_270 }) {
+        `when`(windowManager.currentWindowMetrics).thenReturn(
+            WindowMetrics(Rect(0, 0, 800, 800), insetsForLargeNavbar())
+        )
+        hidesWithTaskbar(visible = false)
+    }
+
+    private fun hidesWithTaskbar(visible: Boolean) {
+        overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+        executor.runAllReady()
+
+        sideFpsController.overviewProxyListener.onTaskbarStatusUpdated(true, false)
+        executor.runAllReady()
+
+        verify(windowManager).addView(any(), any())
+        verify(windowManager, never()).removeView(any())
+        verify(sidefpsView).visibility = if (visible) View.VISIBLE else View.GONE
+    }
 }
+
+private fun insetsForSmallNavbar() = insetsWithBottom(60)
+private fun insetsForLargeNavbar() = insetsWithBottom(100)
+private fun insetsWithBottom(bottom: Int) = WindowInsets.Builder()
+    .setInsets(WindowInsets.Type.navigationBars(), Insets.of(0, 0, 0, bottom))
+    .build()
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index 25aa93a..bd4efdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -33,6 +33,7 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 4bf8821..1adba6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -54,6 +54,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.tuner.TunerService;
 
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 9202cec..e2bd62a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -141,6 +141,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
@@ -224,6 +225,7 @@
     @Mock private KeyguardBypassController mKeyguardBypassController;
     @Mock private DynamicPrivacyController mDynamicPrivacyController;
     @Mock private AutoHideController mAutoHideController;
+    @Mock private StatusBarWindowController mStatusBarWindowController;
     @Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
     @Mock private UserSwitcherController mUserSwitcherController;
     @Mock private NetworkController mNetworkController;
@@ -366,6 +368,7 @@
                 mNotificationsController,
                 mLightBarController,
                 mAutoHideController,
+                mStatusBarWindowController,
                 mKeyguardUpdateMonitor,
                 mPulseExpansionHandler,
                 mNotificationWakeUpCoordinator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 3d2ff47..b385b7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -42,7 +42,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
diff --git a/rs/jni/Android.bp b/rs/jni/Android.bp
new file mode 100644
index 0000000..9a6fa8e
--- /dev/null
+++ b/rs/jni/Android.bp
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2021 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_library_shared {
+    name: "librs_jni",
+
+    srcs: ["android_renderscript_RenderScript.cpp"],
+
+    shared_libs: [
+        "libandroid",
+        "libandroid_runtime",
+        "libandroidfw",
+        "libRS",
+        "libcutils",
+        "libhwui",
+        "liblog",
+        "libutils",
+        "libui",
+        "libgui",
+        "libjnigraphics",
+    ],
+
+    header_libs: [
+        "jni_headers",
+        "libbase_headers",
+    ],
+
+    include_dirs: ["frameworks/rs"],
+
+    cflags: [
+        "-Wno-unused-parameter",
+        "-Wunused",
+        "-Wunreachable-code",
+        "-Wno-deprecated-declarations",
+    ],
+}
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
deleted file mode 100644
index 0caba42..0000000
--- a/rs/jni/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    android_renderscript_RenderScript.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libandroid \
-    libandroid_runtime \
-    libandroidfw \
-    libRS \
-    libcutils \
-    libhwui \
-    liblog \
-    libutils \
-    libui \
-    libgui \
-    libjnigraphics
-
-LOCAL_HEADER_LIBRARIES := \
-    jni_headers \
-    libbase_headers
-
-LOCAL_C_INCLUDES += \
-    frameworks/rs
-
-LOCAL_CFLAGS += -Wno-unused-parameter
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -Wno-deprecated-declarations
-
-LOCAL_MODULE:= librs_jni
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
-LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../NOTICE
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := libRS
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index acdbcb58..67bb726 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -256,6 +256,18 @@
         void setGestureDetectionPassthroughRegion(int displayId, Region region);
 
         void setTouchExplorationPassthroughRegion(int displayId, Region region);
+
+        void setServiceDetectsGesturesEnabled(int displayId, boolean mode);
+
+        void requestTouchExploration(int displayId);
+
+        void requestDragging(int displayId, int pointerId);
+
+        void requestDelegating(int displayId);
+
+        void onDoubleTap(int displayId);
+
+        void onDoubleTapAndHold(int displayId);
     }
 
     public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
@@ -1650,7 +1662,7 @@
         }
     }
 
-    private IAccessibilityServiceClient getServiceInterfaceSafely() {
+    protected IAccessibilityServiceClient getServiceInterfaceSafely() {
         synchronized (mLock) {
             return mServiceInterface;
         }
@@ -2048,4 +2060,28 @@
     protected void logTraceWM(String methodName, String params) {
         mTrace.logTrace(TRACE_WM + "." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
     }
+
+    public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
+        mSystemSupport.setServiceDetectsGesturesEnabled(displayId, mode);
+    }
+
+    public void requestTouchExploration(int displayId) {
+        mSystemSupport.requestTouchExploration(displayId);
+    }
+
+    public void requestDragging(int displayId, int pointerId) {
+        mSystemSupport.requestDragging(displayId, pointerId);
+    }
+
+    public void requestDelegating(int displayId) {
+        mSystemSupport.requestDelegating(displayId);
+    }
+
+    public void onDoubleTap(int displayId) {
+        mSystemSupport.onDoubleTap(displayId);
+    }
+
+    public void onDoubleTapAndHold(int displayId) {
+        mSystemSupport.onDoubleTapAndHold(displayId);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 10cfd04..75724bf 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -893,6 +893,42 @@
         }
     }
 
+    public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
+        if (mTouchExplorer.contains(displayId)) {
+            mTouchExplorer.get(displayId).setServiceDetectsGestures(mode);
+        }
+    }
+
+    public void requestTouchExploration(int displayId) {
+        if (mTouchExplorer.contains(displayId)) {
+            mTouchExplorer.get(displayId).requestTouchExploration();
+        }
+    }
+
+    public void requestDragging(int displayId, int pointerId) {
+        if (mTouchExplorer.contains(displayId)) {
+            mTouchExplorer.get(displayId).requestDragging(pointerId);
+        }
+    }
+
+    public void requestDelegating(int displayId) {
+        if (mTouchExplorer.contains(displayId)) {
+            mTouchExplorer.get(displayId).requestDelegating();
+        }
+    }
+
+    public void onDoubleTap(int displayId) {
+        if (mTouchExplorer.contains(displayId)) {
+            mTouchExplorer.get(displayId).onDoubleTap();
+        }
+    }
+
+    public void onDoubleTapAndHold(int displayId) {
+        if (mTouchExplorer.contains(displayId)) {
+            mTouchExplorer.get(displayId).onDoubleTapAndHold();
+        }
+    }
+
     /**
      * Dumps all {@link AccessibilityInputFilter}s here.
      */
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e6f91a25f..214769b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -44,6 +44,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityShortcutInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.TouchInteractionController;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -101,6 +102,7 @@
 import android.view.IWindow;
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
+import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
@@ -1198,7 +1200,7 @@
     }
 
     /**
-     * Called when a gesture is detected on a display.
+     * Called when a gesture is detected on a display by the framework.
      *
      * @param gestureEvent the detail of the gesture.
      * @return true if the event is handled.
@@ -1213,6 +1215,29 @@
         }
     }
 
+    /** Send a motion event to the service to allow it to perform gesture detection. */
+    public boolean sendMotionEventToListeningServices(MotionEvent event) {
+        synchronized (mLock) {
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "Sending event to service: " + event);
+            }
+            return notifyMotionEvent(event);
+        }
+    }
+
+    /**
+     * Notifies services that the touch state on a given display has changed.
+     */
+    public boolean onTouchStateChanged(int displayId, int state) {
+        synchronized (mLock) {
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "Notifying touch state:"
+                        + TouchInteractionController.stateToString(state));
+            }
+            return notifyTouchState(displayId, state);
+        }
+    }
+
     /**
      * Called when the system action list is changed.
      */
@@ -1527,6 +1552,30 @@
         return false;
     }
 
+    private boolean notifyMotionEvent(MotionEvent event) {
+        AccessibilityUserState state = getCurrentUserStateLocked();
+        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+            AccessibilityServiceConnection service = state.mBoundServices.get(i);
+            if (service.mRequestTouchExplorationMode) {
+                service.notifyMotionEvent(event);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean notifyTouchState(int displayId, int touchState) {
+        AccessibilityUserState state = getCurrentUserStateLocked();
+        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+            AccessibilityServiceConnection service = state.mBoundServices.get(i);
+            if (service.mRequestTouchExplorationMode) {
+                service.notifyTouchState(displayId, touchState);
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void notifyClearAccessibilityCacheLocked() {
         AccessibilityUserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
@@ -2083,7 +2132,6 @@
             if (userState.isSendMotionEventsEnabled()) {
                 flags |= AccessibilityInputFilter.FLAG_SEND_MOTION_EVENTS;
             }
-
             if (userState.isAutoclickEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
             }
@@ -3960,6 +4008,93 @@
         }
     }
 
+    @Override
+    public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
+        mMainHandler.sendMessage(
+                obtainMessage(AccessibilityManagerService::setServiceDetectsGesturesInternal, this,
+                        displayId, mode));
+    }
+
+    private void setServiceDetectsGesturesInternal(int displayId, boolean mode) {
+        synchronized (mLock) {
+            if (mHasInputFilter && mInputFilter != null) {
+                mInputFilter.setServiceDetectsGesturesEnabled(displayId, mode);
+            }
+        }
+    }
+
+    @Override
+    public void requestTouchExploration(int displayId) {
+        mMainHandler.sendMessage(obtainMessage(
+                AccessibilityManagerService::requestTouchExplorationInternal, this, displayId));
+    }
+
+    private void requestTouchExplorationInternal(int displayId) {
+        synchronized (mLock) {
+            if (mHasInputFilter && mInputFilter != null) {
+                mInputFilter.requestTouchExploration(displayId);
+            }
+        }
+    }
+
+    @Override
+    public void requestDragging(int displayId, int pointerId) {
+        mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::requestDraggingInternal,
+                this, displayId, pointerId));
+    }
+
+    private void requestDraggingInternal(int displayId, int pointerId) {
+        synchronized (mLock) {
+            if (mHasInputFilter && mInputFilter != null) {
+                mInputFilter.requestDragging(displayId, pointerId);
+            }
+        }
+    }
+
+    @Override
+    public void requestDelegating(int displayId) {
+        mMainHandler.sendMessage(
+                obtainMessage(
+                        AccessibilityManagerService::requestDelegatingInternal, this, displayId));
+    }
+
+    private void requestDelegatingInternal(int displayId) {
+        synchronized (mLock) {
+            if (mHasInputFilter && mInputFilter != null) {
+                mInputFilter.requestDelegating(displayId);
+            }
+        }
+    }
+
+    @Override
+    public void onDoubleTap(int displayId) {
+        mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::onDoubleTapInternal,
+                this, displayId));
+    }
+
+    private void onDoubleTapInternal(int displayId) {
+        synchronized (mLock) {
+            if (mHasInputFilter && mInputFilter != null) {
+                mInputFilter.onDoubleTap(displayId);
+            }
+        }
+    }
+
+    @Override
+    public void onDoubleTapAndHold(int displayId) {
+        mMainHandler
+                .sendMessage(obtainMessage(AccessibilityManagerService::onDoubleTapAndHoldInternal,
+                        this, displayId));
+    }
+
+    private void onDoubleTapAndHoldInternal(int displayId) {
+        synchronized (mLock) {
+            if (mHasInputFilter && mInputFilter != null) {
+                mInputFilter.onDoubleTapAndHold(displayId);
+            }
+        }
+    }
+
     private void updateFocusAppearanceDataLocked(AccessibilityUserState userState) {
         if (userState.mUserId != mCurrentUserId) {
             return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 4bf48a2..e9f5870 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -22,6 +22,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityTrace;
 import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.TouchInteractionController;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -30,12 +31,15 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 import android.view.Display;
+import android.view.MotionEvent;
+
 
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -454,4 +458,48 @@
             mSystemSupport.onClientChangeLocked(false);
         }
     }
+
+    public void notifyMotionEvent(MotionEvent event) {
+        final Message msg = obtainMessage(
+                AccessibilityServiceConnection::notifyMotionEventInternal,
+                AccessibilityServiceConnection.this, event);
+        mMainHandler.sendMessage(msg);
+    }
+
+    public void notifyTouchState(int displayId, int state) {
+        final Message msg = obtainMessage(
+                AccessibilityServiceConnection::notifyTouchStateInternal,
+                AccessibilityServiceConnection.this, displayId, state);
+        mMainHandler.sendMessage(msg);
+    }
+
+    private void notifyMotionEventInternal(MotionEvent event) {
+        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+        if (listener != null) {
+            try {
+                if (mTrace.isA11yTracingEnabled()) {
+                    logTraceSvcClient(".onMotionEvent ",
+                            event.toString());
+                }
+                listener.onMotionEvent(event);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error sending motion event to" + mService, re);
+            }
+        }
+    }
+
+    private void notifyTouchStateInternal(int displayId, int state) {
+        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+        if (listener != null) {
+            try {
+                if (mTrace.isA11yTracingEnabled()) {
+                    logTraceSvcClient(".onTouchStateChanged ",
+                            TouchInteractionController.stateToString(state));
+                }
+                listener.onTouchStateChanged(displayId, state);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error sending motion event to" + mService, re);
+            }
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index df4a52e..b6223c7 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -382,18 +382,21 @@
     }
 
     public boolean longPressWithTouchEvents(MotionEvent event, int policyFlags) {
-        final int pointerIndex = event.getActionIndex();
-        final int pointerId = event.getPointerId(pointerIndex);
         Point clickLocation = mTempPoint;
         final int result = computeClickLocation(clickLocation);
         if (result == CLICK_LOCATION_NONE) {
             return false;
         }
-        mLongPressingPointerId = pointerId;
-        mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
-        mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
-        sendDownForAllNotInjectedPointers(event, policyFlags);
-        return true;
+        if (event != null) {
+            final int pointerIndex = event.getActionIndex();
+            final int pointerId = event.getPointerId(pointerIndex);
+            mLongPressingPointerId = pointerId;
+            mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
+            mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
+            sendDownForAllNotInjectedPointers(event, policyFlags);
+            return true;
+        }
+        return false;
     }
 
     void clear() {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 74f0bcb..946d22e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -46,6 +46,7 @@
 import android.os.Handler;
 import android.util.DisplayMetrics;
 import android.util.Slog;
+import android.view.Display;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -152,6 +153,7 @@
 
     private Region mGestureDetectionPassthroughRegion;
     private Region mTouchExplorationPassthroughRegion;
+    private int mDisplayId = Display.INVALID_DISPLAY;
 
 /**
      * Creates a new instance.
@@ -182,8 +184,9 @@
     TouchExplorer(Context context, AccessibilityManagerService service, GestureManifold detector,
             @NonNull Handler mainHandler) {
         mContext = context;
+        mDisplayId = context.getDisplayId();
         mAms = service;
-        mState = new TouchState();
+        mState = new TouchState(mDisplayId, mAms);
         mReceivedPointerTracker = mState.getReceivedPointerTracker();
         mDispatcher = new EventDispatcher(context, mAms, super.getNext(), mState);
         mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
@@ -267,6 +270,7 @@
         }
         try {
             checkForMalformedEvent(event);
+            checkForMalformedEvent(rawEvent);
         } catch (IllegalArgumentException e) {
             Slog.e(LOG_TAG, "Ignoring malformed event: " + event.toString(), e);
             return;
@@ -277,7 +281,7 @@
             Slog.d(LOG_TAG, mState.toString());
         }
 
-        mState.onReceivedMotionEvent(rawEvent);
+        mState.onReceivedMotionEvent(event, rawEvent, policyFlags);
         if (shouldPerformGestureDetection(event)) {
             if (mGestureDetector.onMotionEvent(event, rawEvent, policyFlags)) {
                 // Event was handled by the gesture detector.
@@ -307,9 +311,12 @@
             // It will be delivered on gesture completion or cancelation.
             // Note that the delay for sending GESTURE_DETECTION_END remains in place.
             mSendTouchInteractionEndDelayed.cancel();
+            if (mState.isServiceDetectingGestures()) {
+                mAms.sendMotionEventToListeningServices(rawEvent);
+            }
         } else {
             Slog.e(LOG_TAG, "Illegal state: " + mState);
-                clear(event, policyFlags);
+            clear(event, policyFlags);
         }
     }
 
@@ -367,7 +374,7 @@
                 AccessibilityGestureEvent gestureEvent =
                         new AccessibilityGestureEvent(
                                 AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD,
-                                event.getDisplayId(),
+                                mDisplayId,
                                 mGestureDetector.getMotionEvents());
                 dispatchGesture(gestureEvent);
             }
@@ -389,7 +396,7 @@
             AccessibilityGestureEvent gestureEvent =
                     new AccessibilityGestureEvent(
                             AccessibilityService.GESTURE_DOUBLE_TAP,
-                            event.getDisplayId(),
+                            mDisplayId,
                             mGestureDetector.getMotionEvents());
             dispatchGesture(gestureEvent);
         }
@@ -404,13 +411,37 @@
         if (!mAms.performActionOnAccessibilityFocusedItem(
                 AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)) {
             Slog.e(LOG_TAG, "ACTION_CLICK failed. Dispatching motion events to simulate click.");
-
-            mDispatcher.clickWithTouchEvents(event, rawEvent, policyFlags);
+            if (event != null && rawEvent != null) {
+                mDispatcher.clickWithTouchEvents(event, rawEvent, policyFlags);
+            }
             return true;
         }
         return true;
     }
 
+    /**
+     * Executes a double-tap. The framework will first attempt to execute the appropriate
+     * accessibility action and if that fails, the framework will deliver touch events to the last
+     * touch-explored location.
+     */
+    public void onDoubleTap() {
+        MotionEvent event = mState.getLastReceivedEvent();
+        MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+        int policyFlags = mState.getLastReceivedPolicyFlags();
+        onDoubleTap(event, rawEvent, policyFlags);
+    }
+
+    /**
+     * Executes a double-tap and hold gesture using touch events. The user can continue to move
+     * their finger around the screen to execute a drag.
+     */
+    public void onDoubleTapAndHold() {
+        MotionEvent event = mState.getLastReceivedEvent();
+        MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+        int policyFlags = mState.getLastReceivedPolicyFlags();
+        onDoubleTapAndHold(event, rawEvent, policyFlags);
+    }
+
     @Override
     public boolean onGestureStarted() {
         if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
@@ -473,7 +504,7 @@
             AccessibilityGestureEvent gestureEvent =
                     new AccessibilityGestureEvent(
                             AccessibilityService.GESTURE_UNKNOWN,
-                            event.getDisplayId(),
+                            mDisplayId,
                             mGestureDetector.getMotionEvents());
             dispatchGesture(gestureEvent);
         }
@@ -502,7 +533,6 @@
      */
     private void handleActionDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         mAms.onTouchInteractionStart();
-
         // If we still have not notified the user for the last
         // touch, we figure out what to do. If were waiting
         // we resent the delayed callback and wait again.
@@ -512,7 +542,6 @@
         if (mState.isTouchExploring()) {
             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
         }
-
         if (mState.isClear()) {
             if (!mSendHoverEnterAndMoveDelayed.isPending()) {
                 // Queue a delayed transition to STATE_TOUCH_EXPLORING.
@@ -521,7 +550,14 @@
                 // The idea is to avoid getting stuck in STATE_TOUCH_INTERACTING
                 final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
                 final int pointerIdBits = (1 << pointerId);
-                mSendHoverEnterAndMoveDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
+                if (mState.isServiceDetectingGestures()) {
+                    // This transition will be triggered manually by the service.
+                    mSendHoverEnterAndMoveDelayed.setPointerIdBits(pointerIdBits);
+                    mSendHoverEnterAndMoveDelayed.setPolicyFlags(policyFlags);
+                    mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
+                } else {
+                    mSendHoverEnterAndMoveDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
+                }
             } else {
                 // Cache the event until we discern exploration from gesturing.
                 mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
@@ -548,6 +584,9 @@
             // Avoid duplicated TYPE_TOUCH_INTERACTION_START event when 2nd tap of double tap.
             mSendTouchInteractionEndDelayed.cancel();
         }
+        if (mState.isServiceDetectingGestures()) {
+            mAms.sendMotionEventToListeningServices(rawEvent);
+        }
     }
 
     /**
@@ -571,6 +610,11 @@
             case ACTION_MOVE:
                 handleActionMoveStateTouchInteracting(event, rawEvent, policyFlags);
                 break;
+            case ACTION_POINTER_UP:
+                if (mState.isServiceDetectingGestures()) {
+                    mAms.sendMotionEventToListeningServices(rawEvent);
+                }
+                break;
             case ACTION_UP:
                 handleActionUp(event, rawEvent, policyFlags);
                 break;
@@ -612,15 +656,16 @@
         // Another finger down means that if we have not started to deliver
         // hover events, we will not have to. The code for ACTION_MOVE will
         // decide what we will actually do next.
-
         if (mSendHoverEnterAndMoveDelayed.isPending()) {
             mSendHoverEnterAndMoveDelayed.cancel();
             mSendHoverExitDelayed.cancel();
         } else {
-            // We have already delivered at least one hover event, so send hover exit to keep the
-            // stream consistent.
+            // We have already delivered at least one hover event, so send hover exit to keep
             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
         }
+        if (mState.isServiceDetectingGestures()) {
+            mAms.sendMotionEventToListeningServices(rawEvent);
+        }
     }
 
     /**
@@ -632,12 +677,19 @@
         final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
         final int pointerIndex = event.findPointerIndex(pointerId);
         int pointerIdBits = (1 << pointerId);
+        if (mState.isServiceDetectingGestures()) {
+            mAms.sendMotionEventToListeningServices(rawEvent);
+            mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
+            return;
+        }
         switch (event.getPointerCount()) {
             case 1:
                 // We have not started sending events since we try to
                 // figure out what the user is doing.
                 if (mSendHoverEnterAndMoveDelayed.isPending()) {
                     // Cache the event until we discern exploration from gesturing.
+                    // When the service is detecting gestures we rely on it to fire touch
+                    // exploration.
                     mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
                 }
                 break;
@@ -684,7 +736,7 @@
                         AccessibilityGestureEvent gestureEvent =
                                 new AccessibilityGestureEvent(
                                         AccessibilityService.GESTURE_PASSTHROUGH,
-                                        event.getDisplayId(),
+                                        mDisplayId,
                                         mGestureDetector.getMotionEvents());
                         dispatchGesture(gestureEvent);
                     }
@@ -708,7 +760,7 @@
                         AccessibilityGestureEvent gestureEvent =
                                 new AccessibilityGestureEvent(
                                         AccessibilityService.GESTURE_PASSTHROUGH,
-                                        event.getDisplayId(),
+                                        mDisplayId,
                                         mGestureDetector.getMotionEvents());
                         dispatchGesture(gestureEvent);
                     }
@@ -730,7 +782,7 @@
                                     AccessibilityGestureEvent gestureEvent =
                                             new AccessibilityGestureEvent(
                                                     AccessibilityService.GESTURE_PASSTHROUGH,
-                                                    event.getDisplayId(),
+                                                    mDisplayId,
                                                     mGestureDetector.getMotionEvents());
                                     dispatchGesture(gestureEvent);
                                 }
@@ -751,7 +803,7 @@
                         AccessibilityGestureEvent gestureEvent =
                                 new AccessibilityGestureEvent(
                                         AccessibilityService.GESTURE_PASSTHROUGH,
-                                        event.getDisplayId(),
+                                        mDisplayId,
                                         mGestureDetector.getMotionEvents());
                         dispatchGesture(gestureEvent);
                     }
@@ -767,7 +819,10 @@
      * Handles ACTION_UP while in the touch interacting state. This event represents all fingers
      * being lifted from the screen.
      */
-    private void handleActionUp(MotionEvent event,  MotionEvent rawEvent, int policyFlags) {
+    private void handleActionUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mState.isServiceDetectingGestures() && mState.isTouchInteracting()) {
+            mAms.sendMotionEventToListeningServices(rawEvent);
+        }
         mAms.onTouchInteractionEnd();
         final int pointerId = event.getPointerId(event.getActionIndex());
         final int pointerIdBits = (1 << pointerId);
@@ -883,6 +938,10 @@
                 clear(event, policyFlags);
                 return;
             case ACTION_POINTER_DOWN:
+                if (mState.isServiceDetectingGestures()) {
+                    mAms.sendMotionEventToListeningServices(rawEvent);
+                    return;
+                }
                 // We are in dragging state so we have two pointers and another one
                 // goes down => delegate the three pointers to the view hierarchy
                 mState.startDelegating();
@@ -896,6 +955,15 @@
                 if (mDraggingPointerId == INVALID_POINTER_ID) {
                     break;
                 }
+                if (mState.isServiceDetectingGestures()) {
+                    // Allow the service to judge whether this is dragging or delegation
+                    mAms.sendMotionEventToListeningServices(rawEvent);
+                    computeDraggingPointerIdIfNeeded(event);
+                    mDispatcher.sendMotionEvent(
+                            event, ACTION_MOVE, rawEvent, pointerIdBits, policyFlags);
+                    return;
+
+                }
                 switch (event.getPointerCount()) {
                     case 1:
                         // do nothing
@@ -921,6 +989,10 @@
                         }
                         break;
                     default:
+                        if (mState.isServiceDetectingGestures()) {
+                            mAms.sendMotionEventToListeningServices(rawEvent);
+                            return;
+                        }
                         mState.startDelegating();
                         mDraggingPointerId = INVALID_POINTER_ID;
                         event = MotionEvent.obtainNoHistory(event);
@@ -1210,9 +1282,7 @@
         mTouchExplorationPassthroughRegion = region;
     }
 
-    /**
-     * Whether to send the motion events that make up each gesture to the accessibility service.
-     */
+    /** Whether to send the motion events that make up each gesture to the accessibility service. */
     public void setSendMotionEventsEnabled(boolean mode) {
         mGestureDetector.setSendMotionEventsEnabled(mode);
     }
@@ -1221,7 +1291,15 @@
         return mGestureDetector.isSendMotionEventsEnabled();
     }
 
+    /** Sets whether or not motion events should be passed to the service to detect gestures. */
+    public void setServiceDetectsGestures(boolean mode) {
+        mState.setServiceDetectsGestures(mode);
+    }
+
     private boolean shouldPerformGestureDetection(MotionEvent event) {
+        if (mState.isServiceDetectingGestures()) {
+            return false;
+        }
         if (mState.isDelegating() || mState.isDragging()) {
             return false;
         }
@@ -1237,8 +1315,105 @@
     }
 
     /**
-     * Class for delayed exiting from gesture detecting mode.
+     * This method allows the service to request that TouchExplorer enter the touch exploration
+     * state.
      */
+    public void requestTouchExploration() {
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "Starting touch explorer from service.");
+        }
+        if (mState.isServiceDetectingGestures() && mState.isTouchInteracting()) {
+            // Cancel without deleting events.
+            mHandler.removeCallbacks(mSendHoverEnterAndMoveDelayed);
+            mSendHoverEnterAndMoveDelayed.run();
+            mSendHoverEnterAndMoveDelayed.clear();
+            final MotionEvent prototype = mState.getLastReceivedEvent();
+            final MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+            final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
+            final int pointerIdBits = (1 << pointerId);
+            final int policyFlags = mState.getLastReceivedPolicyFlags();
+            mSendHoverExitDelayed.post(prototype, rawEvent, pointerIdBits, policyFlags);
+        }
+    }
+
+    /** This method allows the service to request that TouchExplorer enter the dragging state. */
+    public void requestDragging(int pointerId) {
+        if (mState.isServiceDetectingGestures()) {
+            if (pointerId < 0 || pointerId > TouchState.MAX_POINTER_COUNT
+                    || !mReceivedPointerTracker.isReceivedPointerDown(pointerId)) {
+                Slog.e(LOG_TAG, "Trying to drag with invalid pointer: " + pointerId);
+                return;
+            }
+            if (mState.isTouchExploring()) {
+                if (mSendHoverExitDelayed.isPending()) {
+                    mSendHoverExitDelayed.forceSendAndRemove();
+                }
+                if (mSendTouchExplorationEndDelayed.isPending()) {
+                    mSendTouchExplorationEndDelayed.forceSendAndRemove();
+                }
+            }
+            if (!mState.isTouchInteracting()) {
+                // It makes no sense to drag.
+                Slog.e(LOG_TAG, "Error: Trying to drag from "
+                        + mState.getStateSymbolicName(mState.getState()));
+                return;
+            }
+            mDraggingPointerId = pointerId;
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "Drag requested on pointer " + mDraggingPointerId);
+            }
+            MotionEvent event = mState.getLastReceivedEvent();
+            MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+            if (event == null || rawEvent == null) {
+                Slog.e(LOG_TAG, "Unable to start dragging: unable to get last event.");
+                return;
+            }
+            int policyFlags = mState.getLastReceivedPolicyFlags();
+            int pointerIdBits = 1 << mDraggingPointerId;
+            event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
+            MotionEvent downEvent = computeDownEventForDrag(event);
+            mState.startDragging();
+            if (downEvent != null) {
+                mDispatcher.sendMotionEvent(
+                        downEvent, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+                mDispatcher.sendMotionEvent(event, ACTION_MOVE, rawEvent, pointerIdBits,
+                        policyFlags);
+            } else {
+                mDispatcher.sendMotionEvent(event, ACTION_DOWN, rawEvent, pointerIdBits,
+                        policyFlags);
+            }
+        }
+    }
+
+    /** This method allows the service to request that TouchExplorer enter the delegating state. */
+    public void requestDelegating() {
+        if (mState.isServiceDetectingGestures()) {
+            if (mState.isTouchExploring()) {
+                if (mSendHoverExitDelayed.isPending()) {
+                    mSendHoverExitDelayed.forceSendAndRemove();
+                }
+                if (mSendTouchExplorationEndDelayed.isPending()) {
+                    mSendTouchExplorationEndDelayed.forceSendAndRemove();
+                }
+            }
+            if (!mState.isTouchInteracting()) {
+                // It makes no sense to delegate.
+                Slog.e(LOG_TAG, "Error: Trying to delegate from "
+                        + mState.getStateSymbolicName(mState.getState()));
+                return;
+            }
+            mState.startDelegating();
+            MotionEvent prototype = mState.getLastReceivedEvent();
+            if (prototype == null) {
+                Slog.d(LOG_TAG, "Unable to start delegating: unable to get last received event.");
+                return;
+            }
+            int policyFlags = mState.getLastReceivedPolicyFlags();
+            mDispatcher.sendDownForAllNotInjectedPointers(prototype, policyFlags);
+        }
+    }
+
+    /** Class for delayed exiting from gesture detecting mode. */
     private final class ExitGestureDetectionModeDelayed implements Runnable {
 
         public void post() {
@@ -1283,9 +1458,7 @@
         }
     }
 
-    /**
-     * Class for delayed sending of hover enter and move events.
-     */
+    /** Class for delayed sending of hover enter and move events. */
     class SendHoverEnterAndMoveDelayed implements Runnable {
         private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed";
 
@@ -1362,31 +1535,46 @@
             }
             if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
                 // Deliver a down event.
-                mDispatcher.sendMotionEvent(mEvents.get(0), ACTION_HOVER_ENTER,
-                        mRawEvents.get(0), mPointerIdBits, mPolicyFlags);
+                mDispatcher.sendMotionEvent(
+                        mEvents.get(0),
+                        ACTION_HOVER_ENTER,
+                        mRawEvents.get(0),
+                        mPointerIdBits,
+                        mPolicyFlags);
                 if (DEBUG) {
-                    Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
+                    Slog.d(
+                            LOG_TAG_SEND_HOVER_DELAYED,
                             "Injecting motion event: ACTION_HOVER_ENTER");
                 }
 
                 // Deliver move events.
                 final int eventCount = mEvents.size();
                 for (int i = 1; i < eventCount; i++) {
-                    mDispatcher.sendMotionEvent(mEvents.get(i), ACTION_HOVER_MOVE,
-                            mRawEvents.get(i), mPointerIdBits, mPolicyFlags);
+                    mDispatcher.sendMotionEvent(
+                            mEvents.get(i),
+                            ACTION_HOVER_MOVE,
+                            mRawEvents.get(i),
+                            mPointerIdBits,
+                            mPolicyFlags);
                     if (DEBUG) {
-                        Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
+                        Slog.d(
+                                LOG_TAG_SEND_HOVER_DELAYED,
                                 "Injecting motion event: ACTION_HOVER_MOVE");
                     }
                 }
             }
             clear();
         }
-    }
 
-    /**
-     * Class for delayed sending of hover exit events.
-     */
+        public void setPointerIdBits(int pointerIdBits) {
+            mPointerIdBits = pointerIdBits;
+        }
+
+        public void setPolicyFlags(int policyFlags) {
+            mPolicyFlags = policyFlags;
+        }
+    }
+    /** Class for delayed sending of hover exit events. */
     class SendHoverExitDelayed implements Runnable {
         private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed";
 
@@ -1438,21 +1626,18 @@
 
         public void run() {
             if (DEBUG) {
-                Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
-                        + " ACTION_HOVER_EXIT");
+                Slog.d(
+                        LOG_TAG_SEND_HOVER_DELAYED,
+                        "Injecting motion event:" + " ACTION_HOVER_EXIT");
             }
             mDispatcher.sendMotionEvent(
-                    mPrototype,
-                    ACTION_HOVER_EXIT,
-                    mRawEvent,
-                    mPointerIdBits,
-                    mPolicyFlags);
+                    mPrototype, ACTION_HOVER_EXIT, mRawEvent, mPointerIdBits, mPolicyFlags);
             if (!mSendTouchExplorationEndDelayed.isPending()) {
                 mSendTouchExplorationEndDelayed.cancel();
                 mSendTouchExplorationEndDelayed.post();
             }
             if (mSendTouchInteractionEndDelayed.isPending()) {
-                  mSendTouchInteractionEndDelayed.cancel();
+                mSendTouchInteractionEndDelayed.cancel();
                 mSendTouchInteractionEndDelayed.post();
             }
             clear();
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index 6ff0826..eb71885 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -22,9 +22,12 @@
 
 import android.annotation.IntDef;
 import android.util.Slog;
+import android.view.Display;
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityEvent;
 
+import com.android.server.accessibility.AccessibilityManagerService;
+
 /**
  * This class describes the state of the touch explorer as well as the state of received and
  * injected pointers. This data is accessed both for purposes of touch exploration and gesture
@@ -36,7 +39,7 @@
     // This constant captures the current implementation detail that
     // pointer IDs are between 0 and 31 inclusive (subject to change).
     // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
-    static final int MAX_POINTER_COUNT = 32;
+    public static final int MAX_POINTER_COUNT = 32;
     // Constant referring to the ids bits of all pointers.
     public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
 
@@ -50,8 +53,8 @@
     public static final int STATE_TOUCH_EXPLORING = 2;
     // the user is dragging with two fingers.
     public static final int STATE_DRAGGING = 3;
-    // The user is performing some other two finger gesture which we pass through to the view
-    // hierarchy as a one-finger gesture e.g. two-finger scrolling.
+    // The user is performing some other two finger gesture which we pass through to the
+    // input pipeline as a one-finger gesture e.g. two-finger pinch.
     public static final int STATE_DELEGATING = 4;
     // The user is performing something that might be a gesture.
     public static final int STATE_GESTURE_DETECTING = 5;
@@ -75,6 +78,8 @@
     private MotionEvent mLastReceivedEvent;
     // The accompanying raw event without any transformations.
     private MotionEvent mLastReceivedRawEvent;
+    // The policy flags of the last received event.
+    int mLastReceivedPolicyFlags;
     // The id of the last touch explored window.
     private int mLastTouchedWindowId;
     // The last injected hover event.
@@ -85,14 +90,23 @@
     private long mLastInjectedDownEventTime;
     // Keep track of which pointers sent to the system are down.
     private int mInjectedPointersDown;
+    private boolean mServiceDetectsGestures = false;
+    // The requested mode for mServiceDetectsGestures. This will take effect on the next touch
+    // interaction.
+    private boolean mServiceDetectsGesturesRequested = false;
+    private AccessibilityManagerService mAms;
+    private int mDisplayId = Display.INVALID_DISPLAY;
 
-    public TouchState() {
+    public TouchState(int displayId, AccessibilityManagerService ams) {
+        mDisplayId = displayId;
+        mAms = ams;
         mReceivedPointerTracker = new ReceivedPointerTracker();
     }
 
     /** Clears the internal shared state. */
     public void clear() {
         setState(STATE_CLEAR);
+        mServiceDetectsGestures = mServiceDetectsGesturesRequested;
         // Reset the pointer trackers.
         if (mLastReceivedEvent != null) {
             mLastReceivedEvent.recycle();
@@ -107,14 +121,19 @@
      *
      * @param rawEvent The raw touch event.
      */
-    public void onReceivedMotionEvent(MotionEvent rawEvent) {
+    public void onReceivedMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (isClear() && event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            clear();
+        }
         if (mLastReceivedEvent != null) {
             mLastReceivedEvent.recycle();
         }
         if (mLastReceivedRawEvent != null) {
             mLastReceivedRawEvent.recycle();
         }
-        mLastReceivedEvent = MotionEvent.obtain(rawEvent);
+        mLastReceivedEvent = MotionEvent.obtain(event);
+        mLastReceivedRawEvent = MotionEvent.obtain(rawEvent);
+        mLastReceivedPolicyFlags = policyFlags;
         mReceivedPointerTracker.onMotionEvent(rawEvent);
     }
 
@@ -123,7 +142,7 @@
      *
      * @param event The event to process.
      */
-    void onInjectedMotionEvent(MotionEvent event) {
+    public void onInjectedMotionEvent(MotionEvent event) {
         final int action = event.getActionMasked();
         final int pointerId = event.getPointerId(event.getActionIndex());
         final int pointerFlag = (1 << pointerId);
@@ -183,6 +202,7 @@
         }
     }
 
+    /** Updates the state in response to an injected accessibility event. */
     public void onInjectedAccessibilityEvent(int type) {
         // The below state transitions go here because the related events are often sent on a
         // delay.
@@ -195,7 +215,8 @@
                 startTouchInteracting();
                 break;
             case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
-                clear();
+                setState(STATE_CLEAR);
+                // We will clear when we actually handle the next ACTION_DOWN.
                 break;
             case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
                 startTouchExploring();
@@ -228,6 +249,9 @@
             Slog.i(LOG_TAG, getStateSymbolicName(mState) + "->" + getStateSymbolicName(state));
         }
         mState = state;
+        if (mServiceDetectsGestures) {
+            mAms.onTouchStateChanged(mDisplayId, state);
+        }
     }
 
     public boolean isTouchExploring() {
@@ -314,6 +338,16 @@
         return mLastReceivedEvent;
     }
 
+    /** Gets the most recently received policy flags. */
+    public int getLastReceivedPolicyFlags() {
+        return mLastReceivedPolicyFlags;
+    }
+
+    /** Gets the most recently received raw event. */
+    public MotionEvent getLastReceivedRawEvent() {
+        return mLastReceivedRawEvent;
+    }
+
     /** @return The the last injected hover event. */
     public MotionEvent getLastInjectedHoverEvent() {
         return mLastInjectedHoverEvent;
@@ -354,6 +388,18 @@
         return mLastInjectedHoverEventForClick;
     }
 
+    public boolean isServiceDetectingGestures() {
+        return mServiceDetectsGestures;
+    }
+
+    /** Whether the service is handling gesture detection. */
+    public void setServiceDetectsGestures(boolean mode) {
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "serviceDetectsGestures: " + mode);
+        }
+        mServiceDetectsGesturesRequested = mode;
+    }
+
     /** This class tracks where and when a pointer went down. It does not track its movement. */
     class ReceivedPointerTracker {
         private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index fab58e8..b72b095 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1164,17 +1164,6 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(
-                        TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
-                    updateReportSignalStrengthDecision(r.subId);
-                    try {
-                        if (mSignalStrength[r.phoneId] != null) {
-                            r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
-                        }
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
                 if (validateEventAndUserLocked(
                         r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
                     try {
@@ -1353,27 +1342,6 @@
         }
     }
 
-    private void updateReportSignalStrengthDecision(int subscriptionId) {
-        synchronized (mRecords) {
-            TelephonyManager telephonyManager = (TelephonyManager) mContext
-                    .getSystemService(Context.TELEPHONY_SERVICE);
-            for (Record r : mRecords) {
-                // If any of the system clients wants to always listen to signal strength,
-                // we need to set it on.
-                if (r.matchTelephonyCallbackEvent(
-                        TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
-                    telephonyManager.createForSubscriptionId(subscriptionId)
-                            .setAlwaysReportSignalStrength(true);
-                    return;
-                }
-            }
-            // If none of the system clients wants to always listen to signal strength,
-            // we need to set it off.
-            telephonyManager.createForSubscriptionId(subscriptionId)
-                    .setAlwaysReportSignalStrength(false);
-        }
-    }
-
     private String getCallIncomingNumber(Record record, int phoneId) {
         // Only reveal the incoming number if the record has read call log permission.
         return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
@@ -1457,14 +1425,6 @@
                     }
 
                     mRecords.remove(i);
-
-                    // Every time a client that is registrating to always receive the signal
-                    // strength is removed from registry records, we need to check if
-                    // the signal strength decision needs to update on its slot.
-                    if (r.matchTelephonyCallbackEvent(
-                            TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
-                        updateReportSignalStrengthDecision(r.subId);
-                    }
                     return;
                 }
             }
@@ -1696,10 +1656,8 @@
                         log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
                                 + " phoneId=" + phoneId + " ss=" + signalStrength);
                     }
-                    if ((r.matchTelephonyCallbackEvent(
+                    if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
-                            || r.matchTelephonyCallbackEvent(
-                            TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
                             && idMatch(r, subId, phoneId)) {
                         try {
                             if (DBG) {
@@ -3120,11 +3078,6 @@
                     android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
         }
 
-        if ((events.contains(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
-        }
-
         if (isPrivilegedPhoneStatePermissionRequired(events)) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
@@ -3285,9 +3238,7 @@
             }
         }
 
-        if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
-                || events.contains(
-                TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+        if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
             try {
                 if (mSignalStrength[phoneId] != null) {
                     SignalStrength signalStrength = mSignalStrength[phoneId];
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cf4d048..c22c90e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -31,6 +31,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.AppOpsManager.OP_NONE;
@@ -15330,6 +15331,11 @@
     }
 
     @Override
+    public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+        mUserController.setStopBackgroundUsersOnSwitch(value);
+    }
+
+    @Override
     public int stopUser(final int userId, boolean force, final IStopUserCallback callback) {
         return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ false,
                 /* callback= */ callback, /* keyEvictedCallback= */ null);
@@ -16652,6 +16658,11 @@
                 @Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
             ActivityManagerService.this.setVoiceInteractionManagerProvider(provider);
         }
+
+        @Override
+        public void setStopBackgroundUsersOnSwitch(int value) {
+            ActivityManagerService.this.setStopBackgroundUsersOnSwitch(value);
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0f71639..25adddd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -29,6 +29,8 @@
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
 
 import android.app.ActivityManager;
@@ -100,6 +102,7 @@
 import com.android.internal.util.MemInfoReader;
 import com.android.server.am.LowMemDetector.MemFactor;
 import com.android.server.compat.PlatformCompat;
+import com.android.server.utils.Slogf;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -128,6 +131,10 @@
 import javax.microedition.khronos.egl.EGLSurface;
 
 final class ActivityManagerShellCommand extends ShellCommand {
+
+    static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerShellCommand" : TAG_AM;
+
+
     public static final String NO_CLASS_ERROR_CODE = "Error type 3";
 
     private static final String SHELL_PACKAGE_NAME = "com.android.shell";
@@ -325,6 +332,8 @@
                     return runServiceRestartBackoff(pw);
                 case "get-isolated-pids":
                     return runGetIsolatedProcesses(pw);
+                case "set-stop-user-on-switch":
+                    return runSetStopBackgroundUsersOnSwitch(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -3175,6 +3184,30 @@
         return 0;
     }
 
+    private int runSetStopBackgroundUsersOnSwitch(PrintWriter pw) throws RemoteException {
+        mInternal.enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                "setStopBackgroundUsersOnSwitch()");
+        String arg = getNextArg();
+        if (arg == null) {
+            Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): resetting to default value");
+            mInternal.setStopBackgroundUsersOnSwitch(
+                    ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT);
+            pw.println("Reset to default value");
+            return 0;
+        }
+
+        boolean stop = Boolean.parseBoolean(arg);
+        int value = stop
+                ? ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE
+                : ActivityManager.STOP_BG_USERS_ON_SWITCH_FALSE;
+
+        Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): setting to %d (%b)", value, stop);
+        mInternal.setStopBackgroundUsersOnSwitch(value);
+        pw.println("Set to " + stop);
+
+        return 0;
+    }
+
     private Resources getResources(PrintWriter pw) throws RemoteException {
         // system resources does not contain all the device configuration, construct it manually.
         Configuration config = mInterface.getConfiguration();
@@ -3507,6 +3540,10 @@
             pw.println("            Shows the restart backoff policy state for <PACKAGE_NAME>.");
             pw.println("  get-isolated-pids <UID>");
             pw.println("         Get the PIDs of isolated processes with packages in this <UID>");
+            pw.println("  set-stop-user-on-switch [true|false]");
+            pw.println("         Sets whether the current user (and its profiles) should be stopped"
+                    + " when switching to a different user.");
+            pw.println("         Without arguments, it resets to the value defined by platform.");
             pw.println();
             Intent.printIntentArgsHelp(pw, "");
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index fa7eae3..0c518a0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -19,6 +19,9 @@
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT;
+import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE;
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
 import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
 import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
@@ -368,6 +371,13 @@
     @GuardedBy("mLock")
     private boolean mInitialized;
 
+    /**
+     * Defines the behavior of whether the background users should be stopped when the foreground
+     * user is switched.
+     */
+    @GuardedBy("mLock")
+    private @StopBgUsersOnSwitch int mStopBgUsersOnSwitch = STOP_BG_USERS_ON_SWITCH_DEFAULT;
+
     UserController(ActivityManagerService service) {
         this(new Injector(service));
     }
@@ -408,8 +418,33 @@
         }
     }
 
+    void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+        if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
+                == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException(
+                    "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to "
+                            + "call setStopBackgroundUsersOnSwitch()");
+        }
+
+        synchronized (mLock) {
+            Slogf.i(TAG, "setStopBackgroundUsersOnSwitch(): %d -> %d",
+                    mStopBgUsersOnSwitch, value);
+            mStopBgUsersOnSwitch = value;
+        }
+    }
+
     private boolean shouldStopBackgroundUsersOnSwitch() {
-        int property = SystemProperties.getInt("fw.stop_bg_users_on_switch", -1);
+        synchronized (mLock) {
+            if (mStopBgUsersOnSwitch != STOP_BG_USERS_ON_SWITCH_DEFAULT) {
+                final boolean value = mStopBgUsersOnSwitch == STOP_BG_USERS_ON_SWITCH_TRUE;
+                Slogf.i(TAG, "isStopBackgroundUsersOnSwitch(): returning overridden value (%b)",
+                        value);
+                return value;
+            }
+        }
+        final int property = SystemProperties.getInt("fw.stop_bg_users_on_switch", -1);
         return property == -1 ? mDelayUserDataLocking : property == 1;
     }
 
@@ -2636,8 +2671,9 @@
             pw.println("  mTargetUserId:" + mTargetUserId);
             pw.println("  mLastActiveUsers:" + mLastActiveUsers);
             pw.println("  mDelayUserDataLocking:" + mDelayUserDataLocking);
-            pw.println("  shouldStopBackgroundUsersOnSwitch:"
+            pw.println("  shouldStopBackgroundUsersOnSwitch():"
                     + shouldStopBackgroundUsersOnSwitch());
+            pw.println("  mStopBgUsersOnSwitch:" + mStopBgUsersOnSwitch);
             pw.println("  mMaxRunningUsers:" + mMaxRunningUsers);
             pw.println("  mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
             pw.println("  mInitialized:" + mInitialized);
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 8c1a90c..fc0ee23 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,7 +1,3 @@
 include platform/frameworks/base:/core/java/android/permission/OWNERS
 
-per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
-per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
-per-file DefaultPermissionGrantPolicy.java = toddke@google.com
-per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
-per-file DefaultPermissionGrantPolicy.java = patb@google.com
+per-file DefaultPermissionGrantPolicy.java = file:platform/frameworks/base:/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index 3a9b2dc..cb52e5f 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -19,6 +19,7 @@
 import static java.util.stream.Collectors.toMap;
 
 import android.Manifest;
+import android.content.ComponentName;
 import android.content.Context;
 import android.os.ISystemConfig;
 import android.util.ArrayMap;
@@ -87,14 +88,14 @@
         }
 
         @Override
-        public List<String> getEnabledComponentOverrides(String packageName) {
+        public List<ComponentName> getEnabledComponentOverrides(String packageName) {
             ArrayMap<String, Boolean> systemComponents = SystemConfig.getInstance()
                     .getComponentsEnabledStates(packageName);
-            List<String> enabledComponent = new ArrayList<>();
+            List<ComponentName> enabledComponent = new ArrayList<>();
             if (systemComponents != null) {
                 for (Map.Entry<String, Boolean> entry : systemComponents.entrySet()) {
                     if (Boolean.TRUE.equals(entry.getValue())) {
-                        enabledComponent.add(entry.getKey());
+                        enabledComponent.add(new ComponentName(packageName, entry.getKey()));
                     }
                 }
             }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
index 0e78785..45f0e67 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
@@ -27,18 +27,20 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.os.Handler;
+import android.view.Display;
 import android.view.MotionEvent;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.server.accessibility.AccessibilityManagerService;
+
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 
 import java.util.ArrayList;
 
-/**
- * Tests for GestureManifold
- */
+/** Tests for GestureManifold */
 public class GestureManifoldTest {
 
     // Constants for testRecognizeGesturePath()
@@ -50,25 +52,23 @@
     private GestureManifold mManifold;
     private TouchState mState;
     private GestureManifold.Listener mResultListener;
+    @Mock private AccessibilityManagerService mMockAms;
 
     @Before
     public void setUp() {
         Context context = InstrumentationRegistry.getContext();
         // Construct a testable GestureManifold.
         mResultListener = mock(GestureManifold.Listener.class);
-        mState = new TouchState();
+        mState = new TouchState(Display.DEFAULT_DISPLAY, mMockAms);
         Handler handler = new Handler(context.getMainLooper());
         mManifold = new GestureManifold(context, mResultListener, mState, handler);
         // Play the role of touch explorer in updating the shared state.
         when(mResultListener.onGestureStarted()).thenReturn(onGestureStarted());
-
-
     }
 
-
     @Test
     public void testRecognizeGesturePath() {
-        final int d = 1000;  // Length of each segment in the test gesture, in pixels.
+        final int d = 1000; // Length of each segment in the test gesture, in pixels.
 
         testPath(p(-d, +0), AccessibilityService.GESTURE_SWIPE_LEFT);
         testPath(p(+d, +0), AccessibilityService.GESTURE_SWIPE_RIGHT);
@@ -138,9 +138,10 @@
 
         // For each path step from start (non-inclusive) to end ... add a motion point.
         for (int step = 1; step < numSteps; ++step) {
-            path.add(new PointF(
-                    (start.x + (stepX * (float) step)),
-                    (start.y + (stepY * (float) step))));
+            path.add(
+                    new PointF(
+                            (start.x + (stepX * (float) step)),
+                            (start.y + (stepY * (float) step))));
         }
     }
 
@@ -164,11 +165,12 @@
             } else if (pointIndex == path.size() - 1) {
                 action = MotionEvent.ACTION_UP;
             }
-            MotionEvent event = MotionEvent.obtain(eventDownTimeMs, eventTimeMs, action,
-                    point.x, point.y, 0);
+            MotionEvent event =
+                    MotionEvent.obtain(eventDownTimeMs, eventTimeMs, action, point.x, point.y, 0);
 
             // Send event.
-            mState.onReceivedMotionEvent(event);
+            // In this case the event and raw event values are the same.
+            mState.onReceivedMotionEvent(event, event, policyFlags);
             mManifold.onMotionEvent(event, event, policyFlags);
             eventTimeMs += PATH_STEP_MILLISEC;
             if (mState.isClear()) {
@@ -178,8 +180,9 @@
 
         mState.clear();
         // Check that correct gesture was recognized.
-        verify(mResultListener).onGestureCompleted(
-                argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId));
+        verify(mResultListener)
+                .onGestureCompleted(
+                        argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId));
     }
 
     private boolean onGestureStarted() {
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 41e24dd..2ff4ac5 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -17,6 +17,8 @@
 package android.telephony;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -66,10 +68,15 @@
     private final IBinder mLiveToken;
 
     private SignalStrengthUpdateRequest(
-            @NonNull List<SignalThresholdInfo> signalThresholdInfos,
+            @Nullable List<SignalThresholdInfo> signalThresholdInfos,
             boolean isReportingRequestedWhileIdle,
             boolean isSystemThresholdReportingRequestedWhileIdle) {
-        validate(signalThresholdInfos);
+        // System app (like Bluetooth) can specify the request to report system thresholds while
+        // device is idle (with permission protection). In this case, the request doesn't need to
+        // provide a non-empty list of SignalThresholdInfo which is only asked for public apps.
+        if (!isSystemThresholdReportingRequestedWhileIdle) {
+            validate(signalThresholdInfos);
+        }
 
         mSignalThresholdInfos = signalThresholdInfos;
         mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle;
@@ -128,13 +135,15 @@
         /**
          * Set the builder object if require reporting on the system thresholds when device is idle.
          *
-         * This can only used by the system caller.
+         * <p>This can only used by the system caller. Requires permission
+         * {@link android.Manifest.permission#LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH}.
          *
          * @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the
          *                                                     system thresholds when device is idle
          * @return the builder to facilitate the chaining
          * @hide
          */
+        @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
         public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle(
                 boolean isSystemThresholdReportingRequestedWhileIdle) {
             mIsSystemThresholdReportingRequestedWhileIdle =
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9c52220..0236306 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12524,26 +12524,6 @@
     }
 
     /**
-     * Enable or disable signal strength changes from radio will always be reported in any
-     * condition (e.g. screen is off). This is only allowed for System caller.
-     *
-     * @param isEnabled {@code true} for enabling; {@code false} for disabling.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setAlwaysReportSignalStrength(boolean isEnabled) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                telephony.setAlwaysReportSignalStrength(getSubId(), isEnabled);
-            }
-        } catch (RemoteException ex) {
-            Log.e(TAG, "setAlwaysReportSignalStrength RemoteException", ex);
-            ex.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
      * Get the most recently available signal strength information.
      *
      * Get the most recent SignalStrength information reported by the modem. Due
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6765576..b3c5d042 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1003,11 +1003,6 @@
      boolean isManualNetworkSelectionAllowed(int subId);
 
     /**
-     * Enable or disable always reporting signal strength changes from radio.
-     */
-     void setAlwaysReportSignalStrength(int subId, boolean isEnable);
-
-    /**
      * Get P-CSCF address from PCO after data connection is established or modified.
      * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
      * @param callingPackage The package making the call.