Introduce WindowOrganizer

WM currently only allows the organization of tasks, however we will soon
be allowing the organization of DisplayAreas. To help with the code
structure, we are introducing WindowOrganizer interface which will
contain common APIs for all types of windows organizers (e.g.
applyTransaction) and also be the interfaece for getting the controller
for other organizers.

Test: they pass!
Bug: 147406652
Bug: 152113464
Bug: 152117221

Change-Id: Id2797b288ce92430206c25345d60e7b0e3be98c8
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index c9673f5..1b941de 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -30,7 +30,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Singleton;
-import android.window.ITaskOrganizerController;
 
 import java.util.List;
 
@@ -159,24 +158,6 @@
                 }
             };
 
-    /** @hide */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public static ITaskOrganizerController getTaskOrganizerController() {
-        return ITaskOrganizerControllerSingleton.get();
-    }
-
-    private static final Singleton<ITaskOrganizerController> ITaskOrganizerControllerSingleton =
-            new Singleton<ITaskOrganizerController>() {
-                @Override
-                protected ITaskOrganizerController create() {
-                    try {
-                        return getService().getTaskOrganizerController();
-                    } catch (RemoteException e) {
-                        return null;
-                    }
-                }
-            };
-
     /**
      * Sets the windowing mode for a specific task. Only works on tasks of type
      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index ab5c678..03717ec 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -29,7 +29,6 @@
 import android.app.IRequestFinishCallback;
 import android.app.IServiceConnection;
 import android.app.IStopUserCallback;
-import android.window.ITaskOrganizerController;
 import android.app.ITaskStackListener;
 import android.app.IUiAutomationConnection;
 import android.app.IUidObserver;
@@ -69,9 +68,9 @@
 import android.os.WorkSource;
 import android.service.voice.IVoiceInteractionSession;
 import android.view.IRecentsAnimationRunner;
-import android.window.ITaskOrganizer;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationAdapter;
+import android.window.IWindowOrganizerController;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -340,10 +339,8 @@
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
 
-    /**
-     * Returns an interface enabling the management of task organizers.
-     */
-    ITaskOrganizerController getTaskOrganizerController();
+    /** Returns an interface enabling the management of window organizers. */
+    IWindowOrganizerController getWindowOrganizerController();
 
     /**
      * Sets whether we are currently in an interactive split screen resize operation where we
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index b2da239..0a04462 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -1,19 +1,18 @@
-/* //device/java/android/android/view/ITaskOrganizer.aidl
-**
-** Copyright 2019, 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.
-*/
+/*
+ * Copyright (C) 2019 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.window;
 
@@ -30,12 +29,6 @@
     void taskVanished(in ActivityManager.RunningTaskInfo taskInfo);
 
     /**
-     * Called upon completion of
-     * ActivityTaskManagerService#applyTaskOrganizerTransaction
-     */
-    void transactionReady(int id, in SurfaceControl.Transaction t);
-
-    /**
      * Will fire when core attributes of a Task's info change. Relevant properties include the
      * {@link WindowConfiguration.ActivityType} and whether it is resizable.
      *
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index e8af8e1..a8b6aae 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -36,20 +36,6 @@
      */
     void unregisterTaskOrganizer(ITaskOrganizer organizer);
 
-    /**
-     * Apply multiple WindowContainer operations at once.
-     * @param organizer If non-null this transaction will use the synchronization
-     *        scheme described in BLASTSyncEngine.java. The SurfaceControl transaction
-     *        containing the effects of this WindowContainer transaction will be passed
-     *        to the organizers Transaction ready callback. If null the transaction
-     *        will apply with non particular synchronization constraints (other than
-     *        it will all apply at once).
-     * @return If organizer was non-null returns an ID for the sync operation which will
-     *         later be passed to transactionReady. This lets TaskOrganizer implementations
-     *         differentiate overlapping sync operations.
-     */
-    int applyContainerTransaction(in WindowContainerTransaction t, ITaskOrganizer organizer);
-
     /** Creates a persistent root task in WM for a particular windowing-mode. */
     ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode);
 
