1/ Initial preparation for separate shell main thread

- Add @BinderThread/@ShellMainThread/@SysUIMainThread annotations
  for some common shell classes and callbacks
- Add mechanism to inject shell-anim/shell-main/sysui-main/etc
  executors (removed AnimationThread static instance)
(Note: The shell main thread will still be the sysui main thread)

Bug: 161979899
Test: atest WMShellUnitTests

Change-Id: I4adfeaebe5b79fab7c9451eef7b54fcdf4d8d40a
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 856c9c2..96e0559 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -122,6 +122,7 @@
         "kotlinx-coroutines-android",
         "kotlinx-coroutines-core",
         "iconloader_base",
+        "jsr330",
         "protolog-lib",
         "SettingsLib",
         "WindowManager-Shell-proto",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
index ada7e1a..45948dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
@@ -19,6 +19,7 @@
 import android.view.Gravity;
 
 import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.letterbox.LetterboxConfigController;
 import com.android.wm.shell.onehanded.OneHanded;
@@ -61,6 +62,7 @@
     }
 
     /** Dumps WM Shell internal state. */
+    @ExternalThread
     public void dump(PrintWriter pw) {
         mShellTaskOrganizer.dump(pw, "");
         pw.println();
@@ -76,6 +78,7 @@
 
 
     /** Returns {@code true} if command was found and executed. */
+    @ExternalThread
     public boolean handleCommand(String[] args, PrintWriter pw) {
         if (args.length < 2) {
             // Argument at position 0 is "WMShell".
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
index 118f189..94555de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
@@ -21,6 +21,7 @@
 
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.letterbox.LetterboxTaskListener;
 import com.android.wm.shell.splitscreen.SplitScreen;
@@ -56,6 +57,7 @@
         mFullscreenTaskListener = fullscreenTaskListener;
     }
 
+    @ExternalThread
     public void init() {
         // Start listening for display changes
         mDisplayImeController.startMonitorDisplays();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index beb9690..174c16a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -31,12 +31,14 @@
 import android.os.IBinder;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.SurfaceControl;
 import android.window.ITaskOrganizerController;
 import android.window.TaskAppearedInfo;
 import android.window.TaskOrganizer;
 
+import androidx.annotation.BinderThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -45,6 +47,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
 
 import java.io.PrintWriter;
@@ -119,7 +122,7 @@
             ShellExecutor mainExecutor, ShellExecutor animExecutor, Context context) {
         super(taskOrganizerController, mainExecutor);
         mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) mTransitions.register(this);
         // TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled
         //  by a controller, that class should be create while porting
         //  ActivityRecord#addStartingWindow to WMShell.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
index 120039d..10195b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
@@ -23,9 +23,9 @@
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
-import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -35,15 +35,18 @@
 import android.window.TransitionInfo;
 import android.window.WindowOrganizer;
 
+import androidx.annotation.BinderThread;
+
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.util.ArrayList;
 
 /** Plays transition animations */
-public class Transitions extends ITransitionPlayer.Stub {
+public class Transitions {
     private static final String TAG = "ShellTransitions";
 
     /** Set to {@code true} to enable shell transitions. */
@@ -54,6 +57,7 @@
     private final TransactionPool mTransactionPool;
     private final ShellExecutor mMainExecutor;
     private final ShellExecutor mAnimExecutor;
+    private final TransitionPlayerImpl mPlayerImpl;
 
     /** Keeps track of currently tracked transitions and all the animations associated with each */
     private final ArrayMap<IBinder, ArrayList<Animator>> mActiveTransitions = new ArrayMap<>();
@@ -64,6 +68,11 @@
         mTransactionPool = pool;
         mMainExecutor = mainExecutor;
         mAnimExecutor = animExecutor;
+        mPlayerImpl = new TransitionPlayerImpl();
+    }
+
+    public void register(ShellTaskOrganizer taskOrganizer) {
+        taskOrganizer.registerTransitionPlayer(mPlayerImpl);
     }
 
     // TODO(shell-transitions): real animations
@@ -115,77 +124,73 @@
                 || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
     }
 
-    @Override
-    public void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
+    private void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
                 transitionToken, info);
         // start task
-        mMainExecutor.execute(() -> {
-            if (!mActiveTransitions.containsKey(transitionToken)) {
-                Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken
-                        + " expecting one of " + mActiveTransitions.keySet());
-            }
-            if (mActiveTransitions.get(transitionToken) != null) {
-                throw new IllegalStateException("Got a duplicate onTransitionReady call for "
-                        + transitionToken);
-            }
-            mActiveTransitions.put(transitionToken, new ArrayList<>());
-            boolean isOpening = isOpeningType(info.getType());
-            if (info.getRootLeash().isValid()) {
-                t.show(info.getRootLeash());
-            }
-            // changes should be ordered top-to-bottom in z
-            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-                final TransitionInfo.Change change = info.getChanges().get(i);
-                final SurfaceControl leash = change.getLeash();
-                final int mode = info.getChanges().get(i).getMode();
+        if (!mActiveTransitions.containsKey(transitionToken)) {
+            Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken
+                    + " expecting one of " + mActiveTransitions.keySet());
+        }
+        if (mActiveTransitions.get(transitionToken) != null) {
+            throw new IllegalStateException("Got a duplicate onTransitionReady call for "
+                    + transitionToken);
+        }
+        mActiveTransitions.put(transitionToken, new ArrayList<>());
+        boolean isOpening = isOpeningType(info.getType());
+        if (info.getRootLeash().isValid()) {
+            t.show(info.getRootLeash());
+        }
+        // changes should be ordered top-to-bottom in z
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            final SurfaceControl leash = change.getLeash();
+            final int mode = info.getChanges().get(i).getMode();
 
-                // Don't animate anything with an animating parent
-                if (change.getParent() != null) {
-                    if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) {
-                        t.show(leash);
-                        t.setMatrix(leash, 1, 0, 0, 1);
-                    }
-                    continue;
-                }
-
-                t.reparent(leash, info.getRootLeash());
-                t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x,
-                        change.getEndAbsBounds().top - info.getRootOffset().y);
-                // Put all the OPEN/SHOW on top
+            // Don't animate anything with an animating parent
+            if (change.getParent() != null) {
                 if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) {
                     t.show(leash);
                     t.setMatrix(leash, 1, 0, 0, 1);
-                    if (isOpening) {
-                        // put on top and fade in
-                        t.setLayer(leash, info.getChanges().size() - i);
-                        t.setAlpha(leash, 0.f);
-                        startExampleAnimation(transitionToken, leash, true /* show */);
-                    } else {
-                        // put on bottom and leave it visible without fade
-                        t.setLayer(leash, -i);
-                        t.setAlpha(leash, 1.f);
-                    }
-                } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) {
-                    if (isOpening) {
-                        // put on bottom and leave visible without fade
-                        t.setLayer(leash, -i);
-                    } else {
-                        // put on top and fade out
-                        t.setLayer(leash, info.getChanges().size() - i);
-                        startExampleAnimation(transitionToken, leash, false /* show */);
-                    }
-                } else {
-                    t.setLayer(leash, info.getChanges().size() - i);
                 }
