Fix crash on devices that don't support multiwindow

Only initializes split-screen organizer if device supports it.

Also added some graceful degredation for when organizer binding
fails. Needed to clean-up some things for this though since
now unregistering task-organizers needs to work.

Previously, registering one task-org for multiple windowing
modes worked, but unregistering only cleaned-up one windowing-mode.
So this reworks some of the data-structures to support that
use-case.

Bug: 152401027
Test: Use device that doesn't support split-screen multiwindow.
Change-Id: I7d417721b7b51b20b0c054d9a25f62c443837670
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 01498f9..66e3211 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -25,6 +25,7 @@
 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;
@@ -32,11 +33,11 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.Slog;
-import android.window.IWindowContainer;
 import android.view.LayoutInflater;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
+import android.window.IWindowContainer;
 import android.window.WindowContainerTransaction;
 import android.window.WindowOrganizer;
 
@@ -112,6 +113,9 @@
 
     private DisplayChangeController.OnDisplayChangingListener mRotationController =
             (display, fromRotation, toRotation, t) -> {
+                if (!mSplits.isSplitScreenSupported()) {
+                    return;
+                }
                 DisplayLayout displayLayout =
                         new DisplayLayout(mDisplayController.getDisplayLayout(display));
                 SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
@@ -472,6 +476,10 @@
                 mDisplayController.getDisplayLayout(displayId), mSplits);
         mImeController.addPositionProcessor(mImePositionProcessor);
         mDisplayController.addDisplayChangingController(mRotationController);
+        if (!ActivityTaskManager.supportsSplitScreenMultiWindow(mContext)) {
+            removeDivider();
+            return;
+        }
         try {
             mSplits.init(mSurfaceSession);
             // Set starting tile bounds based on middle target
@@ -481,13 +489,15 @@
             WindowOrganizer.applyTransaction(tct);
         } catch (Exception e) {
             Slog.e(TAG, "Failed to register docked stack listener", e);
+            removeDivider();
+            return;
         }
         update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration());
     }
 
     @Override
     public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
-        if (displayId != DEFAULT_DISPLAY) {
+        if (displayId != DEFAULT_DISPLAY || !mSplits.isSplitScreenSupported()) {
             return;
         }
         mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 1aa7831..1e4c8e4 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -25,7 +25,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -130,7 +129,6 @@
 
     private int mDividerInsets;
     private final Display mDefaultDisplay;
-    private boolean mSupportSplitScreenMultiWindow;
 
     private int mDividerSize;
     private int mTouchElevation;
@@ -284,8 +282,6 @@
         final DisplayManager displayManager =
                 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
         mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
-        mSupportSplitScreenMultiWindow =
-                ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
     }
 
     @Override