diff --git a/core/java/android/window/IWindowContainerTransactionCallback.aidl b/core/java/android/window/IWindowContainerTransactionCallback.aidl
new file mode 100644
index 0000000..0579932
--- /dev/null
+++ b/core/java/android/window/IWindowContainerTransactionCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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 android.window;
+
+import android.view.SurfaceControl;
+
+/**
+ * See WindowOrganizer#applyTransaction.
+ * {@hide}
+ */
+oneway interface IWindowContainerTransactionCallback {
+    /** Called upon completion of WindowOrganizer#applyTransaction */
+    void transactionReady(int id, in SurfaceControl.Transaction t);
+}
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
new file mode 100644
index 0000000..4b47924
--- /dev/null
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -0,0 +1,46 @@
+/**
+ * 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 android.window;
+
+import android.window.ITaskOrganizerController;
+import android.window.IWindowContainerTransactionCallback;
+import android.window.WindowContainerTransaction;
+
+/** @hide */
+interface IWindowOrganizerController {
+
+    /**
+     * Apply multiple WindowContainer operations at once.
+     * @param t The transaction to apply.
+     */
+    void applyTransaction(in WindowContainerTransaction t);
+
+    /**
+     * Apply multiple WindowContainer operations at once.
+     * @param t The transaction to apply.
+     * @param callback This transaction will use the synchronization scheme described in
+     *        BLASTSyncEngine.java. The SurfaceControl transaction containing the effects of this
+     *        WindowContainer transaction will be passed to this callback when ready.
+     * @return An ID for the sync operation which will later be passed to transactionReady callback.
+     *         This lets the caller differentiate overlapping sync operations.
+     */
+    int applySyncTransaction(in WindowContainerTransaction t,
+            in IWindowContainerTransactionCallback callback);
+
+    /** @return An interface enabling the management of task organizers. */
+    ITaskOrganizerController getTaskOrganizerController();
+}
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
new file mode 100644
index 0000000..b8969c1
--- /dev/null
+++ b/core/java/android/window/WindowOrganizer.java
@@ -0,0 +1,157 @@
+/*
+ * 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 android.window;
+
+import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.os.RemoteException;
+import android.util.Singleton;
+
+import java.util.List;
+
+/**
+ * Class for organizing specific types of windows like Tasks and DisplayAreas
+ *
+ * @hide
+ */
+public class WindowOrganizer {
+
+    /**
+     * Apply multiple WindowContainer operations at once.
+     * @param t The transaction to apply.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public static void applyTransaction(WindowContainerTransaction t) throws RemoteException {
+        getWindowOrganizerController().applyTransaction(t);
+    }
+
+    /**
+     * Apply multiple WindowContainer operations at once.
+     * @param t The transaction to apply.
+     * @param callback This transaction will use the synchronization scheme described in
+     *        BLASTSyncEngine.java. The SurfaceControl transaction containing the effects of this
+     *        WindowContainer transaction will be passed to this callback when ready.
+     * @return An ID for the sync operation which will later be passed to transactionReady callback.
+     *         This lets the caller differentiate overlapping sync operations.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public static int applySyncTransaction(WindowContainerTransaction t,
+            IWindowContainerTransactionCallback callback) throws RemoteException {
+        return getWindowOrganizerController().applySyncTransaction(t, callback);
+    }
+
+    /** @hide */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    private static IWindowOrganizerController getWindowOrganizerController() {
+        return IWindowOrganizerControllerSingleton.get();
+    }
+
+    private static final Singleton<IWindowOrganizerController> IWindowOrganizerControllerSingleton =
+            new Singleton<IWindowOrganizerController>() {
+                @Override
+                protected IWindowOrganizerController create() {
+                    try {
+                        return ActivityTaskManager.getService().getWindowOrganizerController();
+                    } catch (RemoteException e) {
+                        return null;
+                    }
+                }
+            };
+
+    public static class TaskOrganizer {
+
+        /**
+         * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
+         * If there was already a TaskOrganizer for this windowing mode it will be evicted
+         * and receive taskVanished callbacks in the process.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static void registerOrganizer(ITaskOrganizer organizer, int windowingMode)
+                throws RemoteException {
+            getController().registerTaskOrganizer(organizer, windowingMode);
+        }
+
+        /** Unregisters a previously registered task organizer. */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static void unregisterOrganizer(ITaskOrganizer organizer) throws RemoteException {
+            getController().unregisterTaskOrganizer(organizer);
+        }
+
+        /** Creates a persistent root task in WM for a particular windowing-mode. */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static ActivityManager.RunningTaskInfo createRootTask(
+                int displayId, int windowingMode) throws RemoteException {
+            return getController().createRootTask(displayId, windowingMode);
+        }
+
+        /** Deletes a persistent root task in WM */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static boolean deleteRootTask(IWindowContainer task) throws RemoteException {
+            return getController().deleteRootTask(task);
+        }
+
+        /** Gets direct child tasks (ordered from top-to-bottom) */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static List<ActivityManager.RunningTaskInfo> getChildTasks(IWindowContainer parent,
+                int[] activityTypes) throws RemoteException {
+            return getController().getChildTasks(parent, activityTypes);
+        }
+
+        /** Gets all root tasks on a display (ordered from top-to-bottom) */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static List<ActivityManager.RunningTaskInfo> getRootTasks(
+                int displayId, int[] activityTypes) throws RemoteException {
+            return getController().getRootTasks(displayId, activityTypes);
+        }
+
+        /** Get the root task which contains the current ime target */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static IWindowContainer getImeTarget(int display) throws RemoteException {
+            return getController().getImeTarget(display);
+        }
+
+        /**
+         * Set's the root task to launch new tasks into on a display. {@code null} means no launch
+         * root and thus new tasks just end up directly on the display.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+        public static void setLaunchRoot(int displayId, IWindowContainer root)
+                throws RemoteException {
+            getController().setLaunchRoot(displayId, root);
+        }
+
+        private static ITaskOrganizerController getController() {
+            return ITaskOrganizerControllerSingleton.get();
+        }
+
+        private static final Singleton<ITaskOrganizerController> ITaskOrganizerControllerSingleton =
+                new Singleton<ITaskOrganizerController>() {
+                    @Override
+                    protected ITaskOrganizerController create() {
+                        try {
+                            return getWindowOrganizerController().getTaskOrganizerController();
+                        } catch (RemoteException e) {
+                            return null;
+                        }
+                    }
+                };
+
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 195050e..7f14645 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -42,6 +42,7 @@
 import android.window.IWindowContainer;
 import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
+import android.window.WindowOrganizer;
 
 import com.android.internal.os.SomeArgs;
 import com.android.systemui.R;
@@ -75,7 +76,6 @@
 
     private final Handler mMainHandler;
     private final Handler mUpdateHandler;
-    private final ITaskOrganizerController mTaskOrganizerController;
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipAnimationController mPipAnimationController;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
@@ -181,7 +181,6 @@
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper) {
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
-        mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
         mPipBoundsHandler = boundsHandler;
         mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
@@ -258,10 +257,6 @@
     }
 
     @Override
-    public void transactionReady(int id, SurfaceControl.Transaction t) {
-    }
-
-    @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         final PictureInPictureParams newParams = info.pictureInPictureParams;
         if (!shouldUpdateDestinationBounds(newParams)) {
@@ -415,7 +410,7 @@
                 wct.setBounds(mToken, taskBounds);
             }
             wct.setBoundsChangeTransaction(mToken, tx);
-            mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
+            WindowOrganizer.applyTransaction(wct);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to apply container transaction", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index a0dff3f..e89ce2e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.window.WindowOrganizer.TaskOrganizer;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -35,6 +36,7 @@
 import android.view.DisplayInfo;
 import android.view.IPinnedStackController;
 import android.window.WindowContainerTransaction;
+import android.window.WindowOrganizer;
 
 import com.android.systemui.Dependency;
 import com.android.systemui.UiOffloadThread;
@@ -248,8 +250,7 @@
         mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
 
         try {
-            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
-                    mPipTaskOrganizer, WINDOWING_MODE_PINNED);
+            TaskOrganizer.registerOrganizer(mPipTaskOrganizer, WINDOWING_MODE_PINNED);
             ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
             if (stackInfo != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 050acd5..2dcf1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.window.WindowOrganizer.TaskOrganizer;
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
@@ -44,6 +45,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.view.DisplayInfo;
+import android.window.WindowOrganizer;
 
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -293,8 +295,7 @@
 
         try {
             WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
-            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
-                    mPipTaskOrganizer, WINDOWING_MODE_PINNED);
+            TaskOrganizer.registerOrganizer(mPipTaskOrganizer, WINDOWING_MODE_PINNED);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 3d52812..01498f9 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -20,11 +20,11 @@
 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.WindowOrganizer.TaskOrganizer;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
-import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -38,6 +38,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.window.WindowContainerTransaction;
+import android.window.WindowOrganizer;
 
 import androidx.annotation.Nullable;
 
@@ -177,8 +178,7 @@
 
         private boolean getSecondaryHasFocus(int displayId) {
             try {
-                IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController()
-                        .getImeTarget(displayId);
+                IWindowContainer imeSplit = TaskOrganizer.getImeTarget(displayId);
                 return imeSplit != null
                         && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder());
             } catch (RemoteException e) {
@@ -267,8 +267,7 @@
                         SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
             }
             try {
-                ActivityTaskManager.getTaskOrganizerController()
-                        .applyContainerTransaction(wct, null /* organizer */);
+                WindowOrganizer.applyTransaction(wct);
             } catch (RemoteException e) {
             }
 