+                continue;
             }
-            t.apply();
-            onFinish(transitionToken);
-        });
+
+            t.reparent(leash, info.getRootLeash());
+            t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x,
+                    change.getEndAbsBounds().top - info.getRootOffset().y);
+            // Put all the OPEN/SHOW on top
+            if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) {
+                t.show(leash);
+                t.setMatrix(leash, 1, 0, 0, 1);
+                if (isOpening) {
+                    // put on top and fade in
+                    t.setLayer(leash, info.getChanges().size() - i);
+                    t.setAlpha(leash, 0.f);
+                    startExampleAnimation(transitionToken, leash, true /* show */);
+                } else {
+                    // put on bottom and leave it visible without fade
+                    t.setLayer(leash, -i);
+                    t.setAlpha(leash, 1.f);
+                }
+            } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) {
+                if (isOpening) {
+                    // put on bottom and leave visible without fade
+                    t.setLayer(leash, -i);
+                } else {
+                    // put on top and fade out
+                    t.setLayer(leash, info.getChanges().size() - i);
+                    startExampleAnimation(transitionToken, leash, false /* show */);
+                }
+            } else {
+                t.setLayer(leash, info.getChanges().size() - i);
+            }
+        }
+        t.apply();
+        onFinish(transitionToken);
     }
 
-    @MainThread
     private void onFinish(IBinder transition) {
         if (!mActiveTransitions.get(transition).isEmpty()) return;
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -194,16 +199,32 @@
         mOrganizer.finishTransition(transition, null, null);
     }
 
-    @Override
-    public void requestStartTransition(int type, @NonNull IBinder transitionToken) {
+    private void requestStartTransition(int type, @NonNull IBinder transitionToken) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: type=%d %s",
                 type, transitionToken);