@@ -358,11 +354,6 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (!mSupportSplitScreenMultiWindow) {
-            super.onLayout(changed, left, top, right, bottom);
-            return;
-        }
-
         if (mFirstLayout) {
             // Wait for first layout so that the ViewRootImpl surface has been created.
             initializeSurfaceState();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 6cb7f4f..0a528a6 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -49,6 +49,7 @@
     ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>();
     Rect mHomeBounds = new Rect();
     final Divider mDivider;
+    private boolean mSplitScreenSupported = false;
 
     SplitScreenTaskOrganizer(Divider divider) {
         mDivider = divider;
@@ -57,12 +58,19 @@
     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 = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
-                WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        mPrimarySurface = mPrimary.token.getLeash();
-        mSecondarySurface = mSecondary.token.getLeash();
+        try {
+            mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
+                    WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+            mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
+                    WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+            mPrimarySurface = mPrimary.token.getLeash();
+            mSecondarySurface = mSecondary.token.getLeash();
+        } catch (RemoteException e) {
+            // teardown to prevent callbacks
+            TaskOrganizer.unregisterOrganizer(this);
+            throw e;
+        }
+        mSplitScreenSupported = true;
 
         // Initialize dim surfaces:
         mPrimaryDim = new SurfaceControl.Builder(session).setParent(mPrimarySurface)
@@ -78,6 +86,10 @@
         releaseTransaction(t);
     }
 
+    boolean isSplitScreenSupported() {
+        return mSplitScreenSupported;
+    }
+
     SurfaceControl.Transaction getTransaction() {
         return mDivider.mTransactionPool.acquire();
     }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4382e9d..8a896f5 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -20,14 +20,13 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-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.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.Intent;
 import android.content.pm.ActivityInfo;
 import android.os.Binder;
@@ -53,7 +52,7 @@
  */
 class TaskOrganizerController extends ITaskOrganizerController.Stub {
     private static final String TAG = "TaskOrganizerController";
-    private static final LinkedList<TaskOrganizerState> EMPTY_LIST = new LinkedList<>();
+    private static final LinkedList<IBinder> EMPTY_LIST = new LinkedList<>();
 
     /**
      * Masks specifying which configurations are important to report back to an organizer when
@@ -65,12 +64,10 @@
     private final WindowManagerGlobalLock mGlobalLock;
 
     private class DeathRecipient implements IBinder.DeathRecipient {
-        int mWindowingMode;
         ITaskOrganizer mTaskOrganizer;
 
-        DeathRecipient(ITaskOrganizer organizer, int windowingMode) {
+        DeathRecipient(ITaskOrganizer organizer) {
             mTaskOrganizer = organizer;
-            mWindowingMode = windowingMode;
         }
 
         @Override
@@ -86,18 +83,16 @@
     private class TaskOrganizerState {
         private final ITaskOrganizer mOrganizer;
         private final DeathRecipient mDeathRecipient;
-        private final int mWindowingMode;
         private final ArrayList<Task> mOrganizedTasks = new ArrayList<>();
 
-        TaskOrganizerState(ITaskOrganizer organizer, int windowingMode) {
+        TaskOrganizerState(ITaskOrganizer organizer) {
             mOrganizer = organizer;
-            mDeathRecipient = new DeathRecipient(organizer, windowingMode);
+            mDeathRecipient = new DeathRecipient(organizer);
             try {
                 organizer.asBinder().linkToDeath(mDeathRecipient, 0);
             } catch (RemoteException e) {
                 Slog.e(TAG, "TaskOrganizer failed to register death recipient");
             }
-            mWindowingMode = windowingMode;
         }
 
         void addTask(Task t) {
@@ -120,7 +115,9 @@
 
         void dispose() {
             releaseTasks();
-            mTaskOrganizersForWindowingMode.get(mWindowingMode).remove(this);
+            for (int i = mTaskOrganizersForWindowingMode.size() - 1; i >= 0; --i) {
+                mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.asBinder());
+            }
         }
 
         private void releaseTasks() {
@@ -136,7 +133,7 @@
         }
     }
 
-    private final SparseArray<LinkedList<TaskOrganizerState>> mTaskOrganizersForWindowingMode =
+    private final SparseArray<LinkedList<IBinder>> mTaskOrganizersForWindowingMode =
             new SparseArray<>();
     private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
     private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
@@ -162,10 +159,22 @@
      */
     @Override
     public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
-        if (windowingMode != WINDOWING_MODE_PINNED
-                && windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                && windowingMode != WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                && windowingMode != WINDOWING_MODE_MULTI_WINDOW) {
+        if (windowingMode == WINDOWING_MODE_PINNED) {
+            if (!mService.mSupportsPictureInPicture) {
+                throw new UnsupportedOperationException("Picture in picture is not supported on "
+                        + "this device");
+            }
+        } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+            if (!mService.mSupportsSplitScreenMultiWindow) {
+                throw new UnsupportedOperationException("Split-screen is not supported on this "
+                        + "device");
+            }
+        } else if (windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+            if (!mService.mSupportsMultiWindow) {
+                throw new UnsupportedOperationException("Multi-window is not supported on this "
+                        + "device");
+            }
+        } else {
             throw new UnsupportedOperationException("As of now only Pinned/Split/Multiwindow"
                     + " windowing modes are supported for registerTaskOrganizer");
         }
@@ -178,19 +187,18 @@
                             + windowingMode);
                 }
 
-                LinkedList<TaskOrganizerState> states;
-                if (mTaskOrganizersForWindowingMode.contains(windowingMode)) {
-                    states = mTaskOrganizersForWindowingMode.get(windowingMode);
-                } else {
-                    states = new LinkedList<>();
-                    mTaskOrganizersForWindowingMode.put(windowingMode, states);
+                LinkedList<IBinder> orgs = mTaskOrganizersForWindowingMode.get(windowingMode);
+                if (orgs == null) {
+                    orgs = new LinkedList<>();
+                    mTaskOrganizersForWindowingMode.put(windowingMode, orgs);
                 }
-                final TaskOrganizerState previousState = states.peekLast();
-                final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode);
-                states.add(state);
-                mTaskOrganizerStates.put(organizer.asBinder(), state);
+                orgs.add(organizer.asBinder());
+                if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
+                    mTaskOrganizerStates.put(organizer.asBinder(),
+                            new TaskOrganizerState(organizer));
+                }
 
-                if (previousState == null) {
+                if (orgs.size() == 1) {
                     // Only in the case where this is the root task organizer for the given
                     // windowing mode, we add report all existing tasks in that mode to the new
                     // task organizer.
@@ -214,8 +222,12 @@
     }
 
     ITaskOrganizer getTaskOrganizer(int windowingMode) {
-        final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode,
-                EMPTY_LIST).peekLast();
+        final IBinder organizer =
+                mTaskOrganizersForWindowingMode.get(windowingMode, EMPTY_LIST).peekLast();
+        if (organizer == null) {
+            return null;
+        }
+        final TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
         if (state == null) {
             return null;
         }