@@ -474,13 +473,12 @@
         mImeController.addPositionProcessor(mImePositionProcessor);
         mDisplayController.addDisplayChangingController(mRotationController);
         try {
-            mSplits.init(ActivityTaskManager.getTaskOrganizerController(), mSurfaceSession);
+            mSplits.init(mSurfaceSession);
             // Set starting tile bounds based on middle target
             final WindowContainerTransaction tct = new WindowContainerTransaction();
             int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
             mSplitLayout.resizeSplits(midPos, tct);
-            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
-                    null /* organizer */);
+            WindowOrganizer.applyTransaction(tct);
         } catch (Exception e) {
             Slog.e(TAG, "Failed to register docked stack listener", e);
         }
@@ -499,8 +497,7 @@
             final WindowContainerTransaction tct = new WindowContainerTransaction();
             mSplitLayout.resizeSplits(midPos, tct);
             try {
-                ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
-                        null /* organizer */);
+                WindowOrganizer.applyTransaction(tct);
             } catch (RemoteException e) {
             }
         } else if (mRotateSplitLayout != null
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index f82936a..8bbb548 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -22,9 +22,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.WindowOrganizer.TaskOrganizer;
 
 import android.app.ActivityManager.RunningTaskInfo;
-import android.window.ITaskOrganizerController;
 import android.app.WindowConfiguration;
 import android.os.RemoteException;
 import android.util.Log;
@@ -49,13 +49,12 @@
         mDivider = divider;
     }
 
-    void init(ITaskOrganizerController organizerController, SurfaceSession session)
-            throws RemoteException {
-        organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        mPrimary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+    void init(SurfaceSession session) throws RemoteException {
+        TaskOrganizer.registerOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        TaskOrganizer.registerOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
                 WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        mSecondary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+        mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
                 WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
         mPrimarySurface = mPrimary.token.getLeash();
         mSecondarySurface = mSecondary.token.getLeash();
@@ -91,10 +90,6 @@
     }
 
     @Override
-    public void transactionReady(int id, SurfaceControl.Transaction t) {
-    }
-
-    @Override
     public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
         if (taskInfo.displayId != DEFAULT_DISPLAY) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 0e391ec..8724e49 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.WindowOrganizer.TaskOrganizer;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -31,6 +32,7 @@
 import android.window.IWindowContainer;
 import android.window.WindowContainerTransaction;
 import android.view.WindowManagerGlobal;
+import android.window.WindowOrganizer;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -111,8 +113,7 @@
         WindowContainerTransaction t = new WindowContainerTransaction();
         splitLayout.resizeSplits(position, t);
         try {
-            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(t,
-                    null /* organizer */);
+            WindowOrganizer.applyTransaction(t);
         } catch (RemoteException e) {
         }
     }
@@ -122,10 +123,8 @@
         boolean resizable = false;
         try {
             List<ActivityManager.RunningTaskInfo> rootTasks = parent == null
-                    ? ActivityTaskManager.getTaskOrganizerController().getRootTasks(
-                            Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
-                    : ActivityTaskManager.getTaskOrganizerController().getChildTasks(parent,
-                            HOME_AND_RECENTS);
+                    ? TaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
+                    : TaskOrganizer.getChildTasks(parent, HOME_AND_RECENTS);
             for (int i = 0, n = rootTasks.size(); i < n; ++i) {
                 final ActivityManager.RunningTaskInfo ti = rootTasks.get(i);
                 out.add(ti.token);
@@ -174,11 +173,9 @@
             // Set launchtile first so that any stack created after
             // getAllStackInfos and before reparent (even if unlikely) are placed
             // correctly.
-            ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(
-                    DEFAULT_DISPLAY, tiles.mSecondary.token);
+            TaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token);
             List<ActivityManager.RunningTaskInfo> rootTasks =
-                    ActivityTaskManager.getTaskOrganizerController().getRootTasks(DEFAULT_DISPLAY,
-                            null /* activityTypes */);
+                    TaskOrganizer.getRootTasks(DEFAULT_DISPLAY, null /* activityTypes */);
             WindowContainerTransaction wct = new WindowContainerTransaction();
             if (rootTasks.isEmpty()) {
                 return false;
@@ -192,8 +189,7 @@
                         true /* onTop */);
             }
             boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
-            ActivityTaskManager.getTaskOrganizerController()
-                    .applyContainerTransaction(wct, null /* organizer */);
+            WindowOrganizer.applyTransaction(wct);
             return isHomeResizable;
         } catch (RemoteException e) {
             Log.w(TAG, "Error moving fullscreen tasks to secondary split: " + e);
@@ -216,22 +212,18 @@
         try {
             // Set launch root first so that any task created after getChildContainers and
             // before reparent (pretty unlikely) are put into fullscreen.
-            ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(Display.DEFAULT_DISPLAY,
-                    null);
+            TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
             // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
             //                 plus specific APIs to clean this up.
             List<ActivityManager.RunningTaskInfo> primaryChildren =
-                    ActivityTaskManager.getTaskOrganizerController().getChildTasks(
-                            tiles.mPrimary.token, null /* activityTypes */);
+                    TaskOrganizer.getChildTasks(tiles.mPrimary.token, null /* activityTypes */);
             List<ActivityManager.RunningTaskInfo> secondaryChildren =
-                    ActivityTaskManager.getTaskOrganizerController().getChildTasks(
-                            tiles.mSecondary.token, null /* activityTypes */);
+                    TaskOrganizer.getChildTasks(tiles.mSecondary.token, null /* activityTypes */);
             // In some cases (eg. non-resizable is launched), system-server will leave split-screen.
             // as a result, the above will not capture any tasks; yet, we need to clean-up the
             // home task bounds.
             List<ActivityManager.RunningTaskInfo> freeHomeAndRecents =
-                    ActivityTaskManager.getTaskOrganizerController().getRootTasks(
-                            Display.DEFAULT_DISPLAY, HOME_AND_RECENTS);
+                    TaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS);
             if (primaryChildren.isEmpty() && secondaryChildren.isEmpty()
                     && freeHomeAndRecents.isEmpty()) {
                 return;
@@ -281,8 +273,7 @@
             }
             // Reset focusable to true
             wct.setFocusable(tiles.mPrimary.token, true /* focusable */);
-            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
-                    null /* organizer */);
+            WindowOrganizer.applyTransaction(wct);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to remove stack: " + e);
         }