-        mMainExecutor.execute(() -> {
-            if (mActiveTransitions.containsKey(transitionToken)) {
-                throw new RuntimeException("Transition already started " + transitionToken);
-            }
-            IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */);
-            mActiveTransitions.put(transition, null);
-        });
+
+        if (mActiveTransitions.containsKey(transitionToken)) {
+            throw new RuntimeException("Transition already started " + transitionToken);
+        }
+        IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */);
+        mActiveTransitions.put(transition, null);
+    }
+
+    @BinderThread
+    private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
+        @Override
+        public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
+                SurfaceControl.Transaction transaction) throws RemoteException {
+            mMainExecutor.execute(() -> {
+                Transitions.this.onTransitionReady(iBinder, transitionInfo, transaction);
+            });
+        }
+
+        @Override
+        public void requestStartTransition(int i, IBinder iBinder) throws RemoteException {
+            mMainExecutor.execute(() -> {
+                Transitions.this.requestStartTransition(i, iBinder);
+            });
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
index acb9a5da..834de3f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
@@ -18,11 +18,11 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import android.app.WindowConfiguration;
 import android.os.RemoteException;
-import android.view.WindowManagerGlobal;
 
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
+import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener;
 
 /**
  * The singleton wrapper to communicate between WindowManagerService and WMShell features
@@ -31,32 +31,30 @@
 public class WindowManagerShellWrapper {
     private static final String TAG = WindowManagerShellWrapper.class.getSimpleName();
 
-    public static final int WINDOWING_MODE_PINNED = WindowConfiguration.WINDOWING_MODE_PINNED;
-
     /**
      * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive
      * updates from the window manager service.
      */
-    private PinnedStackListenerForwarder mPinnedStackListenerForwarder =
-            new PinnedStackListenerForwarder();
+    private final PinnedStackListenerForwarder mPinnedStackListenerForwarder;
+
+    public WindowManagerShellWrapper(ShellExecutor shellMainExecutor) {
+        mPinnedStackListenerForwarder = new PinnedStackListenerForwarder(shellMainExecutor);
+    }
 
     /**
      * Adds a pinned stack listener, which will receive updates from the window manager service
      * along with any other pinned stack listeners that were added via this method.
      */
-    public void addPinnedStackListener(PinnedStackListenerForwarder.PinnedStackListener listener)
-            throws
-            RemoteException {
+    public void addPinnedStackListener(PinnedStackListener listener)
+            throws RemoteException {
         mPinnedStackListenerForwarder.addListener(listener);
-        WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
-                DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
+        mPinnedStackListenerForwarder.register(DEFAULT_DISPLAY);
     }
 
     /**
      * Removes a pinned stack listener.
      */
-    public void removePinnedStackListener(
-            PinnedStackListenerForwarder.PinnedStackListener listener) {
+    public void removePinnedStackListener(PinnedStackListener listener) {
         mPinnedStackListenerForwarder.removeListener(listener);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
index ef3e3e0..f5aa852 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
@@ -20,11 +20,14 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.wm.shell.common.annotations.ExternalThread;
+
 import java.io.PrintWriter;
 
 /**
  * Interface to engage app pairs feature.
  */
+@ExternalThread
 public interface AppPairs {
     /** Pairs indicated tasks. */
     boolean pair(int task1, int task2);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index aa7355b..40b41e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1213,7 +1213,7 @@
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
             if (mStackView != null) {
-                mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight));
+                mStackView.onImeVisibilityChanged(imeVisible, imeHeight);
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 92d15c5..fa5ac44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -30,6 +30,8 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
+import com.android.wm.shell.common.annotations.ExternalThread;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -40,6 +42,7 @@
 /**
  * Interface to engage bubbles feature.
  */
+@ExternalThread
 public interface Bubbles {
 
     @Retention(SOURCE)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java
deleted file mode 100644
index 96b9f86..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.common;
-
-import static android.os.Process.THREAD_PRIORITY_DISPLAY;
-
-import android.annotation.NonNull;
-import android.os.HandlerThread;
-import android.util.Singleton;
-
-/**
- * A singleton thread for Shell to run animations on.
- */
-public class AnimationThread extends HandlerThread {
-    private ShellExecutor mExecutor;
-
-    private AnimationThread() {
-        super("wmshell.anim", THREAD_PRIORITY_DISPLAY);
-    }
-
-    /** Get the singleton instance of this thread */
-    public static AnimationThread instance() {
-        return sAnimationThreadSingleton.get();
-    }
-
-    /**
-     * @return a shared {@link ShellExecutor} associated with this thread
-     * @hide
-     */
-    @NonNull
-    public ShellExecutor getExecutor() {
-        if (mExecutor == null) {
-            mExecutor = new HandlerExecutor(getThreadHandler());
-        }
-        return mExecutor;
-    }
-
-    private static final Singleton<AnimationThread> sAnimationThreadSingleton =
-            new Singleton<AnimationThread>() {
-                @Override
-                protected AnimationThread create() {
-                    final AnimationThread animThread = new AnimationThread();
-                    animThread.start();
-                    return animThread;
-                }
-            };
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 3263f79..cb4584c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -23,6 +23,10 @@
 import android.view.IWindowManager;
 import android.window.WindowContainerTransaction;
 
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
 import java.util.ArrayList;
 
 /**
@@ -35,39 +39,18 @@
 
     private final Handler mHandler;
     private final IWindowManager mWmService;
+    private final IDisplayWindowRotationController mControllerImpl;
 
     private final ArrayList<OnDisplayChangingListener> mRotationListener =
             new ArrayList<>();
     private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>();
 
-    private final IDisplayWindowRotationController mDisplayRotationController =
-            new IDisplayWindowRotationController.Stub() {
-                @Override
-                public void onRotateDisplay(int displayId, final int fromRotation,
-                        final int toRotation, IDisplayWindowRotationCallback callback) {
-                    mHandler.post(() -> {
-                        WindowContainerTransaction t = new WindowContainerTransaction();
-                        synchronized (mRotationListener) {
-                            mTmpListeners.clear();
-                            // Make a local copy in case the handlers add/remove themselves.
-                            mTmpListeners.addAll(mRotationListener);
-                        }
-                        for (OnDisplayChangingListener c : mTmpListeners) {
-                            c.onRotateDisplay(displayId, fromRotation, toRotation, t);
-                        }
-                        try {
-                            callback.continueRotateDisplay(toRotation, t);
-                        } catch (RemoteException e) {
-                        }
-                    });
-                }
-            };
-
     public DisplayChangeController(Handler mainHandler, IWindowManager wmService) {
         mHandler = mainHandler;
         mWmService = wmService;
+        mControllerImpl = new DisplayWindowRotationControllerImpl();
         try {
-            mWmService.setDisplayWindowRotationController(mDisplayRotationController);
+            mWmService.setDisplayWindowRotationController(mControllerImpl);
         } catch (RemoteException e) {
             throw new RuntimeException("Unable to register rotation controller");
         }
@@ -91,10 +74,41 @@
         }
     }
 
+    private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation,
+            IDisplayWindowRotationCallback callback) {
+        WindowContainerTransaction t = new WindowContainerTransaction();
+        synchronized (mRotationListener) {
+            mTmpListeners.clear();
+            // Make a local copy in case the handlers add/remove themselves.
+            mTmpListeners.addAll(mRotationListener);
+        }
+        for (OnDisplayChangingListener c : mTmpListeners) {
+            c.onRotateDisplay(displayId, fromRotation, toRotation, t);
+        }
+        try {
+            callback.continueRotateDisplay(toRotation, t);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @BinderThread
+    private class DisplayWindowRotationControllerImpl
+            extends IDisplayWindowRotationController.Stub {
+        @Override
+        public void onRotateDisplay(int displayId, final int fromRotation,
+                final int toRotation, IDisplayWindowRotationCallback callback) {
+            mHandler.post(() -> {
+                DisplayChangeController.this.onRotateDisplay(displayId, fromRotation, toRotation,
+                        callback);
+            });
+        }
+    }
+
     /**
      * Give a listener a chance to queue up configuration changes to execute as part of a
      * display rotation. The contents of {@link #onRotateDisplay} must run synchronously.
      */
+    @ShellMainThread
     public interface OnDisplayChangingListener {
         /**
          * Called before the display is rotated. Contents of this method must run synchronously.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 4189732..a413c05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -28,7 +28,10 @@
 import android.view.IDisplayWindowListener;
 import android.view.IWindowManager;
 
+import androidx.annotation.BinderThread;
+
 import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener;
+import com.android.wm.shell.common.annotations.ShellMainThread;
 
 import java.util.ArrayList;
 
@@ -45,6 +48,7 @@
     private final Context mContext;
     private final IWindowManager mWmService;
     private final DisplayChangeController mChangeController;
+    private final IDisplayWindowListener mDisplayContainerListener;
 
     private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>();
     private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
@@ -57,119 +61,13 @@
         return displayManager.getDisplay(displayId);
     }
 
-    private final IDisplayWindowListener mDisplayContainerListener =
-            new IDisplayWindowListener.Stub() {
-                @Override
-                public void onDisplayAdded(int displayId) {
-                    mHandler.post(() -> {
-                        synchronized (mDisplays) {
-                            if (mDisplays.get(displayId) != null) {
-                                return;
-                            }
-                            Display display = getDisplay(displayId);
-                            if (display == null) {
-                                // It's likely that the display is private to some app and thus not
-                                // accessible by system-ui.
-                                return;
-                            }
-                            DisplayRecord record = new DisplayRecord();
-                            record.mDisplayId = displayId;
-                            record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext
-                                    : mContext.createDisplayContext(display);
-                            record.mDisplayLayout = new DisplayLayout(record.mContext, display);
-                            mDisplays.put(displayId, record);
-                            for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
-                                mDisplayChangedListeners.get(i).onDisplayAdded(displayId);
-                            }
-                        }
-                    });
-                }
-
-                @Override
-                public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
-                    mHandler.post(() -> {
-                        synchronized (mDisplays) {
-                            DisplayRecord dr = mDisplays.get(displayId);
-                            if (dr == null) {
-                                Slog.w(TAG, "Skipping Display Configuration change on non-added"
-                                        + " display.");
-                                return;
-                            }
-                            Display display = getDisplay(displayId);
-                            if (display == null) {
-                                Slog.w(TAG, "Skipping Display Configuration change on invalid"
-                                        + " display. It may have been removed.");
-                                return;
-                            }
-                            Context perDisplayContext = mContext;
-                            if (displayId != Display.DEFAULT_DISPLAY) {
-                                perDisplayContext = mContext.createDisplayContext(display);
-                            }
-                            dr.mContext = perDisplayContext.createConfigurationContext(newConfig);
-                            dr.mDisplayLayout = new DisplayLayout(dr.mContext, display);
-                            for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
-                                mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
-                                        displayId, newConfig);
-                            }
-                        }
-                    });
-                }
-
-                @Override
-                public void onDisplayRemoved(int displayId) {
-                    mHandler.post(() -> {
-                        synchronized (mDisplays) {
-                            if (mDisplays.get(displayId) == null) {
-                                return;
-                            }
-                            for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
-                                mDisplayChangedListeners.get(i).onDisplayRemoved(displayId);
-                            }
-                            mDisplays.remove(displayId);
-                        }
-                    });
-                }
-
-                @Override
-                public void onFixedRotationStarted(int displayId, int newRotation) {
-                    mHandler.post(() -> {
-                        synchronized (mDisplays) {
-                            if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
-                                Slog.w(TAG, "Skipping onFixedRotationStarted on unknown"
-                                        + " display, displayId=" + displayId);
-                                return;
-                            }
-                            for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
-                                mDisplayChangedListeners.get(i).onFixedRotationStarted(
-                                        displayId, newRotation);
-                            }
-                        }
-                    });
-                }
-
-                @Override
-                public void onFixedRotationFinished(int displayId) {
-                    mHandler.post(() -> {
-                        synchronized (mDisplays) {
-                            if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
-                                Slog.w(TAG, "Skipping onFixedRotationFinished on unknown"
-                                        + " display, displayId=" + displayId);
-                                return;
-                            }
-                            for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
-                                mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId);
-                            }
-                        }
-                    });
-                }
-            };
-
     public DisplayController(Context context, Handler handler,
             IWindowManager wmService) {
         mHandler = handler;
         mContext = context;
         mWmService = wmService;
         mChangeController = new DisplayChangeController(mHandler, mWmService);
+        mDisplayContainerListener = new DisplayWindowListenerImpl();
         try {
             mWmService.registerDisplayWindowListener(mDisplayContainerListener);
         } catch (RemoteException e) {
@@ -232,18 +130,146 @@
         mChangeController.removeRotationListener(controller);
     }
 
+    private void onDisplayAdded(int displayId) {
+        synchronized (mDisplays) {
+            if (mDisplays.get(displayId) != null) {
+                return;
+            }
+            Display display = getDisplay(displayId);
+            if (display == null) {
+                // It's likely that the display is private to some app and thus not
+                // accessible by system-ui.
+                return;
+            }
+            DisplayRecord record = new DisplayRecord();
+            record.mDisplayId = displayId;
+            record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext
+                    : mContext.createDisplayContext(display);
+            record.mDisplayLayout = new DisplayLayout(record.mContext, display);
+            mDisplays.put(displayId, record);
+            for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
+                mDisplayChangedListeners.get(i).onDisplayAdded(displayId);
+            }
+        }
+    }
+
+    private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+        synchronized (mDisplays) {
+            DisplayRecord dr = mDisplays.get(displayId);
+            if (dr == null) {
+                Slog.w(TAG, "Skipping Display Configuration change on non-added"
+                        + " display.");
+                return;
+            }
+            Display display = getDisplay(displayId);
+            if (display == null) {
+                Slog.w(TAG, "Skipping Display Configuration change on invalid"
+                        + " display. It may have been removed.");
+                return;
+            }
+            Context perDisplayContext = mContext;
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                perDisplayContext = mContext.createDisplayContext(display);
+            }
+            dr.mContext = perDisplayContext.createConfigurationContext(newConfig);
+            dr.mDisplayLayout = new DisplayLayout(dr.mContext, display);
+            for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
+                mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
+                        displayId, newConfig);
+            }
+        }
+    }
+
+    private void onDisplayRemoved(int displayId) {
+        synchronized (mDisplays) {
+            if (mDisplays.get(displayId) == null) {
+                return;
+            }
+            for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+                mDisplayChangedListeners.get(i).onDisplayRemoved(displayId);
+            }
+            mDisplays.remove(displayId);
+        }
+    }
+
+    private void onFixedRotationStarted(int displayId, int newRotation) {
+        synchronized (mDisplays) {
+            if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+                Slog.w(TAG, "Skipping onFixedRotationStarted on unknown"
+                        + " display, displayId=" + displayId);
+                return;
+            }
+            for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+                mDisplayChangedListeners.get(i).onFixedRotationStarted(
+                        displayId, newRotation);
+            }
+        }
+    }
+
+    private void onFixedRotationFinished(int displayId) {
+        synchronized (mDisplays) {
+            if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+                Slog.w(TAG, "Skipping onFixedRotationFinished on unknown"
+                        + " display, displayId=" + displayId);
+                return;
+            }
+            for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+                mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId);
+            }
+        }
+    }
+
     private static class DisplayRecord {
         int mDisplayId;
         Context mContext;
         DisplayLayout mDisplayLayout;
     }
 
+    @BinderThread
+    private class DisplayWindowListenerImpl extends IDisplayWindowListener.Stub {
+        @Override
+        public void onDisplayAdded(int displayId) {
+            mHandler.post(() -> {
+                DisplayController.this.onDisplayAdded(displayId);
+            });
+        }
+
+        @Override
+        public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+            mHandler.post(() -> {
+                DisplayController.this.onDisplayConfigurationChanged(displayId, newConfig);
+            });
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            mHandler.post(() -> {
+                DisplayController.this.onDisplayRemoved(displayId);
+            });
+        }
+
+        @Override
+        public void onFixedRotationStarted(int displayId, int newRotation) {
+            mHandler.post(() -> {
+                DisplayController.this.onFixedRotationStarted(displayId, newRotation);
+            });
+        }
+
+        @Override
+        public void onFixedRotationFinished(int displayId) {
+            mHandler.post(() -> {
+                DisplayController.this.onFixedRotationFinished(displayId);
+            });
+        }
+    }
+
     /**
      * Gets notified when a display is added/removed to the WM hierarchy and when a display's
      * window-configuration changes.
      *
      * @see IDisplayWindowListener
      */
+    @ShellMainThread
     public interface OnDisplaysChangedListener {
         /**
          * Called when a display has been added to the WM hierarchy.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index a953ea2..3fbd7ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -39,6 +39,8 @@
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
+import androidx.annotation.BinderThread;
+
 import com.android.internal.view.IInputMethodManager;
 
 import java.util.ArrayList;
@@ -196,6 +198,7 @@
             mRotation = initialRotation;
         }
 
+        @BinderThread
         @Override
         public void insetsChanged(InsetsState insetsState) {
             mExecutor.execute(() -> {
@@ -217,6 +220,7 @@
             });
         }
 
+        @BinderThread
         @Override
         public void insetsControlChanged(InsetsState insetsState,
                 InsetsSourceControl[] activeControls) {
@@ -267,6 +271,7 @@
             }
         }
 
+        @BinderThread
         @Override
         public void showInsets(int types, boolean fromIme) {
             if ((types & WindowInsets.Type.ime()) == 0) {
@@ -276,6 +281,7 @@
             mExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */));
         }
 
+        @BinderThread
         @Override
         public void hideInsets(int types, boolean fromIme) {
             if ((types & WindowInsets.Type.ime()) == 0) {
@@ -285,6 +291,7 @@
             mExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */));
         }
 
+        @BinderThread
         @Override
         public void topFocusedWindowChanged(String packageName) {
             // no-op
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
index cd75840..fa0a75c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
@@ -28,6 +28,17 @@
     }
 
     @Override
+    public void execute(@NonNull Runnable command) {
+        if (mHandler.getLooper().isCurrentThread()) {
+            command.run();
+            return;
+        }
+        if (!mHandler.post(command)) {
+            throw new RuntimeException(mHandler + " is probably exiting");
+        }
+    }
+
+    @Override
     public void executeDelayed(@NonNull Runnable r, long delayMillis) {
         if (!mHandler.postDelayed(r, delayMillis)) {
             throw new RuntimeException(mHandler + " is probably exiting");
@@ -38,11 +49,4 @@
     public void removeCallbacks(@NonNull Runnable r) {
         mHandler.removeCallbacks(r);
     }
-
-    @Override
-    public void execute(@NonNull Runnable command) {
-        if (!mHandler.post(command)) {
-            throw new RuntimeException(mHandler + " is probably exiting");
-        }
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index aafe240..22b831b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -16,13 +16,40 @@
 
 package com.android.wm.shell.common;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Super basic Executor interface that adds support for delayed execution and removing callbacks.
  * Intended to wrap Handler while better-supporting testing.
  */
 public interface ShellExecutor extends Executor {
+
+    /**
+     * Executes the given runnable. If the caller is running on the same looper as this executor,
+     * the runnable must be executed immediately.
+     */
+    @Override
+    void execute(Runnable runnable);
+
+    /**
+     * Executes the given runnable in a blocking call. If the caller is running on the same looper
+     * as this executor, the runnable must be executed immediately.
+     *
+     * @throws InterruptedException if runnable does not return in the time specified by
+     *                              {@param waitTimeout}
+     */
+    default void executeBlocking(Runnable runnable, int waitTimeout, TimeUnit waitTimeUnit)
+            throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        execute(() -> {
+            runnable.run();
+            latch.countDown();
+        });
+        latch.await(waitTimeout, waitTimeUnit);
+    }
+
     /**
      * See {@link android.os.Handler#postDelayed(Runnable, long)}.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index 9cb1250..7321dc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -24,6 +24,10 @@
 import android.window.WindowContainerTransactionCallback;
 import android.window.WindowOrganizer;
 
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
 import java.util.ArrayList;
 
 /**
@@ -151,6 +155,7 @@
             mHandler.postDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
         }
 
+        @BinderThread
         @Override
         public void onTransactionReady(int id,
                 @NonNull SurfaceControl.Transaction t) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
index 0f6dd93..5e07718 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
@@ -19,12 +19,10 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ITaskStackListener;
-import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.os.IBinder;
 
 import androidx.annotation.BinderThread;
-import androidx.annotation.MainThread;
 
 /**
  * An interface to track task stack changes. Classes should implement this instead of
@@ -32,85 +30,61 @@
  */
 public interface TaskStackListenerCallback {
 
-    @MainThread
     default void onRecentTaskListUpdated() { }
 
-    @MainThread
     default void onRecentTaskListFrozenChanged(boolean frozen) { }
 
     @BinderThread
     default void onTaskStackChangedBackground() { }
 
-    @MainThread
     default void onTaskStackChanged() { }
 
-    @MainThread
     default void onTaskProfileLocked(int taskId, int userId) { }
 
-    @MainThread
     default void onTaskDisplayChanged(int taskId, int newDisplayId) { }
 
-    @MainThread
     default void onTaskCreated(int taskId, ComponentName componentName) { }
 
-    @MainThread
     default void onTaskRemoved(int taskId) { }
 
-    @MainThread
     default void onTaskMovedToFront(int taskId) { }
 
-    @MainThread
     default void onTaskMovedToFront(RunningTaskInfo taskInfo) {
         onTaskMovedToFront(taskInfo.taskId);
     }
 
-    @MainThread
     default void onTaskDescriptionChanged(RunningTaskInfo taskInfo) { }
 
-    @MainThread
     default void onTaskSnapshotChanged(int taskId, ActivityManager.TaskSnapshot snapshot) { }
 
-    @MainThread
     default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { }
 
-    @MainThread
     default void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
             boolean clearedTask, boolean wasVisible) { }
 
-    @MainThread
     default void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
 
-    @MainThread
     default void onActivityUnpinned() { }
 
-    @MainThread
     default void onActivityForcedResizable(String packageName, int taskId, int reason) { }
 
-    @MainThread
     default void onActivityDismissingDockedStack() { }
 
-    @MainThread
     default void onActivityLaunchOnSecondaryDisplayFailed() { }
 
-    @MainThread
     default void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo) {
         onActivityLaunchOnSecondaryDisplayFailed();
     }
 
-    @MainThread
     default void onActivityLaunchOnSecondaryDisplayRerouted() { }
 
-    @MainThread
     default void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo) {
         onActivityLaunchOnSecondaryDisplayRerouted();
     }
 
-    @MainThread
     default void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
 
-    @MainThread
     default void onActivityRotation(int displayId) { }
 
-    @MainThread
     default void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java
new file mode 100644
index 0000000..4009ad2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java
@@ -0,0 +1,18 @@
+package com.android.wm.shell.common.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * Annotates a method that or qualifies a provider runs aligned to the Choreographer SF vsync
+ * instead of the app vsync.
+ */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ChoreographerSfVsync {}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java
new file mode 100644
index 0000000..7560f71
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java
@@ -0,0 +1,15 @@
+package com.android.wm.shell.common.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/** Annotates a method or class that is called from an external thread to the Shell threads. */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExternalThread {}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java
new file mode 100644
index 0000000..0479f87
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java
@@ -0,0 +1,15 @@
+package com.android.wm.shell.common.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/** Annotates a method or qualifies a provider that runs on the Shell animation-thread */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ShellAnimationThread {}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java
new file mode 100644
index 0000000..423f4ce
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java
@@ -0,0 +1,15 @@
+package com.android.wm.shell.common.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/** Annotates a method or qualifies a provider that runs on the Shell main-thread */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ShellMainThread {}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
index 38e0519..3a2f0da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
@@ -20,11 +20,14 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.wm.shell.common.annotations.ExternalThread;
+
 import java.io.PrintWriter;
 
 /**
  * Interface to engage hide display cutout feature.
  */
+@ExternalThread
 public interface HideDisplayCutout {
     /**
      * Notifies {@link Configuration} changed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 9bb709f..821a007 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
 
 import java.io.PrintWriter;
@@ -25,6 +26,7 @@
 /**
  * Interface to engage one handed feature.
  */
+@ExternalThread
 public interface OneHanded {
     /**
      * Return one handed settings enabled or not.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
index 993e0e7..6145b7c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
@@ -19,12 +19,17 @@
 import android.app.RemoteAction;
 import android.content.ComponentName;
 import android.content.pm.ParceledListSlice;
+import android.os.RemoteException;
 import android.view.DisplayInfo;
 import android.view.IPinnedStackController;
 import android.view.IPinnedStackListener;
+import android.view.WindowManagerGlobal;
+
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.ShellExecutor;
 
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * PinnedStackListener that simply forwards all calls to each listener added via
@@ -32,8 +37,15 @@
  * {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any
  * previously set listener.
  */
-public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
-    private List<PinnedStackListener> mListeners = new ArrayList<>();
+public class PinnedStackListenerForwarder {
+
+    private final IPinnedStackListener mListenerImpl = new PinnedStackListenerImpl();
+    private final ShellExecutor mShellMainExecutor;
+    private final ArrayList<PinnedStackListener> mListeners = new ArrayList<>();
+
+    public PinnedStackListenerForwarder(ShellExecutor shellMainExecutor) {
+        mShellMainExecutor = shellMainExecutor;
+    }
 
     /** Adds a listener to receive updates from the WindowManagerService. */
     public void addListener(PinnedStackListener listener) {
@@ -45,62 +57,118 @@
         mListeners.remove(listener);
     }
 
-    @Override
-    public void onListenerRegistered(IPinnedStackController controller) {
+    public void register(int displayId) throws RemoteException {
+        WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
+                displayId, mListenerImpl);
+    }
+
+    private void onListenerRegistered(IPinnedStackController controller) {
         for (PinnedStackListener listener : mListeners) {
             listener.onListenerRegistered(controller);
         }
     }
 
-    @Override
-    public void onMovementBoundsChanged(boolean fromImeAdjustment) {
+    private void onMovementBoundsChanged(boolean fromImeAdjustment) {
         for (PinnedStackListener listener : mListeners) {
             listener.onMovementBoundsChanged(fromImeAdjustment);
         }
     }
 
-    @Override
-    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+    private void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
         for (PinnedStackListener listener : mListeners) {
             listener.onImeVisibilityChanged(imeVisible, imeHeight);
         }
     }
 
-    @Override
-    public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
+    private void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
         for (PinnedStackListener listener : mListeners) {
             listener.onActionsChanged(actions);
         }
     }
 
-    @Override
-    public void onActivityHidden(ComponentName componentName) {
+    private void onActivityHidden(ComponentName componentName) {
         for (PinnedStackListener listener : mListeners) {
             listener.onActivityHidden(componentName);
         }
     }
 
-    @Override
-    public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+    private void onDisplayInfoChanged(DisplayInfo displayInfo) {
         for (PinnedStackListener listener : mListeners) {
             listener.onDisplayInfoChanged(displayInfo);
         }
     }
 
-    @Override
-    public void onConfigurationChanged() {
+    private void onConfigurationChanged() {
         for (PinnedStackListener listener : mListeners) {
             listener.onConfigurationChanged();
         }
     }
 
-    @Override
-    public void onAspectRatioChanged(float aspectRatio) {
+    private void onAspectRatioChanged(float aspectRatio) {
         for (PinnedStackListener listener : mListeners) {
             listener.onAspectRatioChanged(aspectRatio);
         }
     }
 
+    @BinderThread
+    private class PinnedStackListenerImpl extends IPinnedStackListener.Stub {
+        @Override
+        public void onListenerRegistered(IPinnedStackController controller) {
+            mShellMainExecutor.execute(() -> {
+                PinnedStackListenerForwarder.this.onListenerRegistered(controller);
+            });
+        }
+
+        @Override
+        public void onMovementBoundsChanged(boolean fromImeAdjustment) {
+            mShellMainExecutor.execute(() -> {
+                PinnedStackListenerForwarder.this.onMovementBoundsChanged(fromImeAdjustment);
+            });
+        }
+
+        @Override
+        public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+            mShellMainExecutor.execute(() -> {
+                PinnedStackListenerForwarder.this.onImeVisibilityChanged(imeVisible, imeHeight);
+            });
+        }
+
+        @Override
+        public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
+            mShellMainExecutor.execute(() -> {
+                PinnedStackListenerForwarder.this.onActionsChanged(actions);
+            });
+        }
+
+        @Override
+        public void onActivityHidden(ComponentName componentName) {
+            mShellMainExecutor.execute(() -> {
+                PinnedStackListenerForwarder.this.onActivityHidden(componentName);
+            });
+        }
+
+        @Override
+        public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+            mShellMainExecutor.execute(() -> {
+                PinnedStackListenerForwarder.this.onDisplayInfoChanged(displayInfo);
+            });
+        }
+
+        @Override
+        public void onConfigurationChanged() {
+            mShellMainExecutor.execute(() -> {
+                PinnedStackListenerForwarder.this.onConfigurationChanged();
+            });
+        }
+
+        @Override
+        public void onAspectRatioChanged(float aspectRatio) {
+            mShellMainExecutor.execute(() -> {
+                PinnedStackListenerForwarder.this.onAspectRatioChanged(aspectRatio);
+            });
+        }
+    }
+
     /**
      * A counterpart of {@link IPinnedStackListener} with empty implementations.
      * Subclasses can ignore those methods they do not intend to take action upon.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 9fa222a..da9ce0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -17,12 +17,12 @@
 package com.android.wm.shell.pip;
 
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 
+import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 
 import java.io.PrintWriter;
@@ -31,6 +31,7 @@
 /**
  * Interface to engage picture in picture feature.
  */
+@ExternalThread
 public interface Pip {
     /**
      * Closes PIP (PIPed activity and PIP system UI).
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 f153aa5..7194fc7 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
@@ -20,15 +20,18 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.RemoteException;
 import android.view.MagnificationSpec;
+import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
+import androidx.annotation.BinderThread;
+
 import com.android.wm.shell.R;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -40,8 +43,7 @@
  * Expose the touch actions to accessibility as if this object were a window with a single view.
  * That pseudo-view exposes all of the actions this object can perform.
  */
-public class PipAccessibilityInteractionConnection
-        extends IAccessibilityInteractionConnection.Stub {
+public class PipAccessibilityInteractionConnection {
 
     public interface AccessibilityCallbacks {
         void onAccessibilityShowMenu();
@@ -50,14 +52,15 @@
     private static final long ACCESSIBILITY_NODE_ID = 1;
     private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;
 
-    private Context mContext;
-    private Handler mHandler;
+    private final Context mContext;
+    private final ShellExecutor mShellMainExcutor;
     private final @NonNull PipBoundsState mPipBoundsState;
-    private PipMotionHelper mMotionHelper;
-    private PipTaskOrganizer mTaskOrganizer;
-    private PipSnapAlgorithm mSnapAlgorithm;
-    private Runnable mUpdateMovementBoundCallback;
-    private AccessibilityCallbacks mCallbacks;
+    private final PipMotionHelper mMotionHelper;
+    private final PipTaskOrganizer mTaskOrganizer;
+    private final PipSnapAlgorithm mSnapAlgorithm;
+    private final Runnable mUpdateMovementBoundCallback;
+    private final AccessibilityCallbacks mCallbacks;
+    private final IAccessibilityInteractionConnection mConnectionImpl;
 
     private final Rect mNormalBounds = new Rect();
     private final Rect mExpandedBounds = new Rect();
@@ -69,19 +72,23 @@
             @NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
             PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm,
             AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback,
-            Handler handler) {
+            ShellExecutor shellMainExcutor) {
         mContext = context;
-        mHandler = handler;
+        mShellMainExcutor = shellMainExcutor;
         mPipBoundsState = pipBoundsState;
         mMotionHelper = motionHelper;
         mTaskOrganizer = taskOrganizer;
         mSnapAlgorithm = snapAlgorithm;
         mUpdateMovementBoundCallback = updateMovementBoundCallback;
         mCallbacks = callbacks;
+        mConnectionImpl = new PipAccessibilityInteractionConnectionImpl();
     }
 
-    @Override
-    public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
+    public void register(AccessibilityManager am) {
+        am.setPictureInPictureActionReplacingConnection(mConnectionImpl);
+    }
+
+    private void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
             Region interactiveRegion, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags,
             int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
@@ -94,8 +101,7 @@
         }
     }
 
-    @Override
-    public void performAccessibilityAction(long accessibilityNodeId, int action,
+    private void performAccessibilityAction(long accessibilityNodeId, int action,
             Bundle arguments, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags,
             int interrogatingPid, long interrogatingTid) {
@@ -115,9 +121,7 @@
             } else {
                 switch (action) {
                     case AccessibilityNodeInfo.ACTION_CLICK:
-                        mHandler.post(() -> {
-                            mCallbacks.onAccessibilityShowMenu();
-                        });
+                        mCallbacks.onAccessibilityShowMenu();
                         result = true;
                         break;
                     case AccessibilityNodeInfo.ACTION_DISMISS:
@@ -172,8 +176,7 @@
         });
     }
 
-    @Override
-    public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
+    private void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
             String viewId, Region interactiveRegion, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags,
             int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
@@ -185,8 +188,7 @@
         }
     }
 
-    @Override
-    public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
+    private void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
             Region interactiveRegion, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags,
             int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
@@ -198,8 +200,7 @@
         }
     }
 
-    @Override
-    public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
+    private void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
             int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
             int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
         // We have no view that can take focus
@@ -210,8 +211,7 @@
         }
     }
 
-    @Override
-    public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
+    private void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
             int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
             int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
         // We have no view that can take focus
@@ -222,16 +222,6 @@
         }
     }
 
