From 253765144915806217926293c11f56259fcdeccc Mon Sep 17 00:00:00 2001 From: Rob Carr Date: Mon, 9 Mar 2020 15:23:19 -0700 Subject: [PATCH] BLASTSyncEngine: Child Window Support Add support for syncing WindowState with their children. Recall that the BLASTSyncEngines work with a 2 way latch: 1. setReady must be invoked 2. All the children must invoke transaction ready So we use a setup where we set-up a sync state from WindowState including all our children, and then mark it as ready from our own finishDrawing in order to implement a 2 way latch between us an our children. Bug: 148871522 Test: TaskOrganizerTests Change-Id: I14b3160586d06606ac1366b086b8daeba97c8cc2 --- .../android/server/wm/WindowContainer.java | 21 ++++++++---- .../com/android/server/wm/WindowState.java | 31 ++++++++++++++--- .../android/server/wm/TaskOrganizerTests.java | 33 +++++++++++++++++++ 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index ec90097e75e9b..9a9283257b032 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2461,6 +2461,19 @@ class WindowContainer extends ConfigurationContainer< mWaitingSyncId = -1; } + /** + * Returns true if any of the children elected to participate in the Sync + */ + boolean addChildrenToSyncSet(int localId) { + boolean willSync = false; + + for (int i = 0; i < mChildren.size(); i++) { + final WindowContainer child = mChildren.get(i); + willSync |= mBLASTSyncEngine.addToSyncSet(localId, child); + } + return willSync; + } + boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener, int waitingId) { boolean willSync = false; @@ -2469,16 +2482,12 @@ class WindowContainer extends ConfigurationContainer< } mUsingBLASTSyncTransaction = true; - int localId = mBLASTSyncEngine.startSyncSet(this); - for (int i = 0; i < mChildren.size(); i++) { - final WindowContainer child = mChildren.get(i); - willSync = mBLASTSyncEngine.addToSyncSet(localId, child) | willSync; - } - // Make sure to set these before we call setReady in case the sync was a no-op mWaitingSyncId = waitingId; mWaitingListener = waitingListener; + int localId = mBLASTSyncEngine.startSyncSet(this); + willSync |= addChildrenToSyncSet(localId); mBLASTSyncEngine.setReady(localId); return willSync; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c44be4d00a84a..a69d259074431 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -679,6 +679,15 @@ class WindowState extends WindowContainer implements WindowManagerP */ int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET; + /** + * BLASTSyncEngine ID corresponding to a sync-set for all + * our children. We add our children to this set in Sync, + * but we save it and don't mark it as ready until finishDrawing + * this way we have a two way latch between all our children finishing + * and drawing ourselves. + */ + private int mLocalSyncId = -1; + static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */ /** @@ -5684,11 +5693,13 @@ class WindowState extends WindowContainer implements WindowManagerP @Override boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener, int waitingId) { - // TODO(b/148871522): Support child window mWaitingListener = waitingListener; mWaitingSyncId = waitingId; mUsingBLASTSyncTransaction = true; + mLocalSyncId = mBLASTSyncEngine.startSyncSet(this); + addChildrenToSyncSet(mLocalSyncId); + mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this); mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this, BLAST_TIMEOUT_DURATION); @@ -5702,11 +5713,21 @@ class WindowState extends WindowContainer implements WindowManagerP } mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this); - if (postDrawTransaction == null) { - postDrawTransaction = new SurfaceControl.Transaction(); + if (postDrawTransaction != null) { + mBLASTSyncTransaction.merge(postDrawTransaction); } - postDrawTransaction.merge(mBLASTSyncTransaction); - mWaitingListener.transactionReady(mWaitingSyncId, postDrawTransaction); + + // If localSyncId is >0 then we are syncing with children and will + // invoke transaction ready from our own #transactionReady callback + // we just need to signal our side of the sync (setReady). But if we + // have no sync operation at this level transactionReady will never + // be invoked and we need to invoke it ourself. + if (mLocalSyncId >= 0) { + mBLASTSyncEngine.setReady(mLocalSyncId); + } else { + mWaitingListener.transactionReady(mWaitingSyncId, mBLASTSyncTransaction); + } + mUsingBLASTSyncTransaction = false; mWaitingSyncId = 0; 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 19ed7a9101de9..5007e3afd94f9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -32,6 +32,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -567,6 +568,38 @@ public class TaskOrganizerTests extends WindowTestsBase { .transactionReady(anyInt(), any()); } + @Test + public void testBLASTCallbackWithChildWindow() { + final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ITaskOrganizer organizer = registerMockOrganizer(); + final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); + final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window"); + + w.mActivityRecord.setVisible(true); + makeWindowVisible(w, child); + + BLASTSyncEngine bse = new BLASTSyncEngine(); + + BLASTSyncEngine.TransactionReadyListener transactionListener = + mock(BLASTSyncEngine.TransactionReadyListener.class); + + int id = bse.startSyncSet(transactionListener); + assertEquals(true, bse.addToSyncSet(id, task)); + bse.setReady(id); + w.finishDrawing(null); + + // Since we have a child window we still shouldn't be done. + verify(transactionListener, never()) + .transactionReady(anyInt(), any()); + reset(transactionListener); + + child.finishDrawing(null); + // Ah finally! Done + verify(transactionListener) + .transactionReady(anyInt(), any()); + } + class StubOrganizer extends ITaskOrganizer.Stub { RunningTaskInfo mInfo;