@@ -290,8 +281,7 @@
 
     static void applyContainerTransaction(WindowContainerTransaction wct) {
         try {
-            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
-                    null /* organizer */);
+            WindowOrganizer.applyTransaction(wct);
         } catch (RemoteException e) {
             Log.w(TAG, "Error setting focusability: " + e);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1844395..f7278e7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -230,6 +230,7 @@
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
+import android.window.IWindowOrganizerController;
 import android.window.WindowContainerTransaction;
 import android.view.WindowManager;
 
@@ -663,10 +664,8 @@
 
     private FontScaleSettingObserver mFontScaleSettingObserver;
 
-    /**
-     * Stores the registration and state of TaskOrganizers in use.
-     */
-    TaskOrganizerController mTaskOrganizerController = new TaskOrganizerController(this);
+    WindowOrganizerController mWindowOrganizerController;
+    TaskOrganizerController mTaskOrganizerController;
 
     private int mDeviceOwnerUid = Process.INVALID_UID;
 
@@ -724,6 +723,8 @@
         mLifecycleManager = new ClientLifecycleManager();
         mInternal = new LocalService();
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
+        mWindowOrganizerController = new WindowOrganizerController(this);
+        mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController;
     }
 
     public void onSystemReady() {
@@ -2772,7 +2773,7 @@
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
-        mTaskOrganizerController.applyContainerTransaction(wct, null);
+        mWindowOrganizerController.applyTransaction(wct);
     }
 
     /**
@@ -4311,7 +4312,7 @@
                     }
                 }
                 wct.setBounds(secondary.mRemoteToken, otherRect);
-                mTaskOrganizerController.applyContainerTransaction(wct, null /* organizer */);
+                mWindowOrganizerController.applyTransaction(wct);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -4332,10 +4333,10 @@
     }
 
     @Override