-    @Override
-    public void clearAccessibilityFocus() {
-        // We should not be here.
-    }
-
-    @Override
-    public void notifyOutsideTouch() {
-        // Do nothing.
-    }
-
     /**
      * Update the normal and expanded bounds so they can be used for Resize.
      */
@@ -271,4 +261,95 @@
         mAccessibilityNodeInfoList.add(info);
         return mAccessibilityNodeInfoList;
     }
+
+    @BinderThread
+    private class PipAccessibilityInteractionConnectionImpl
+            extends IAccessibilityInteractionConnection.Stub {
+        @Override
+        public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
+                Region bounds, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, int flags,
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                Bundle arguments) throws RemoteException {
+            mShellMainExcutor.execute(() -> {
+                PipAccessibilityInteractionConnection.this
+                        .findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds,
+                                interactionId, callback, flags, interrogatingPid, interrogatingTid,
+                                spec, arguments);
+            });
+        }
+
+        @Override
+        public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId,
+                Region bounds, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, int flags,
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+                throws RemoteException {
+            mShellMainExcutor.execute(() -> {
+                PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId(
+                        accessibilityNodeId, viewId, bounds, interactionId, callback, flags,
+                        interrogatingPid, interrogatingTid, spec);
+            });
+        }
+
+        @Override
+        public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
+                Region bounds, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, int flags,
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+                throws RemoteException {
+            mShellMainExcutor.execute(() -> {
+                PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText(
+                        accessibilityNodeId, text, bounds, interactionId, callback, flags,
+                        interrogatingPid, interrogatingTid, spec);
+            });
+        }
+
+        @Override
+        public void findFocus(long accessibilityNodeId, int focusType, Region bounds,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+                throws RemoteException {
+            mShellMainExcutor.execute(() -> {
+                PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType,
+                        bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid,
+                        spec);
+            });
+        }
+
+        @Override
+        public void focusSearch(long accessibilityNodeId, int direction, Region bounds,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+                throws RemoteException {
+            mShellMainExcutor.execute(() -> {
+                PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId,
+                        direction,
+                        bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid,
+                        spec);
+            });
+        }
+
+        @Override
+        public void performAccessibilityAction(long accessibilityNodeId, int action,
+                Bundle arguments, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, int flags,
+                int interrogatingPid, long interrogatingTid) throws RemoteException {
+            mShellMainExcutor.execute(() -> {
+                PipAccessibilityInteractionConnection.this.performAccessibilityAction(
+                        accessibilityNodeId, action, arguments, interactionId, callback, flags,
+                        interrogatingPid, interrogatingTid);
+            });
+        }
+
+        @Override
+        public void clearAccessibilityFocus() throws RemoteException {
+            // Do nothing
+        }
+
+        @Override
+        public void notifyOutsideTouch() throws RemoteException {
+            // Do nothing
+        }
+    }
 }
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 db02e28..119ef6d 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
@@ -52,7 +52,6 @@
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.PipInputConsumer;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerCallback;
 import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -164,62 +163,54 @@
             PinnedStackListenerForwarder.PinnedStackListener {
         @Override
         public void onListenerRegistered(IPinnedStackController controller) {
-            mMainExecutor.execute(() -> mTouchHandler.setPinnedStackController(controller));
+            mTouchHandler.setPinnedStackController(controller);
         }
 
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
-            mMainExecutor.execute(() -> {
-                mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
-                mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
-            });
+            mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
+            mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
         }
 
         @Override
         public void onMovementBoundsChanged(boolean fromImeAdjustment) {
-            mMainExecutor.execute(() -> updateMovementBounds(null /* toBounds */,
+            updateMovementBounds(null /* toBounds */,
                     false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */,
-                    null /* windowContainerTransaction */));
+                    null /* windowContainerTransaction */);
         }
 
         @Override
         public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
