Loading services/core/java/com/android/server/wm/BLASTSyncEngine.java +18 −15 Original line number Diff line number Diff line Loading @@ -16,17 +16,19 @@ package com.android.server.wm; import android.view.SurfaceControl; import android.util.ArrayMap; import android.util.ArraySet; import java.util.HashMap; import java.util.Set; /** * Utility class for collecting and merging transactions from various sources asynchronously. * Utility class for collecting WindowContainers that will merge transactions. * For example to use to synchronously resize all the children of a window container * 1. Open a new sync set, and pass the listener that will be invoked * int id startSyncSet(TransactionReadyListener) * the returned ID will be eventually passed to the TransactionReadyListener in combination * with the prepared transaction. You also use it to refer to the operation in future steps. * with a set of WindowContainers that are ready, meaning onTransactionReady was called for * those WindowContainers. You also use it to refer to the operation in future steps. * 2. Ask each child to participate: * addToSyncSet(int id, WindowContainer wc) * if the child thinks it will be affected by a configuration change (a.k.a. has a visible Loading @@ -38,35 +40,37 @@ import java.util.HashMap; * setReady(int id) * 5. If there were no sub windows anywhere in the hierarchy to wait on, then * transactionReady is immediately invoked, otherwise all the windows are poked * to redraw and to deliver a buffer to WMS#finishDrawing. * Once all this drawing is complete the combined transaction of all the buffers * and pending transaction hierarchy changes will be delivered to the TransactionReadyListener * to redraw and to deliver a buffer to {@link WindowState#finishDrawing}. * Once all this drawing is complete the WindowContainer that's ready will be added to the * set of ready WindowContainers. When the final onTransactionReady is called, it will merge * the transactions of the all the WindowContainers and will be delivered to the * TransactionReadyListener */ class BLASTSyncEngine { private static final String TAG = "BLASTSyncEngine"; interface TransactionReadyListener { void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction); void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady); }; // Holds state associated with a single synchronous set of operations. class SyncState implements TransactionReadyListener { int mSyncId; SurfaceControl.Transaction mMergedTransaction; int mRemainingTransactions; TransactionReadyListener mListener; boolean mReady = false; Set<WindowContainer> mWindowContainersReady = new ArraySet<>(); private void tryFinish() { if (mRemainingTransactions == 0 && mReady) { mListener.onTransactionReady(mSyncId, mMergedTransaction); mListener.onTransactionReady(mSyncId, mWindowContainersReady); mPendingSyncs.remove(mSyncId); } } public void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { mRemainingTransactions--; mMergedTransaction.merge(mergedTransaction); mWindowContainersReady.addAll(windowContainersReady); tryFinish(); } Loading @@ -86,14 +90,13 @@ class BLASTSyncEngine { SyncState(TransactionReadyListener l, int id) { mListener = l; mSyncId = id; mMergedTransaction = new SurfaceControl.Transaction(); mRemainingTransactions = 0; } }; int mNextSyncId = 0; private int mNextSyncId = 0; final HashMap<Integer, SyncState> mPendingSyncs = new HashMap(); private final ArrayMap<Integer, SyncState> mPendingSyncs = new ArrayMap<>(); BLASTSyncEngine() { } Loading services/core/java/com/android/server/wm/WindowContainer.java +12 −4 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; Loading Loading @@ -2685,11 +2686,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } @Override public void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { mergedTransaction.merge(mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { if (mWaitingListener == null) { return; } mWaitingListener.onTransactionReady(mWaitingSyncId, mergedTransaction); windowContainersReady.add(this); mWaitingListener.onTransactionReady(mWaitingSyncId, windowContainersReady); mWaitingListener = null; mWaitingSyncId = -1; Loading Loading @@ -2740,4 +2743,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< boolean useBLASTSync() { return mUsingBLASTSyncTransaction; } void mergeBlastSyncTransaction(Transaction t) { t.merge(mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; } } services/core/java/com/android/server/wm/WindowOrganizerController.java +17 −3 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.window.IWindowOrganizerController; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; Loading @@ -51,6 +52,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Server side implementation for the interface for organizing windows Loading Loading @@ -142,7 +144,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // operations so we don't end up splitting effects between the WM // pending transaction and the BLASTSync transaction. if (syncId >= 0) { mBLASTSyncEngine.addToSyncSet(syncId, wc); addToSyncSet(syncId, wc); } int containerEffect = applyWindowContainerChange(wc, entry.getValue()); Loading @@ -164,7 +166,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub continue; } if (syncId >= 0) { mBLASTSyncEngine.addToSyncSet(syncId, wc); addToSyncSet(syncId, wc); } effects |= sanitizeAndApplyHierarchyOp(wc, hop); } Loading Loading @@ -396,21 +398,33 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return mDisplayAreaOrganizerController; } @VisibleForTesting int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) { int id = mBLASTSyncEngine.startSyncSet(this); mTransactionCallbacksByPendingSyncId.put(id, callback); return id; } @VisibleForTesting void setSyncReady(int id) { mBLASTSyncEngine.setReady(id); } @VisibleForTesting void addToSyncSet(int syncId, WindowContainer wc) { mBLASTSyncEngine.addToSyncSet(syncId, wc); } @Override public void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { final IWindowContainerTransactionCallback callback = mTransactionCallbacksByPendingSyncId.get(mSyncId); SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction(); for (WindowContainer container : windowContainersReady) { container.mergeBlastSyncTransaction(mergedTransaction); } try { callback.onTransactionReady(mSyncId, mergedTransaction); } catch (RemoteException e) { Loading services/core/java/com/android/server/wm/WindowState.java +26 −4 Original line number Diff line number Diff line Loading @@ -242,8 +242,10 @@ import com.android.server.wm.utils.WmDisplayCutout; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.function.Predicate; /** A window in the window manager. */ Loading Loading @@ -655,6 +657,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private static final StringBuilder sTmpSB = new StringBuilder(); /** * Whether the next surfacePlacement call should notify that the blast sync is ready. * This is set to true when {@link #finishDrawing(Transaction)} is called so * {@link #onTransactionReady(int, Set)} is called after the next surfacePlacement. This allows * Transactions to get flushed into the syncTransaction before notifying {@link BLASTSyncEngine} * that this WindowState is ready. */ private boolean mNotifyBlastOnSurfacePlacement; /** * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms * of z-order and 1 otherwise. Loading Loading @@ -5346,6 +5357,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateFrameRateSelectionPriorityIfNeeded(); mWinAnimator.prepareSurfaceLocked(true); notifyBlastSyncTransaction(); super.prepareSurfaces(); } Loading Loading @@ -5837,6 +5849,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mBLASTSyncTransaction.merge(postDrawTransaction); } mNotifyBlastOnSurfacePlacement = true; return mWinAnimator.finishDrawingLocked(null); } @VisibleForTesting void notifyBlastSyncTransaction() { if (!mNotifyBlastOnSurfacePlacement || mWaitingListener == null) { mNotifyBlastOnSurfacePlacement = false; return; } // 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 Loading @@ -5844,15 +5867,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // be invoked and we need to invoke it ourself. if (mLocalSyncId >= 0) { mBLASTSyncEngine.setReady(mLocalSyncId); return mWinAnimator.finishDrawingLocked(null); return; } mWaitingListener.onTransactionReady(mWaitingSyncId, mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; mWaitingListener.onTransactionReady(mWaitingSyncId, Collections.singleton(this)); mWaitingSyncId = 0; mWaitingListener = null; return mWinAnimator.finishDrawingLocked(null); mNotifyBlastOnSurfacePlacement = false; } private boolean requestResizeForBlastSync() { Loading services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +43 −4 Original line number Diff line number Diff line Loading @@ -67,6 +67,7 @@ import android.util.Rational; import android.view.Display; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.IWindowContainerTransactionCallback; import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; Loading Loading @@ -728,7 +729,7 @@ public class WindowOrganizerTests extends WindowTestsBase { // We should be rejected from the second sync since we are already // in one. assertEquals(false, bse.addToSyncSet(id2, task)); w.finishDrawing(null); finishAndNotifyDrawing(w); assertEquals(true, bse.addToSyncSet(id2, task)); bse.setReady(id2); } Loading @@ -752,7 +753,7 @@ public class WindowOrganizerTests extends WindowTestsBase { // Since we have a window we have to wait for it to draw to finish sync. verify(transactionListener, never()) .onTransactionReady(anyInt(), any()); w.finishDrawing(null); finishAndNotifyDrawing(w); verify(transactionListener) .onTransactionReady(anyInt(), any()); } Loading Loading @@ -820,14 +821,14 @@ public class WindowOrganizerTests extends WindowTestsBase { int id = bse.startSyncSet(transactionListener); assertEquals(true, bse.addToSyncSet(id, task)); bse.setReady(id); w.finishDrawing(null); finishAndNotifyDrawing(w); // Since we have a child window we still shouldn't be done. verify(transactionListener, never()) .onTransactionReady(anyInt(), any()); reset(transactionListener); child.finishDrawing(null); finishAndNotifyDrawing(child); // Ah finally! Done verify(transactionListener) .onTransactionReady(anyInt(), any()); Loading Loading @@ -979,4 +980,42 @@ public class WindowOrganizerTests extends WindowTestsBase { new IRequestFinishCallback.Default()); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } @Test public void testBLASTCallbackWithMultipleWindows() throws Exception { final ActivityStack stackController = createStack(); final Task task = createTask(stackController); final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1"); final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2"); makeWindowVisible(w1); makeWindowVisible(w2); IWindowContainerTransactionCallback mockCallback = mock(IWindowContainerTransactionCallback.class); int id = mWm.mAtmService.mWindowOrganizerController.startSyncWithOrganizer(mockCallback); mWm.mAtmService.mWindowOrganizerController.addToSyncSet(id, task); mWm.mAtmService.mWindowOrganizerController.setSyncReady(id); // Since we have a window we have to wait for it to draw to finish sync. verify(mockCallback, never()).onTransactionReady(anyInt(), any()); assertTrue(w1.useBLASTSync()); assertTrue(w2.useBLASTSync()); finishAndNotifyDrawing(w1); // Even though one Window finished drawing, both windows should still be using blast sync assertTrue(w1.useBLASTSync()); assertTrue(w2.useBLASTSync()); finishAndNotifyDrawing(w2); verify(mockCallback).onTransactionReady(anyInt(), any()); assertFalse(w1.useBLASTSync()); assertFalse(w2.useBLASTSync()); } private void finishAndNotifyDrawing(WindowState ws) { ws.finishDrawing(null); ws.notifyBlastSyncTransaction(); } } Loading
services/core/java/com/android/server/wm/BLASTSyncEngine.java +18 −15 Original line number Diff line number Diff line Loading @@ -16,17 +16,19 @@ package com.android.server.wm; import android.view.SurfaceControl; import android.util.ArrayMap; import android.util.ArraySet; import java.util.HashMap; import java.util.Set; /** * Utility class for collecting and merging transactions from various sources asynchronously. * Utility class for collecting WindowContainers that will merge transactions. * For example to use to synchronously resize all the children of a window container * 1. Open a new sync set, and pass the listener that will be invoked * int id startSyncSet(TransactionReadyListener) * the returned ID will be eventually passed to the TransactionReadyListener in combination * with the prepared transaction. You also use it to refer to the operation in future steps. * with a set of WindowContainers that are ready, meaning onTransactionReady was called for * those WindowContainers. You also use it to refer to the operation in future steps. * 2. Ask each child to participate: * addToSyncSet(int id, WindowContainer wc) * if the child thinks it will be affected by a configuration change (a.k.a. has a visible Loading @@ -38,35 +40,37 @@ import java.util.HashMap; * setReady(int id) * 5. If there were no sub windows anywhere in the hierarchy to wait on, then * transactionReady is immediately invoked, otherwise all the windows are poked * to redraw and to deliver a buffer to WMS#finishDrawing. * Once all this drawing is complete the combined transaction of all the buffers * and pending transaction hierarchy changes will be delivered to the TransactionReadyListener * to redraw and to deliver a buffer to {@link WindowState#finishDrawing}. * Once all this drawing is complete the WindowContainer that's ready will be added to the * set of ready WindowContainers. When the final onTransactionReady is called, it will merge * the transactions of the all the WindowContainers and will be delivered to the * TransactionReadyListener */ class BLASTSyncEngine { private static final String TAG = "BLASTSyncEngine"; interface TransactionReadyListener { void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction); void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady); }; // Holds state associated with a single synchronous set of operations. class SyncState implements TransactionReadyListener { int mSyncId; SurfaceControl.Transaction mMergedTransaction; int mRemainingTransactions; TransactionReadyListener mListener; boolean mReady = false; Set<WindowContainer> mWindowContainersReady = new ArraySet<>(); private void tryFinish() { if (mRemainingTransactions == 0 && mReady) { mListener.onTransactionReady(mSyncId, mMergedTransaction); mListener.onTransactionReady(mSyncId, mWindowContainersReady); mPendingSyncs.remove(mSyncId); } } public void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { mRemainingTransactions--; mMergedTransaction.merge(mergedTransaction); mWindowContainersReady.addAll(windowContainersReady); tryFinish(); } Loading @@ -86,14 +90,13 @@ class BLASTSyncEngine { SyncState(TransactionReadyListener l, int id) { mListener = l; mSyncId = id; mMergedTransaction = new SurfaceControl.Transaction(); mRemainingTransactions = 0; } }; int mNextSyncId = 0; private int mNextSyncId = 0; final HashMap<Integer, SyncState> mPendingSyncs = new HashMap(); private final ArrayMap<Integer, SyncState> mPendingSyncs = new ArrayMap<>(); BLASTSyncEngine() { } Loading
services/core/java/com/android/server/wm/WindowContainer.java +12 −4 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; Loading Loading @@ -2685,11 +2686,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } @Override public void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { mergedTransaction.merge(mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { if (mWaitingListener == null) { return; } mWaitingListener.onTransactionReady(mWaitingSyncId, mergedTransaction); windowContainersReady.add(this); mWaitingListener.onTransactionReady(mWaitingSyncId, windowContainersReady); mWaitingListener = null; mWaitingSyncId = -1; Loading Loading @@ -2740,4 +2743,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< boolean useBLASTSync() { return mUsingBLASTSyncTransaction; } void mergeBlastSyncTransaction(Transaction t) { t.merge(mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; } }
services/core/java/com/android/server/wm/WindowOrganizerController.java +17 −3 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.window.IWindowOrganizerController; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; Loading @@ -51,6 +52,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Server side implementation for the interface for organizing windows Loading Loading @@ -142,7 +144,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // operations so we don't end up splitting effects between the WM // pending transaction and the BLASTSync transaction. if (syncId >= 0) { mBLASTSyncEngine.addToSyncSet(syncId, wc); addToSyncSet(syncId, wc); } int containerEffect = applyWindowContainerChange(wc, entry.getValue()); Loading @@ -164,7 +166,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub continue; } if (syncId >= 0) { mBLASTSyncEngine.addToSyncSet(syncId, wc); addToSyncSet(syncId, wc); } effects |= sanitizeAndApplyHierarchyOp(wc, hop); } Loading Loading @@ -396,21 +398,33 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return mDisplayAreaOrganizerController; } @VisibleForTesting int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) { int id = mBLASTSyncEngine.startSyncSet(this); mTransactionCallbacksByPendingSyncId.put(id, callback); return id; } @VisibleForTesting void setSyncReady(int id) { mBLASTSyncEngine.setReady(id); } @VisibleForTesting void addToSyncSet(int syncId, WindowContainer wc) { mBLASTSyncEngine.addToSyncSet(syncId, wc); } @Override public void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { final IWindowContainerTransactionCallback callback = mTransactionCallbacksByPendingSyncId.get(mSyncId); SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction(); for (WindowContainer container : windowContainersReady) { container.mergeBlastSyncTransaction(mergedTransaction); } try { callback.onTransactionReady(mSyncId, mergedTransaction); } catch (RemoteException e) { Loading
services/core/java/com/android/server/wm/WindowState.java +26 −4 Original line number Diff line number Diff line Loading @@ -242,8 +242,10 @@ import com.android.server.wm.utils.WmDisplayCutout; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.function.Predicate; /** A window in the window manager. */ Loading Loading @@ -655,6 +657,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private static final StringBuilder sTmpSB = new StringBuilder(); /** * Whether the next surfacePlacement call should notify that the blast sync is ready. * This is set to true when {@link #finishDrawing(Transaction)} is called so * {@link #onTransactionReady(int, Set)} is called after the next surfacePlacement. This allows * Transactions to get flushed into the syncTransaction before notifying {@link BLASTSyncEngine} * that this WindowState is ready. */ private boolean mNotifyBlastOnSurfacePlacement; /** * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms * of z-order and 1 otherwise. Loading Loading @@ -5346,6 +5357,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateFrameRateSelectionPriorityIfNeeded(); mWinAnimator.prepareSurfaceLocked(true); notifyBlastSyncTransaction(); super.prepareSurfaces(); } Loading Loading @@ -5837,6 +5849,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mBLASTSyncTransaction.merge(postDrawTransaction); } mNotifyBlastOnSurfacePlacement = true; return mWinAnimator.finishDrawingLocked(null); } @VisibleForTesting void notifyBlastSyncTransaction() { if (!mNotifyBlastOnSurfacePlacement || mWaitingListener == null) { mNotifyBlastOnSurfacePlacement = false; return; } // 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 Loading @@ -5844,15 +5867,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // be invoked and we need to invoke it ourself. if (mLocalSyncId >= 0) { mBLASTSyncEngine.setReady(mLocalSyncId); return mWinAnimator.finishDrawingLocked(null); return; } mWaitingListener.onTransactionReady(mWaitingSyncId, mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; mWaitingListener.onTransactionReady(mWaitingSyncId, Collections.singleton(this)); mWaitingSyncId = 0; mWaitingListener = null; return mWinAnimator.finishDrawingLocked(null); mNotifyBlastOnSurfacePlacement = false; } private boolean requestResizeForBlastSync() { Loading
services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +43 −4 Original line number Diff line number Diff line Loading @@ -67,6 +67,7 @@ import android.util.Rational; import android.view.Display; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.IWindowContainerTransactionCallback; import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; Loading Loading @@ -728,7 +729,7 @@ public class WindowOrganizerTests extends WindowTestsBase { // We should be rejected from the second sync since we are already // in one. assertEquals(false, bse.addToSyncSet(id2, task)); w.finishDrawing(null); finishAndNotifyDrawing(w); assertEquals(true, bse.addToSyncSet(id2, task)); bse.setReady(id2); } Loading @@ -752,7 +753,7 @@ public class WindowOrganizerTests extends WindowTestsBase { // Since we have a window we have to wait for it to draw to finish sync. verify(transactionListener, never()) .onTransactionReady(anyInt(), any()); w.finishDrawing(null); finishAndNotifyDrawing(w); verify(transactionListener) .onTransactionReady(anyInt(), any()); } Loading Loading @@ -820,14 +821,14 @@ public class WindowOrganizerTests extends WindowTestsBase { int id = bse.startSyncSet(transactionListener); assertEquals(true, bse.addToSyncSet(id, task)); bse.setReady(id); w.finishDrawing(null); finishAndNotifyDrawing(w); // Since we have a child window we still shouldn't be done. verify(transactionListener, never()) .onTransactionReady(anyInt(), any()); reset(transactionListener); child.finishDrawing(null); finishAndNotifyDrawing(child); // Ah finally! Done verify(transactionListener) .onTransactionReady(anyInt(), any()); Loading Loading @@ -979,4 +980,42 @@ public class WindowOrganizerTests extends WindowTestsBase { new IRequestFinishCallback.Default()); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } @Test public void testBLASTCallbackWithMultipleWindows() throws Exception { final ActivityStack stackController = createStack(); final Task task = createTask(stackController); final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1"); final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2"); makeWindowVisible(w1); makeWindowVisible(w2); IWindowContainerTransactionCallback mockCallback = mock(IWindowContainerTransactionCallback.class); int id = mWm.mAtmService.mWindowOrganizerController.startSyncWithOrganizer(mockCallback); mWm.mAtmService.mWindowOrganizerController.addToSyncSet(id, task); mWm.mAtmService.mWindowOrganizerController.setSyncReady(id); // Since we have a window we have to wait for it to draw to finish sync. verify(mockCallback, never()).onTransactionReady(anyInt(), any()); assertTrue(w1.useBLASTSync()); assertTrue(w2.useBLASTSync()); finishAndNotifyDrawing(w1); // Even though one Window finished drawing, both windows should still be using blast sync assertTrue(w1.useBLASTSync()); assertTrue(w2.useBLASTSync()); finishAndNotifyDrawing(w2); verify(mockCallback).onTransactionReady(anyInt(), any()); assertFalse(w1.useBLASTSync()); assertFalse(w2.useBLASTSync()); } private void finishAndNotifyDrawing(WindowState ws) { ws.finishDrawing(null); ws.notifyBlastSyncTransaction(); } }