-    public ITaskOrganizerController getTaskOrganizerController() {
+    public IWindowOrganizerController getWindowOrganizerController() {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS,
-                "getTaskOrganizerController()");
-        return mTaskOrganizerController;
+                "getWindowOrganizerController()");
+        return mWindowOrganizerController;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 776a986..60b817c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -531,8 +531,7 @@
             try {
                 mDisplayContent.sendNewConfiguration();
                 if (t != null) {
-                    mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t,
-                            null /* organizer */);
+                    mService.mAtmService.mWindowOrganizerController.applyTransaction(t);
                 }
             } finally {
                 mService.mAtmService.continueWindowLayout();
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 9bf4d63..fc58ee7 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -22,59 +22,34 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
-import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
+import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration;
 import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.util.ArraySet;
 import android.util.Slog;
-import android.view.SurfaceControl;
 import android.window.ITaskOrganizerController;
 import android.window.ITaskOrganizer;
 import android.window.IWindowContainer;
-import android.window.WindowContainerTransaction;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.WeakHashMap;
 
 /**
  * Stores the TaskOrganizers associated with a given windowing mode and
  * their associated state.
  */
-class TaskOrganizerController extends ITaskOrganizerController.Stub
-    implements BLASTSyncEngine.TransactionReadyListener {
+class TaskOrganizerController extends ITaskOrganizerController.Stub {
     private static final String TAG = "TaskOrganizerController";
 
-    /** Flag indicating that an applied transaction may have effected lifecycle */
-    private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
-    private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
-
-    /**
-     * Masks specifying which configurations task-organizers can control. Incoming transactions
-     * will be filtered to only include these.
-     */
-    private static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION
-                | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE;
-    private static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS
-                | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
     /**
      * Masks specifying which configurations are important to report back to an organizer when
      * changed.
@@ -181,13 +156,9 @@
     final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap();
     final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap();
 
-    final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap();
-
     private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
     private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
 
-    private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
-
     final ActivityTaskManagerService mService;
 
     RunningTaskInfo mTmpTaskInfo;
@@ -498,253 +469,4 @@
             Binder.restoreCallingIdentity(ident);
         }
     }
-
-    private int sanitizeAndApplyChange(WindowContainer container,
-            WindowContainerTransaction.Change change) {
-        if (!(container instanceof Task)) {
-            throw new RuntimeException("Invalid token in task transaction");
-        }
-        final Task task = (Task) container;
-        // The "client"-facing API should prevent bad changes; however, just in case, sanitize
-        // masks here.
-        final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
-        final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
-        int effects = 0;
-        if (configMask != 0) {
-            Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
-            c.setTo(change.getConfiguration(), configMask, windowMask);
-            container.onRequestedOverrideConfigurationChanged(c);
-            // TODO(b/145675353): remove the following once we could apply new bounds to the
-            // pinned stack together with its children.
-            resizePinnedStackIfNeeded(container, configMask, windowMask, c);
-            effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
-        }
-        if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
-            if (container.setFocusable(change.getFocusable())) {
-                effects |= TRANSACT_EFFECTS_LIFECYCLE;
-            }
-        }
-        if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
-            if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) {
-                effects |= TRANSACT_EFFECTS_LIFECYCLE;
-            }
-        }
-        return effects;
-    }
-
-    private int sanitizeAndApplyHierarchyOp(WindowContainer container,
-            WindowContainerTransaction.HierarchyOp hop) {
-        if (!(container instanceof Task)) {
-            throw new IllegalArgumentException("Invalid container in hierarchy op");
-        }
-        if (container.getDisplayContent() == null) {
-            Slog.w(TAG, "Container is no longer attached: " + container);
-            return 0;
-        }
-        if (hop.isReparent()) {
-            // special case for tiles since they are "virtual" parents
-            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
-                ActivityStack as = (ActivityStack) container;
-                TaskTile newParent = hop.getNewParent() == null ? null
-                        : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
-                if (as.getTile() != newParent) {
-                    if (as.getTile() != null) {
-                        as.getTile().removeChild(as);
-                    }
-                    if (newParent != null) {
-                        if (!as.affectedBySplitScreenResize()) {
-                            return 0;
-                        }
-                        newParent.addChild(as, POSITION_TOP);
-                    }
-                }
-                if (hop.getToTop()) {
-                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
-                } else {
-                    as.getDisplay().positionStackAtBottom(as);
-                }
-            } else if (container instanceof Task) {
-                throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
-            }
-        } else {
-            // Ugh, of course ActivityStack has its own special reorder logic...
-            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
-                ActivityStack as = (ActivityStack) container;
-                if (hop.getToTop()) {
-                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
-                } else {
-                    as.getDisplay().positionStackAtBottom(as);
-                }
-            } else {
-                container.getParent().positionChildAt(
-                        hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
-                        container, false /* includingParents */);
-            }
-        }
-        return TRANSACT_EFFECTS_LIFECYCLE;
-    }
-
-    private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
-            int windowMask, Configuration config) {
-        if ((container instanceof ActivityStack)
-                && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
-                && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
-            final ActivityStack stack = (ActivityStack) container;
-            if (stack.inPinnedWindowingMode()) {
-                stack.resize(config.windowConfiguration.getBounds(),
-                        null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */);
-            }
-        }
-    }
-
-    private int applyWindowContainerChange(WindowContainer wc,
-            WindowContainerTransaction.Change c) {
-        int effects = sanitizeAndApplyChange(wc, c);
-
-        final Task tr = wc.asTask();
-
-        final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
-        if (t != null) {
-            tr.setMainWindowSizeChangeTransaction(t);
-        }
-
-        Rect enterPipBounds = c.getEnterPipBounds();
-        if (enterPipBounds != null) {
-            mService.mStackSupervisor.updatePictureInPictureMode(tr,
-                    enterPipBounds, true);
-        }
-
-        final int windowingMode = c.getWindowingMode();
-        if (windowingMode > -1) {
-            tr.setWindowingMode(windowingMode);
-        }
-        final int childWindowingMode = c.getActivityWindowingMode();
-        if (childWindowingMode > -1) {
-            tr.setActivityWindowingMode(childWindowingMode);
-        }
-
-        return effects;
-    }
-
-    @Override
-    public int applyContainerTransaction(WindowContainerTransaction t, ITaskOrganizer organizer) {
-        enforceStackPermission("applyContainerTransaction()");
-        int syncId = -1;
-        if (t == null) {
-            throw new IllegalArgumentException(
-                    "Null transaction passed to applyContainerTransaction");
-        }
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                int effects = 0;
-
-                /**
-                 * If organizer is non-null we are looking to synchronize this transaction
-                 * by collecting all the results in to a SurfaceFlinger transaction and
-                 * then delivering that to the given organizers transaction ready callback.
-                 * See {@link BLASTSyncEngine} for the details of the operation. But at
-                 * a high level we create a sync operation with a given ID and an associated
-                 * organizer. Then we notify each WindowContainer in this WindowContainer
-                 * transaction that it is participating in a sync operation with that
-                 * ID. Once everything is notified we tell the BLASTSyncEngine
-                 * "setSyncReady" which means that we have added everything
-                 * to the set. At any point after this, all the WindowContainers
-                 * will eventually finish applying their changes and notify the
-                 * BLASTSyncEngine which will deliver the Transaction to the organizer.
-                 */
-                if (organizer != null) {
-                    syncId = startSyncWithOrganizer(organizer);
-                }
-                mService.deferWindowLayout();
-                try {
-                    ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
-                    Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
-                            t.getChanges().entrySet().iterator();
-                    while (entries.hasNext()) {
-                        final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
-                                entries.next();
-                        final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
-                        int containerEffect = applyWindowContainerChange(wc, entry.getValue());
-                        effects |= containerEffect;
-
-                        // Lifecycle changes will trigger ensureConfig for everything.
-                        if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
-                                && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
-                            haveConfigChanges.add(wc);
-                        }
-                        if (syncId >= 0) {
-                            mBLASTSyncEngine.addToSyncSet(syncId, wc);
-                        }
-                    }
-                    // Hierarchy changes
-                    final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
-                    for (int i = 0, n = hops.size(); i < n; ++i) {
-                        final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
-                        final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
-                        effects |= sanitizeAndApplyHierarchyOp(wc, hop);
-                    }
-                    if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
-                        // Already calls ensureActivityConfig
-                        mService.mRootWindowContainer.ensureActivitiesVisible(
-                                null, 0, PRESERVE_WINDOWS);
-                    } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
-                        final PooledConsumer f = PooledLambda.obtainConsumer(
-                                ActivityRecord::ensureActivityConfiguration,
-                                PooledLambda.__(ActivityRecord.class), 0,
-                                false /* preserveWindow */);
-                        try {
-                            for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
-                                final WindowContainer wc = haveConfigChanges.valueAt(i);
-                                final Task task = wc.asTask();
-                                final TaskTile tile = task != null ? task.asTile() : null;
-                                if (tile != null) {
-                                    // Special case for tile. Can't override normal forAllActivities
-                                    // because it generates duplicate calls and messes up existing
-                                    // code-paths.
-                                    tile.forAllTileActivities(f);
-                                } else {
-                                    wc.forAllActivities(f);
-                                }
-                            }
-                        } finally {
-                            f.recycle();
-                        }
-                    }
-                } finally {
-                    mService.continueWindowLayout();
-                    if (syncId >= 0) {
-                        setSyncReady(syncId);
-                    }
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return syncId;
-    }
-
-    @Override
-    public void transactionReady(int id, SurfaceControl.Transaction sc) {
-        final ITaskOrganizer organizer = mTaskOrganizersByPendingSyncId.get(id);
-        if (organizer == null) {
-            Slog.e(TAG, "Got transaction complete for unexpected ID");
-        }
-        try {
-            organizer.transactionReady(id, sc);
-        } catch (RemoteException e) {
-        }
-
-        mTaskOrganizersByPendingSyncId.remove(id);
-    }
-
-    int startSyncWithOrganizer(ITaskOrganizer organizer) {
-        int id = mBLASTSyncEngine.startSyncSet(this);
-        mTaskOrganizersByPendingSyncId.put(id, organizer);
-        return id;
-    }
-
-    void setSyncReady(int id) {
-        mBLASTSyncEngine.setReady(id);
-    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
new file mode 100644
index 0000000..ebfe109
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -0,0 +1,347 @@
+/*
+ * 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.server.wm;
+
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
+import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.ITaskOrganizerController;
+import android.window.IWindowContainerTransactionCallback;
+import android.window.IWindowOrganizerController;
+import android.window.WindowContainerTransaction;
+
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Server side implementation for the interface for organizing windows
+ * @see android.window.WindowOrganizer
+ */
+class WindowOrganizerController extends IWindowOrganizerController.Stub
+    implements BLASTSyncEngine.TransactionReadyListener {
+
+    private static final String TAG = "WindowOrganizerController";
+
+    /** Flag indicating that an applied transaction may have effected lifecycle */
+    private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
+    private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
+
+    /**
+     * Masks specifying which configurations task-organizers can control. Incoming transactions
+     * will be filtered to only include these.
+     */
+    static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION
+            | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE;
+    static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS
+            | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
+
+    private final ActivityTaskManagerService mService;
+    private final WindowManagerGlobalLock mGlobalLock;
+
+    private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
+    private final HashMap<Integer, IWindowContainerTransactionCallback>
+            mTransactionCallbacksByPendingSyncId = new HashMap();
+
+    final TaskOrganizerController mTaskOrganizerController;
+
+    WindowOrganizerController(ActivityTaskManagerService atm) {
+        mService = atm;
+        mGlobalLock = atm.mGlobalLock;
+        mTaskOrganizerController = new TaskOrganizerController(mService);
+    }
+
+    @Override
+    public void applyTransaction(WindowContainerTransaction t) {
+        applySyncTransaction(t, null /*callback*/);
+    }
+
+    @Override
+    public int applySyncTransaction(WindowContainerTransaction t,
+            IWindowContainerTransactionCallback callback) {
+        enforceStackPermission("applySyncTransaction()");
+        int syncId = -1;
+        if (t == null) {
+            throw new IllegalArgumentException(
+                    "Null transaction passed to applySyncTransaction");
+        }
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                int effects = 0;
+
+                /**
+                 * If callback is non-null we are looking to synchronize this transaction by
+                 * collecting all the results in to a SurfaceFlinger transaction and then delivering
+                 * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the
+                 * details of the operation. But at a high level we create a sync operation with a
+                 * given ID and an associated callback. Then we notify each WindowContainer in this
+                 * WindowContainer transaction that it is participating in a sync operation with
+                 * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady"
+                 * which means that we have added everything to the set. At any point after this,
+                 * all the WindowContainers will eventually finish applying their changes and notify
+                 * the BLASTSyncEngine which will deliver the Transaction to the callback.
+                 */
+                if (callback != null) {
+                    syncId = startSyncWithOrganizer(callback);
+                }
+                mService.deferWindowLayout();
+                try {
+                    ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
+                    Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
+                            t.getChanges().entrySet().iterator();
+                    while (entries.hasNext()) {
+                        final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
+                                entries.next();
+                        final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
+                        int containerEffect = applyWindowContainerChange(wc, entry.getValue());
+                        effects |= containerEffect;
+
+                        // Lifecycle changes will trigger ensureConfig for everything.
+                        if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
+                                && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
+                            haveConfigChanges.add(wc);
+                        }
+                        if (syncId >= 0) {
+                            mBLASTSyncEngine.addToSyncSet(syncId, wc);
+                        }
+                    }
+                    // Hierarchy changes
+                    final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
+                    for (int i = 0, n = hops.size(); i < n; ++i) {
+                        final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
+                        final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+                        effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+                    }
+                    if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
+                        // Already calls ensureActivityConfig
+                        mService.mRootWindowContainer.ensureActivitiesVisible(
+                                null, 0, PRESERVE_WINDOWS);
+                    } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
+                        final PooledConsumer f = PooledLambda.obtainConsumer(
+                                ActivityRecord::ensureActivityConfiguration,
+                                PooledLambda.__(ActivityRecord.class), 0,
+                                false /* preserveWindow */);
+                        try {
+                            for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
+                                final WindowContainer wc = haveConfigChanges.valueAt(i);
+                                final Task task = wc.asTask();
+                                final TaskTile tile = task != null ? task.asTile() : null;
+                                if (tile != null) {
+                                    // Special case for tile. Can't override normal forAllActivities
+                                    // because it generates duplicate calls and messes up existing
+                                    // code-paths.
+                                    tile.forAllTileActivities(f);
+                                } else {
+                                    wc.forAllActivities(f);
+                                }
+                            }
+                        } finally {
+                            f.recycle();
+                        }
+                    }
+                } finally {
+                    mService.continueWindowLayout();
+                    if (syncId >= 0) {
+                        setSyncReady(syncId);
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return syncId;
+    }
+
+    private int sanitizeAndApplyChange(WindowContainer container,
+            WindowContainerTransaction.Change change) {
+        if (!(container instanceof Task)) {
+            throw new RuntimeException("Invalid token in task transaction");
+        }
+        final Task task = (Task) container;
+        // The "client"-facing API should prevent bad changes; however, just in case, sanitize
+        // masks here.
+        final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
+        final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
+        int effects = 0;
+        if (configMask != 0) {
+            Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
+            c.setTo(change.getConfiguration(), configMask, windowMask);
+            container.onRequestedOverrideConfigurationChanged(c);
+            // TODO(b/145675353): remove the following once we could apply new bounds to the
+            // pinned stack together with its children.
+            resizePinnedStackIfNeeded(container, configMask, windowMask, c);
+            effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+        }
+        if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
+            if (container.setFocusable(change.getFocusable())) {
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
+            }
+        }
+        if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+            if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) {
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
+            }
+        }
+        return effects;
+    }
+
+    private int sanitizeAndApplyHierarchyOp(WindowContainer container,
+            WindowContainerTransaction.HierarchyOp hop) {
+        if (!(container instanceof Task)) {
+            throw new IllegalArgumentException("Invalid container in hierarchy op");
+        }
+        if (container.getDisplayContent() == null) {
+            Slog.w(TAG, "Container is no longer attached: " + container);
+            return 0;
+        }
+        if (hop.isReparent()) {
+            // special case for tiles since they are "virtual" parents
+            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
+                ActivityStack as = (ActivityStack) container;
+                TaskTile newParent = hop.getNewParent() == null ? null
+                        : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
+                if (as.getTile() != newParent) {
+                    if (as.getTile() != null) {
+                        as.getTile().removeChild(as);
+                    }
+                    if (newParent != null) {
+                        if (!as.affectedBySplitScreenResize()) {
+                            return 0;
+                        }
+                        newParent.addChild(as, POSITION_TOP);
+                    }
+                }
+                if (hop.getToTop()) {
+                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+                } else {
+                    as.getDisplay().positionStackAtBottom(as);
+                }
+            } else if (container instanceof Task) {
+                throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
+            }
+        } else {
+            // Ugh, of course ActivityStack has its own special reorder logic...
+            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
+                ActivityStack as = (ActivityStack) container;
+                if (hop.getToTop()) {
+                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+                } else {
+                    as.getDisplay().positionStackAtBottom(as);
+                }
+            } else {
+                container.getParent().positionChildAt(
+                        hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+                        container, false /* includingParents */);
+            }
+        }
+        return TRANSACT_EFFECTS_LIFECYCLE;
+    }
+
+    private int applyWindowContainerChange(WindowContainer wc,
+            WindowContainerTransaction.Change c) {
+        int effects = sanitizeAndApplyChange(wc, c);
+
+        final Task tr = wc.asTask();
+
+        final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
+        if (t != null) {
+            tr.setMainWindowSizeChangeTransaction(t);
+        }
+
+        Rect enterPipBounds = c.getEnterPipBounds();
+        if (enterPipBounds != null) {
+            mService.mStackSupervisor.updatePictureInPictureMode(tr,
+                    enterPipBounds, true);
+        }
+
+        final int windowingMode = c.getWindowingMode();
+        if (windowingMode > -1) {
+            tr.setWindowingMode(windowingMode);
+        }
+        final int childWindowingMode = c.getActivityWindowingMode();
+        if (childWindowingMode > -1) {
+            tr.setActivityWindowingMode(childWindowingMode);
+        }
+
+        return effects;
+    }
+
+    private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
+            int windowMask, Configuration config) {
+        if ((container instanceof ActivityStack)
+                && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
+                && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
+            final ActivityStack stack = (ActivityStack) container;
+            if (stack.inPinnedWindowingMode()) {
+                stack.resize(config.windowConfiguration.getBounds(),
+                        null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */);
+            }
+        }
+    }
+
+    @Override
+    public ITaskOrganizerController getTaskOrganizerController() {
+        enforceStackPermission("getTaskOrganizerController()");
+        return mTaskOrganizerController;
+    }
+
+    int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
+        int id = mBLASTSyncEngine.startSyncSet(this);
+        mTransactionCallbacksByPendingSyncId.put(id, callback);
+        return id;
+    }
+
+    void setSyncReady(int id) {
+        mBLASTSyncEngine.setReady(id);
+    }
+
+    @Override
+    public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) {
+        final IWindowContainerTransactionCallback callback =
+                mTransactionCallbacksByPendingSyncId.get(mSyncId);
+
+        try {
+            callback.transactionReady(mSyncId, mergedTransaction);
+        } catch (RemoteException e) {
+        }
+
+        mTransactionCallbacksByPendingSyncId.remove(mSyncId);
+    }
+
+    private void enforceStackPermission(String func) {
+        mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 873fa02..658ba1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1026,9 +1026,6 @@
         public void taskVanished(ActivityManager.RunningTaskInfo info) {
         }
         @Override
-        public void transactionReady(int id, SurfaceControl.Transaction t) {
-        }
-        @Override
         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
             if (mInSplit) {
                 return;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 1624bbc..5359bd3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -231,7 +231,7 @@
         WindowContainerTransaction t = new WindowContainerTransaction();
         Rect newBounds = new Rect(10, 10, 100, 100);
         t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertEquals(newBounds, task.getBounds());
     }
 
@@ -246,7 +246,7 @@
         assertEquals(stack.mRemoteToken, info.stackToken);
         Rect newBounds = new Rect(10, 10, 100, 100);
         t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertEquals(newBounds, stack.getBounds());
     }
 