-            mMainExecutor.execute(() -> mMenuController.setAppActions(actions));
+            mMenuController.setAppActions(actions);
         }
 
         @Override
         public void onActivityHidden(ComponentName componentName) {
-            mMainExecutor.execute(() -> {
-                if (componentName.equals(mPipBoundsState.getLastPipComponentName())) {
-                    // The activity was removed, we don't want to restore to the reentry state
-                    // saved for this component anymore.
-                    mPipBoundsState.setLastPipComponentName(null);
-                }
-            });
+            if (componentName.equals(mPipBoundsState.getLastPipComponentName())) {
+                // The activity was removed, we don't want to restore to the reentry state
+                // saved for this component anymore.
+                mPipBoundsState.setLastPipComponentName(null);
+            }
         }
 
         @Override
         public void onDisplayInfoChanged(DisplayInfo displayInfo) {
-            mMainExecutor.execute(() -> mPipBoundsState.setDisplayInfo(displayInfo));
+            mPipBoundsState.setDisplayInfo(displayInfo);
         }
 
         @Override
         public void onConfigurationChanged() {
-            mMainExecutor.execute(() -> {
-                mPipBoundsAlgorithm.onConfigurationChanged(mContext);
-                mTouchHandler.onConfigurationChanged();
-                mPipBoundsState.onConfigurationChanged();
-            });
+            mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+            mTouchHandler.onConfigurationChanged();
+            mPipBoundsState.onConfigurationChanged();
         }
 
         @Override
         public void onAspectRatioChanged(float aspectRatio) {
             // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params
             // change.
-            mMainExecutor.execute(() -> {
-                mPipBoundsState.setAspectRatio(aspectRatio);
-                mTouchHandler.onAspectRatioChanged();
-            });
+            mPipBoundsState.setAspectRatio(aspectRatio);
+            mTouchHandler.onAspectRatioChanged();
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
index 87ddb18..0c64c8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.common;
+package com.android.wm.shell.pip.phone;
 
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
-import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 
 import android.os.Binder;
 import android.os.IBinder;
@@ -30,7 +28,6 @@
 import android.view.IWindowManager;
 import android.view.InputChannel;
 import android.view.InputEvent;
-import android.view.WindowManagerGlobal;
 
 import java.io.PrintWriter;
 
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 9917701..1ab31f8 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
@@ -47,6 +47,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
@@ -157,7 +158,8 @@
             @NonNull PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer,
             FloatingContentCoordinator floatingContentCoordinator,
-            PipUiEventLogger pipUiEventLogger) {
+            PipUiEventLogger pipUiEventLogger,
+            ShellExecutor shellMainExecutor) {
         // Initialize the Pip input consumer
         mContext = context;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
@@ -188,7 +190,7 @@
         mFloatingContentCoordinator = floatingContentCoordinator;
         mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
                 mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
-                this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler);
+                this::onAccessibilityShowMenu, this::updateMovementBounds, shellMainExecutor);
 
         mPipUiEventLogger = pipUiEventLogger;
 
