Re-parent children when Organized Window is no longer controlled

When an Organized Window is no longer controlled by a specific client,
we create a new SurfaceControl and reparent the children so the client
no longer has control over the WindowContainer

Test: Pip, TaskOrganizerMultiWindowTest
Bug: 154558563
Change-Id: I76e0da115fd48f982a82bd6e2351e7171934acfc
This commit is contained in:
chaviw
2020-04-24 11:25:08 -07:00
parent 12a32f35d7
commit da7b3c286b
8 changed files with 172 additions and 54 deletions

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -140,8 +140,12 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
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<T extends WindowContainer> extends WindowContainer<T> {
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

View File

@@ -2004,6 +2004,14 @@ class Task extends WindowContainer<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<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<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<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();

View File

@@ -211,6 +211,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
void removeTask(Task t) {
if (t.mTaskAppearedSent) {
t.migrateToNewSurfaceControl();
t.mTaskAppearedSent = false;
mOrganizer.onTaskVanished(t);
}

View File

@@ -405,6 +405,40 @@ class WindowContainer<E extends 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.
*/

View File

@@ -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);
}

View File

@@ -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();
}
}