@@ -257,7 +257,7 @@
         final WindowContainerTransaction t = new WindowContainerTransaction();
 
         t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
 
         assertEquals(WINDOWING_MODE_FULLSCREEN, stack.getWindowingMode());
     }
@@ -270,7 +270,7 @@
 
         t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_PINNED);
         t.setActivityWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
 
         assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode());
         assertEquals(WINDOWING_MODE_PINNED, stack.getWindowingMode());
@@ -285,10 +285,10 @@
         WindowContainerTransaction t = new WindowContainerTransaction();
         assertTrue(task.isFocusable());
         t.setFocusable(stack.mRemoteToken, false);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertFalse(task.isFocusable());
         t.setFocusable(stack.mRemoteToken, true);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertTrue(task.isFocusable());
     }
 
@@ -300,10 +300,10 @@
         WindowContainerTransaction t = new WindowContainerTransaction();
         assertTrue(stack.shouldBeVisible(null));
         t.setHidden(stack.mRemoteToken, true);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertFalse(stack.shouldBeVisible(null));
         t.setHidden(stack.mRemoteToken, false);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertTrue(stack.shouldBeVisible(null));
     }
 
@@ -315,19 +315,19 @@
         final Task task = stack.getTopMostTask();
         WindowContainerTransaction t = new WindowContainerTransaction();
         t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         final int origScreenWDp = task.getConfiguration().screenHeightDp;
         final int origScreenHDp = task.getConfiguration().screenHeightDp;
         t = new WindowContainerTransaction();
         // verify that setting config overrides on parent restricts children.
         t.setScreenSizeDp(stack.mRemoteToken, origScreenWDp, origScreenHDp);
         t.setBounds(task.mRemoteToken, new Rect(10, 10, 150, 200));
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
         t = new WindowContainerTransaction();
         t.setScreenSizeDp(stack.mRemoteToken, Configuration.SCREEN_WIDTH_DP_UNDEFINED,
                 Configuration.SCREEN_HEIGHT_DP_UNDEFINED);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
         assertNotEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
     }
 