@@ -436,8 +438,11 @@
      * TODO Add appropriate description
      */
     public void onRegistrationChanged(boolean isRegistered) {
-        mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
-                ? mConnection : null);
+        if (isRegistered) {
+            mConnection.register(mAccessibilityManager);
+        } else {
+            mAccessibilityManager.setPictureInPictureActionReplacingConnection(null);
+        }
         if (!isRegistered && mTouchState.isUserInteracting()) {
             // If the input consumer is unregistered while the user is interacting, then we may not
             // get the final TOUCH_UP event, so clean up the dismiss target as well
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 5d8d5e6..763370b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -179,40 +179,33 @@
             PinnedStackListenerForwarder.PinnedStackListener {
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
-            mHandler.post(() -> {
-                mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
-                if (mState == STATE_PIP) {
-                    if (mImeVisible != imeVisible) {
-                        if (imeVisible) {
-                            // Save the IME height adjustment, and offset to not occlude the IME
-                            mPipBoundsState.getNormalBounds().offset(0, -imeHeight);
-                            mImeHeightAdjustment = imeHeight;
-                        } else {
-                            // Apply the inverse adjustment when the IME is hidden
-                            mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment);
-                        }
-                        mImeVisible = imeVisible;
-                        resizePinnedStack(STATE_PIP);
+            mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
+            if (mState == STATE_PIP) {
+                if (mImeVisible != imeVisible) {
+                    if (imeVisible) {
+                        // Save the IME height adjustment, and offset to not occlude the IME
+                        mPipBoundsState.getNormalBounds().offset(0, -imeHeight);
+                        mImeHeightAdjustment = imeHeight;
+                    } else {
+                        // Apply the inverse adjustment when the IME is hidden
+                        mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment);
                     }
+                    mImeVisible = imeVisible;
+                    resizePinnedStack(STATE_PIP);
                 }
-            });
+            }
         }
 
         @Override
         public void onMovementBoundsChanged(boolean fromImeAdjustment) {
-            mHandler.post(() -> {
-                mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
-
-                mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
-            });
+            mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
+            mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
         }
 
         @Override
         public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
             mCustomActions = actions;
-            mHandler.post(() -> {
-                mTvPipMenuController.setAppActions(mCustomActions);
-            });
+            mTvPipMenuController.setAppActions(mCustomActions);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index e55f065..7c70a4efa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -19,6 +19,8 @@
 import android.graphics.Rect;
 import android.window.WindowContainerToken;
 
+import com.android.wm.shell.common.annotations.ExternalThread;
+
 import java.io.PrintWriter;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
@@ -26,6 +28,7 @@
 /**
  * Interface to engage split screen feature.
  */
+@ExternalThread
 public interface SplitScreen {
     /** Called when keyguard showing state changed. */
     void onKeyguardVisibilityChanged(boolean isShowing);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index e602219..4efaebf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -33,6 +33,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -71,6 +72,9 @@
     @Mock
     private PipUiEventLogger mPipUiEventLogger;
 
+    @Mock
+    private ShellExecutor mShellMainExecutor;
+
     private PipBoundsState mPipBoundsState;
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
     private PipSnapAlgorithm mPipSnapAlgorithm;
@@ -94,7 +98,7 @@
         mPipSnapAlgorithm = new PipSnapAlgorithm();
         mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController,
                 mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
-                mFloatingContentCoordinator, mPipUiEventLogger);
+                mFloatingContentCoordinator, mPipUiEventLogger, mShellMainExecutor);
         mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
         mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
         mPipTouchHandler.setPipMotionHelper(mMotionHelper);