diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index f36f8c1318486..ad2b22fb6d9b2 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -36,7 +36,6 @@ import android.provider.Settings; import android.util.Slog; import android.view.LayoutInflater; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.View; import android.window.TaskOrganizer; import android.window.WindowContainerToken; @@ -94,7 +93,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, private boolean mHomeStackResizable = false; private ForcedResizableInfoActivityController mForcedResizableController; private SystemWindows mSystemWindows; - final SurfaceSession mSurfaceSession = new SurfaceSession(); private DisplayController mDisplayController; private DisplayImeController mImeController; final TransactionPool mTransactionPool; @@ -493,7 +491,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, return; } try { - mSplits.init(mSurfaceSession); + mSplits.init(); // Set starting tile bounds based on middle target final WindowContainerTransaction tct = new WindowContainerTransaction(); int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; @@ -505,7 +503,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, return; } ActivityManagerWrapper.getInstance().registerTaskStackListener(mActivityRestartListener); - update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration()); } @Override @@ -582,6 +579,15 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } } + void onTaskVanished() { + mHandler.post(this::removeDivider); + } + + void onTasksReady() { + mHandler.post(() -> update(mDisplayController.getDisplayContext( + mContext.getDisplayId()).getResources().getConfiguration())); + } + void updateVisibility(final boolean visible) { if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible); if (mVisible != visible) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java index f5d6cb6fd4b0a..fd6722ad7d4e1 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java @@ -47,39 +47,27 @@ class SplitScreenTaskOrganizer extends TaskOrganizer { final Divider mDivider; private boolean mSplitScreenSupported = false; + final SurfaceSession mSurfaceSession = new SurfaceSession(); + SplitScreenTaskOrganizer(Divider divider) { mDivider = divider; } - void init(SurfaceSession session) throws RemoteException { + void init() throws RemoteException { registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - 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 (Exception e) { - // teardown to prevent callbacks - unregisterOrganizer(); - throw e; + synchronized (this) { + 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); + } catch (Exception e) { + // teardown to prevent callbacks + unregisterOrganizer(); + throw e; + } } - mSplitScreenSupported = true; - - // Initialize dim surfaces: - mPrimaryDim = new SurfaceControl.Builder(session).setParent(mPrimarySurface) - .setColorLayer().setName("Primary Divider Dim").build(); - mSecondaryDim = new SurfaceControl.Builder(session).setParent(mSecondarySurface) - .setColorLayer().setName("Secondary Divider Dim").build(); - SurfaceControl.Transaction t = getTransaction(); - t.setLayer(mPrimaryDim, Integer.MAX_VALUE); - t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f}); - t.setLayer(mSecondaryDim, Integer.MAX_VALUE); - t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f}); - t.apply(); - releaseTransaction(t); } boolean isSplitScreenSupported() { @@ -94,6 +82,67 @@ class SplitScreenTaskOrganizer extends TaskOrganizer { mDivider.mTransactionPool.release(t); } + @Override + public void onTaskAppeared(RunningTaskInfo taskInfo) { + synchronized (this) { + if (mPrimary == null || mSecondary == null) { + Log.w(TAG, "Received onTaskAppeared before creating root tasks " + taskInfo); + return; + } + + if (taskInfo.token.equals(mPrimary.token)) { + mPrimarySurface = taskInfo.token.getLeash(); + } else if (taskInfo.token.equals(mSecondary.token)) { + mSecondarySurface = taskInfo.token.getLeash(); + } + + if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) { + mSplitScreenSupported = true; + + // Initialize dim surfaces: + mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession) + .setParent(mPrimarySurface).setColorLayer() + .setName("Primary Divider Dim").build(); + mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession) + .setParent(mSecondarySurface).setColorLayer() + .setName("Secondary Divider Dim").build(); + SurfaceControl.Transaction t = getTransaction(); + t.setLayer(mPrimaryDim, Integer.MAX_VALUE); + t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f}); + t.setLayer(mSecondaryDim, Integer.MAX_VALUE); + t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f}); + t.apply(); + releaseTransaction(t); + + mDivider.onTasksReady(); + } + } + } + + @Override + public void onTaskVanished(RunningTaskInfo taskInfo) { + synchronized (this) { + final boolean isPrimaryTask = mPrimary != null + && taskInfo.token.equals(mPrimary.token); + final boolean isSecondaryTask = mSecondary != null + && taskInfo.token.equals(mSecondary.token); + + if (mSplitScreenSupported && (isPrimaryTask || isSecondaryTask)) { + mSplitScreenSupported = false; + + SurfaceControl.Transaction t = getTransaction(); + t.remove(mPrimaryDim); + t.remove(mSecondaryDim); + t.remove(mPrimarySurface); + t.remove(mSecondarySurface); + t.apply(); + releaseTransaction(t); + + mDivider.onTaskVanished(); + } + } + } + @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { if (taskInfo.displayId != DEFAULT_DISPLAY) { diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 9b34bd17d0426..4ecf348ce85da 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -140,8 +140,12 @@ public class DisplayArea extends WindowContainer { void setOrganizer(IDisplayAreaOrganizer organizer) { if (mOrganizer == organizer) return; - sendDisplayAreaVanished(); + IDisplayAreaOrganizer lastOrganizer = mOrganizer; + // Update the new display area organizer before calling sendDisplayAreaVanished since it + // could result in a new SurfaceControl getting created that would notify the old organizer + // about it. mOrganizer = organizer; + sendDisplayAreaVanished(lastOrganizer); sendDisplayAreaAppeared(); } @@ -150,9 +154,10 @@ public class DisplayArea extends WindowContainer { mOrganizerController.onDisplayAreaAppeared(mOrganizer, this); } - void sendDisplayAreaVanished() { - if (mOrganizer == null) return; - mOrganizerController.onDisplayAreaVanished(mOrganizer, this); + void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) { + if (organizer == null) return; + migrateToNewSurfaceControl(); + mOrganizerController.onDisplayAreaVanished(organizer, this); } @Override diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 5a27f47b28aa8..b874f1cbb2254 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2004,6 +2004,14 @@ class Task extends WindowContainer { return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM); } + @Override + void migrateToNewSurfaceControl() { + super.migrateToNewSurfaceControl(); + mLastSurfaceSize.x = 0; + mLastSurfaceSize.y = 0; + updateSurfaceSize(getPendingTransaction()); + } + void updateSurfaceSize(SurfaceControl.Transaction transaction) { if (mSurfaceControl == null || mCreatedByOrganizer) { return; @@ -4400,16 +4408,25 @@ class Task extends WindowContainer { return mHasBeenVisible; } - /** In the case that these three conditions are true, we want to send the Task to - * the organizer: - * 1. We have a SurfaceControl - * 2. An organizer has been set - * 3. We have finished drawing + /** In the case that these conditions are true, we want to send the Task to the organizer: + * 1. An organizer has been set + * 2. The Task was created by the organizer + * or + * 2a. We have a SurfaceControl + * 2b. We have finished drawing * Any time any of these conditions are updated, the updating code should call * sendTaskAppeared. */ boolean taskAppearedReady() { - return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible(); + if (mTaskOrganizer == null) { + return false; + } + + if (mCreatedByOrganizer) { + return true; + } + + return mSurfaceControl != null && getHasBeenVisible(); } private void sendTaskAppeared() { @@ -4418,9 +4435,9 @@ class Task extends WindowContainer { } } - private void sendTaskVanished() { - if (mTaskOrganizer != null) { - mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this); + private void sendTaskVanished(ITaskOrganizer organizer) { + if (organizer != null) { + mAtmService.mTaskOrganizerController.onTaskVanished(organizer, this); } } @@ -4429,9 +4446,13 @@ class Task extends WindowContainer { if (mTaskOrganizer == organizer) { return false; } - // Let the old organizer know it has lost control. - sendTaskVanished(); + + ITaskOrganizer previousOrganizer = mTaskOrganizer; + // Update the new task organizer before calling sendTaskVanished since it could result in + // a new SurfaceControl getting created that would notify the old organizer about it. mTaskOrganizer = organizer; + // Let the old organizer know it has lost control. + sendTaskVanished(previousOrganizer); if (mTaskOrganizer != null) { sendTaskAppeared(); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 306c100e651c9..9d229df1967ea 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -211,6 +211,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void removeTask(Task t) { if (t.mTaskAppearedSent) { + t.migrateToNewSurfaceControl(); t.mTaskAppearedSent = false; mOrganizer.onTaskVanished(t); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 07cf4e5c1e80a..1155bb8520ae4 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -405,6 +405,40 @@ class WindowContainer extends ConfigurationContainer< updateSurfacePosition(); } + /** + * Create a new SurfaceControl for this WindowContainer and migrate all properties to the new + * SurfaceControl. Properties include: + * 1. Children + * 2. Position + * 3. Z order + * + * Remove the old SurfaceControl since it's no longer needed. + * + * This is used to revoke control of the SurfaceControl from a client process that was + * previously organizing this WindowContainer. + */ + void migrateToNewSurfaceControl() { + SurfaceControl.Transaction t = getPendingTransaction(); + t.remove(mSurfaceControl); + // Clear the last position so the new SurfaceControl will get correct position + mLastSurfacePosition.set(0, 0); + + createSurfaceControl(false /* force */); + if (mLastRelativeToLayer != null) { + t.setRelativeLayer(mSurfaceControl, mLastRelativeToLayer, mLastLayer); + } else { + t.setLayer(mSurfaceControl, mLastLayer); + } + + for (int i = 0; i < mChildren.size(); i++) { + SurfaceControl sc = mChildren.get(i).getSurfaceControl(); + if (sc != null) { + t.reparent(sc, mSurfaceControl); + } + } + scheduleAnimation(); + } + /** * Called when the surface is shown for the first time. */ diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java index 86c3fa0fe0346..da4afec8a172c 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java @@ -18,29 +18,24 @@ package com.android.test.taskembed; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; -import android.app.ActivityManager; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; 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; import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; -import android.window.ITaskOrganizer; -import android.window.IWindowContainerTransactionCallback; +import android.widget.LinearLayout; import android.window.TaskOrganizer; import android.window.WindowContainerTransaction; -import android.widget.LinearLayout; import android.window.WindowContainerTransactionCallback; -import android.window.WindowOrganizer; public class TaskOrganizerMultiWindowTest extends Activity { class SplitLayout extends LinearLayout implements View.OnTouchListener { @@ -173,6 +168,12 @@ public class TaskOrganizerMultiWindowTest extends Activity { setContentView(splitView); } + @Override + protected void onDestroy() { + super.onDestroy(); + mOrganizer.unregisterOrganizer(); + } + private void addFlags(Intent intent) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); } diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java index 2a1aa2e1de652..0a7a6d56249f7 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java @@ -24,10 +24,10 @@ import android.content.Intent; import android.graphics.Rect; import android.os.IBinder; import android.view.ViewGroup; -import android.window.TaskOrganizer; -import android.window.WindowContainerTransaction; import android.view.WindowManager; import android.widget.FrameLayout; +import android.window.TaskOrganizer; +import android.window.WindowContainerTransaction; public class TaskOrganizerPipTest extends Service { static final int PIP_WIDTH = 640; @@ -76,5 +76,6 @@ public class TaskOrganizerPipTest extends Service { @Override public void onDestroy() { super.onDestroy(); + mOrganizer.unregisterOrganizer(); } }