@@ -402,9 +402,6 @@
             public void taskVanished(RunningTaskInfo container) { }
 
             @Override
-            public void transactionReady(int id, SurfaceControl.Transaction t) { }
-
-            @Override
             public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
                 lastReportedTiles.add(info);
                 called[0] = true;
@@ -456,9 +453,6 @@
             public void taskVanished(RunningTaskInfo container) { }
 
             @Override
-            public void transactionReady(int id, SurfaceControl.Transaction t) { }
-
-            @Override
             public void onTaskInfoChanged(RunningTaskInfo info) {
                 lastReportedTiles.put(info.token.asBinder(), info);
             }
@@ -487,8 +481,7 @@
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
         wct.reparent(stack2.mRemoteToken, info2.token, true /* onTop */);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
-                null /* organizer */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertFalse(lastReportedTiles.isEmpty());
         assertEquals(ACTIVITY_TYPE_STANDARD,
                 lastReportedTiles.get(info1.token.asBinder()).topActivityType);
@@ -498,8 +491,7 @@
         lastReportedTiles.clear();
         wct = new WindowContainerTransaction();
         wct.reparent(stack2.mRemoteToken, info1.token, false /* onTop */);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
-                null /* organizer */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertFalse(lastReportedTiles.isEmpty());
         // Standard should still be on top of tile 1, so no change there
         assertFalse(lastReportedTiles.containsKey(info1.token.asBinder()));
@@ -524,8 +516,7 @@
         lastReportedTiles.clear();
         wct = new WindowContainerTransaction();
         wct.reorder(stack2.mRemoteToken, true /* onTop */);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
-                null /* organizer */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         // Home should now be on top. No change occurs in second tile, so not reported
         assertEquals(1, lastReportedTiles.size());
         assertEquals(ACTIVITY_TYPE_HOME,
@@ -683,9 +674,6 @@
         public void taskVanished(RunningTaskInfo info) {
         }
         @Override
-        public void transactionReady(int id, SurfaceControl.Transaction t) {
-        }
-        @Override
         public void onTaskInfoChanged(RunningTaskInfo info) {
         }
     };
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index cb75c8b..f186ed3 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -17,9 +17,9 @@
 package com.android.test.taskembed;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.window.WindowOrganizer.TaskOrganizer;
 
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.content.Context;
@@ -27,6 +27,8 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
@@ -34,8 +36,10 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.window.ITaskOrganizer;
+import android.window.IWindowContainerTransactionCallback;
 import android.window.WindowContainerTransaction;
 import android.widget.LinearLayout;
+import android.window.WindowOrganizer;
 
 public class TaskOrganizerMultiWindowTest extends Activity {
     class SplitLayout extends LinearLayout implements View.OnTouchListener {
@@ -112,8 +116,7 @@
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             wct.setBounds(mWc, new Rect(0, 0, width, height));
             try {
-                ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
-                        mOrganizer);
+                WindowOrganizer.applySyncTransaction(wct, mOrganizer.mTransactionCallback);
             } catch (Exception e) {
                 // Oh well
             }
@@ -127,6 +130,25 @@
     class Organizer extends ITaskOrganizer.Stub {
         private int receivedTransactions = 0;
         SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
+        IWindowContainerTransactionCallback mTransactionCallback =
+                new IWindowContainerTransactionCallback() {
+            @Override
+            public void transactionReady(int id, SurfaceControl.Transaction t)
+                    throws RemoteException {
+                mergedTransaction.merge(t);
+                receivedTransactions++;
+                if (receivedTransactions == 2) {
+                    mergedTransaction.apply();
+                    receivedTransactions = 0;
+                }
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return null;
+            }
+        };
+
         @Override
         public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
             if (!gotFirstTask) {
@@ -138,14 +160,6 @@
         }
         public void taskVanished(ActivityManager.RunningTaskInfo ti) {
         }
-        public void transactionReady(int id, SurfaceControl.Transaction t) {
-            mergedTransaction.merge(t);
-            receivedTransactions++;
-            if (receivedTransactions == 2) {
-                mergedTransaction.apply();
-                receivedTransactions = 0;
-            }
-        }
         @Override
         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         }
@@ -158,9 +172,7 @@
         super.onCreate(savedInstanceState);
 
         try {
-            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(mOrganizer,
-                    WINDOWING_MODE_MULTI_WINDOW);
-
+            TaskOrganizer.registerOrganizer(mOrganizer, WINDOWING_MODE_MULTI_WINDOW);
         } catch (Exception e) {
         }
 
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index 177841d..a2f40dc 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -17,19 +17,19 @@
 package com.android.test.taskembed;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.window.WindowOrganizer.TaskOrganizer;
 
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.Service;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.IBinder;
-import android.view.SurfaceControl;
 import android.view.ViewGroup;
 import android.window.ITaskOrganizer;
 import android.window.WindowContainerTransaction;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
+import android.window.WindowOrganizer;
 
 public class TaskOrganizerPipTest extends Service {
     static final int PIP_WIDTH  = 640;
@@ -44,14 +44,12 @@
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             wct.scheduleFinishEnterPip(ti.token, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
             try {
-                ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, null);
+                WindowOrganizer.applyTransaction(wct);
             } catch (Exception e) {
             }
         }
         public void taskVanished(ActivityManager.RunningTaskInfo ti) {
         }
-        public void transactionReady(int id, SurfaceControl.Transaction t) {
-        }
         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         }
     }
@@ -68,9 +66,7 @@
         super.onCreate();
 
         try {
-            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(mOrganizer,
-                    WINDOWING_MODE_PINNED);
-
+            TaskOrganizer.registerOrganizer(mOrganizer, WINDOWING_MODE_PINNED);
         } catch (Exception e) {
         }