Loading core/java/android/app/ITaskOrganizerController.aidl +13 −2 Original line number Diff line number Diff line Loading @@ -31,8 +31,19 @@ interface ITaskOrganizerController { */ void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode); /** Apply multiple WindowContainer operations at once. */ void applyContainerTransaction(in WindowContainerTransaction t); /** * Apply multiple WindowContainer operations at once. * @param organizer If non-null this transaction will use the synchronization * scheme described in BLASTSyncEngine.java. The SurfaceControl transaction * containing the effects of this WindowContainer transaction will be passed * to the organizers Transaction ready callback. If null the transaction * will apply with non particular synchronization constraints (other than * it will all apply at once). * @return If organizer was non-null returns an ID for the sync operation which will * later be passed to transactionReady. This lets TaskOrganizer implementations * differentiate overlapping sync operations. */ int applyContainerTransaction(in WindowContainerTransaction t, ITaskOrganizer organizer); /** Creates a persistent root task in WM for a particular windowing-mode. */ ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode); Loading services/core/java/com/android/server/wm/BLASTSyncEngine.java 0 → 100644 +122 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.view.ITaskOrganizer; import android.view.SurfaceControl; import java.util.HashMap; /** * Utility class for collecting and merging transactions from various sources asynchronously. * 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. * 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 * window in its sub hierarchy, then we will increment a counter of expected callbacks * At this point the containers hierarchy will redirect pendingTransaction and sub hierarchy * updates in to the sync engine. * 3. Apply your configuration changes to the window containers. * 4. Tell the engine that the sync set is ready * 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 */ class BLASTSyncEngine { private static final String TAG = "BLASTSyncEngine"; interface TransactionReadyListener { void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction); }; // 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; private void tryFinish() { if (mRemainingTransactions == 0 && mReady) { mListener.transactionReady(mSyncId, mMergedTransaction); mPendingSyncs.remove(mSyncId); } } public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { mRemainingTransactions--; mMergedTransaction.merge(mergedTransaction); tryFinish(); } void setReady() { mReady = true; tryFinish(); } boolean addToSync(WindowContainer wc) { if (wc.prepareForSync(this, mSyncId)) { mRemainingTransactions++; return true; } return false; } SyncState(TransactionReadyListener l, int id) { mListener = l; mSyncId = id; mMergedTransaction = new SurfaceControl.Transaction(); mRemainingTransactions = 0; } }; int mNextSyncId = 0; final HashMap<Integer, SyncState> mPendingSyncs = new HashMap(); BLASTSyncEngine() { } int startSyncSet(TransactionReadyListener listener) { final int id = mNextSyncId++; final SyncState s = new SyncState(listener, id); mPendingSyncs.put(id, s); return id; } boolean addToSyncSet(int id, WindowContainer wc) { final SyncState st = mPendingSyncs.get(id); return st.addToSync(wc); } // TODO(b/148476626): TIMEOUTS! void setReady(int id) { final SyncState st = mPendingSyncs.get(id); st.setReady(); } } services/core/java/com/android/server/wm/DisplayRotation.java +3 −1 Original line number Diff line number Diff line Loading @@ -526,7 +526,9 @@ public class DisplayRotation { mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); mIsWaitingForRemoteRotation = false; mDisplayContent.sendNewConfiguration(); mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t); if (t != null) { mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); } } } Loading services/core/java/com/android/server/wm/TaskOrganizerController.java +60 −4 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import android.util.ArraySet; import android.util.Slog; import android.view.ITaskOrganizer; import android.view.IWindowContainer; import android.view.SurfaceControl; import android.view.WindowContainerTransaction; import com.android.internal.util.function.pooled.PooledConsumer; Loading @@ -53,7 +54,8 @@ import java.util.WeakHashMap; * Stores the TaskOrganizers associated with a given windowing mode and * their associated state. */ class TaskOrganizerController extends ITaskOrganizerController.Stub { class TaskOrganizerController extends ITaskOrganizerController.Stub implements BLASTSyncEngine.TransactionReadyListener { private static final String TAG = "TaskOrganizerController"; /** Flag indicating that an applied transaction may have effected lifecycle */ Loading Loading @@ -164,6 +166,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); final ActivityTaskManagerService mService; RunningTaskInfo mTmpTaskInfo; Loading Loading @@ -223,7 +227,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void onTaskAppeared(ITaskOrganizer organizer, Task task) { TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); state.addTask(task); } Loading Loading @@ -429,15 +433,35 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override public void applyContainerTransaction(WindowContainerTransaction t) { public int applyContainerTransaction(WindowContainerTransaction t, ITaskOrganizer organizer) { enforceStackPermission("applyContainerTransaction()"); int syncId = -1; if (t == null) { return; throw new IllegalArgumentException( "Null transaction passed to applyContainerTransaction"); } long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { int effects = 0; /** * If organizer is non-null we are looking to synchronize this transaction * by collecting all the results in to a SurfaceFlinger transaction and * then delivering that to the given organizers transaction ready callback. * See {@link BLASTSyncEngine} for the details of the operation. But at * a high level we create a sync operation with a given ID and an associated * organizer. Then we notify each WindowContainer in this WindowContainer * transaction that it is participating in a sync operation with that * ID. Once everything is notified we tell the BLASTSyncEngine * "setSyncReady" which means that we have added everything * to the set. At any point after this, all the WindowContainers * will eventually finish applying their changes and notify the * BLASTSyncEngine which will deliver the Transaction to the organizer. */ if (organizer != null) { syncId = startSyncWithOrganizer(organizer); } mService.deferWindowLayout(); try { ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); Loading @@ -450,11 +474,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { entry.getKey()).getContainer(); int containerEffect = applyWindowContainerChange(wc, entry.getValue()); effects |= containerEffect; // Lifecycle changes will trigger ensureConfig for everything. if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { haveConfigChanges.add(wc); } if (syncId >= 0) { mBLASTSyncEngine.addToSyncSet(syncId, wc); } } if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { // Already calls ensureActivityConfig Loading @@ -475,10 +503,38 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } finally { mService.continueWindowLayout(); if (syncId >= 0) { setSyncReady(syncId); } } } } finally { Binder.restoreCallingIdentity(ident); } return syncId; } @Override public void transactionReady(int id, SurfaceControl.Transaction sc) { final ITaskOrganizer organizer = mTaskOrganizersByPendingSyncId.get(id); if (organizer == null) { Slog.e(TAG, "Got transaction complete for unexpected ID"); } try { organizer.transactionReady(id, sc); } catch (RemoteException e) { } mTaskOrganizersByPendingSyncId.remove(id); } int startSyncWithOrganizer(ITaskOrganizer organizer) { int id = mBLASTSyncEngine.startSyncSet(this); mTaskOrganizersByPendingSyncId.put(id, organizer); return id; } void setSyncReady(int id) { mBLASTSyncEngine.setReady(id); } } services/core/java/com/android/server/wm/WindowContainer.java +46 −1 Original line number Diff line number Diff line Loading @@ -94,7 +94,8 @@ import java.util.function.Predicate; * changes are made to this class. */ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> implements Comparable<WindowContainer>, Animatable { implements Comparable<WindowContainer>, Animatable, BLASTSyncEngine.TransactionReadyListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM; Loading Loading @@ -260,6 +261,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ RemoteToken mRemoteToken = null; BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); SurfaceControl.Transaction mBLASTSyncTransaction = new SurfaceControl.Transaction(); boolean mUsingBLASTSyncTransaction = false; BLASTSyncEngine.TransactionReadyListener mWaitingListener; int mWaitingSyncId; WindowContainer(WindowManagerService wms) { mWmService = wms; mPendingTransaction = wms.mTransactionFactory.get(); Loading Loading @@ -1837,6 +1844,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @Override public Transaction getPendingTransaction() { if (mUsingBLASTSyncTransaction) { return mBLASTSyncTransaction; } final DisplayContent displayContent = getDisplayContent(); if (displayContent != null && displayContent != this) { return displayContent.getPendingTransaction(); Loading Loading @@ -2316,4 +2327,38 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return sb.toString(); } } @Override public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { mergedTransaction.merge(mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; mWaitingListener.transactionReady(mWaitingSyncId, mergedTransaction); mWaitingListener = null; mWaitingSyncId = -1; } boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener, int waitingId) { boolean willSync = false; if (!isVisible()) { return willSync; } 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; mBLASTSyncEngine.setReady(localId); return willSync; } } Loading
core/java/android/app/ITaskOrganizerController.aidl +13 −2 Original line number Diff line number Diff line Loading @@ -31,8 +31,19 @@ interface ITaskOrganizerController { */ void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode); /** Apply multiple WindowContainer operations at once. */ void applyContainerTransaction(in WindowContainerTransaction t); /** * Apply multiple WindowContainer operations at once. * @param organizer If non-null this transaction will use the synchronization * scheme described in BLASTSyncEngine.java. The SurfaceControl transaction * containing the effects of this WindowContainer transaction will be passed * to the organizers Transaction ready callback. If null the transaction * will apply with non particular synchronization constraints (other than * it will all apply at once). * @return If organizer was non-null returns an ID for the sync operation which will * later be passed to transactionReady. This lets TaskOrganizer implementations * differentiate overlapping sync operations. */ int applyContainerTransaction(in WindowContainerTransaction t, ITaskOrganizer organizer); /** Creates a persistent root task in WM for a particular windowing-mode. */ ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode); Loading
services/core/java/com/android/server/wm/BLASTSyncEngine.java 0 → 100644 +122 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.view.ITaskOrganizer; import android.view.SurfaceControl; import java.util.HashMap; /** * Utility class for collecting and merging transactions from various sources asynchronously. * 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. * 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 * window in its sub hierarchy, then we will increment a counter of expected callbacks * At this point the containers hierarchy will redirect pendingTransaction and sub hierarchy * updates in to the sync engine. * 3. Apply your configuration changes to the window containers. * 4. Tell the engine that the sync set is ready * 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 */ class BLASTSyncEngine { private static final String TAG = "BLASTSyncEngine"; interface TransactionReadyListener { void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction); }; // 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; private void tryFinish() { if (mRemainingTransactions == 0 && mReady) { mListener.transactionReady(mSyncId, mMergedTransaction); mPendingSyncs.remove(mSyncId); } } public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { mRemainingTransactions--; mMergedTransaction.merge(mergedTransaction); tryFinish(); } void setReady() { mReady = true; tryFinish(); } boolean addToSync(WindowContainer wc) { if (wc.prepareForSync(this, mSyncId)) { mRemainingTransactions++; return true; } return false; } SyncState(TransactionReadyListener l, int id) { mListener = l; mSyncId = id; mMergedTransaction = new SurfaceControl.Transaction(); mRemainingTransactions = 0; } }; int mNextSyncId = 0; final HashMap<Integer, SyncState> mPendingSyncs = new HashMap(); BLASTSyncEngine() { } int startSyncSet(TransactionReadyListener listener) { final int id = mNextSyncId++; final SyncState s = new SyncState(listener, id); mPendingSyncs.put(id, s); return id; } boolean addToSyncSet(int id, WindowContainer wc) { final SyncState st = mPendingSyncs.get(id); return st.addToSync(wc); } // TODO(b/148476626): TIMEOUTS! void setReady(int id) { final SyncState st = mPendingSyncs.get(id); st.setReady(); } }
services/core/java/com/android/server/wm/DisplayRotation.java +3 −1 Original line number Diff line number Diff line Loading @@ -526,7 +526,9 @@ public class DisplayRotation { mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); mIsWaitingForRemoteRotation = false; mDisplayContent.sendNewConfiguration(); mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t); if (t != null) { mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); } } } Loading
services/core/java/com/android/server/wm/TaskOrganizerController.java +60 −4 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import android.util.ArraySet; import android.util.Slog; import android.view.ITaskOrganizer; import android.view.IWindowContainer; import android.view.SurfaceControl; import android.view.WindowContainerTransaction; import com.android.internal.util.function.pooled.PooledConsumer; Loading @@ -53,7 +54,8 @@ import java.util.WeakHashMap; * Stores the TaskOrganizers associated with a given windowing mode and * their associated state. */ class TaskOrganizerController extends ITaskOrganizerController.Stub { class TaskOrganizerController extends ITaskOrganizerController.Stub implements BLASTSyncEngine.TransactionReadyListener { private static final String TAG = "TaskOrganizerController"; /** Flag indicating that an applied transaction may have effected lifecycle */ Loading Loading @@ -164,6 +166,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); final ActivityTaskManagerService mService; RunningTaskInfo mTmpTaskInfo; Loading Loading @@ -223,7 +227,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void onTaskAppeared(ITaskOrganizer organizer, Task task) { TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); state.addTask(task); } Loading Loading @@ -429,15 +433,35 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override public void applyContainerTransaction(WindowContainerTransaction t) { public int applyContainerTransaction(WindowContainerTransaction t, ITaskOrganizer organizer) { enforceStackPermission("applyContainerTransaction()"); int syncId = -1; if (t == null) { return; throw new IllegalArgumentException( "Null transaction passed to applyContainerTransaction"); } long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { int effects = 0; /** * If organizer is non-null we are looking to synchronize this transaction * by collecting all the results in to a SurfaceFlinger transaction and * then delivering that to the given organizers transaction ready callback. * See {@link BLASTSyncEngine} for the details of the operation. But at * a high level we create a sync operation with a given ID and an associated * organizer. Then we notify each WindowContainer in this WindowContainer * transaction that it is participating in a sync operation with that * ID. Once everything is notified we tell the BLASTSyncEngine * "setSyncReady" which means that we have added everything * to the set. At any point after this, all the WindowContainers * will eventually finish applying their changes and notify the * BLASTSyncEngine which will deliver the Transaction to the organizer. */ if (organizer != null) { syncId = startSyncWithOrganizer(organizer); } mService.deferWindowLayout(); try { ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); Loading @@ -450,11 +474,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { entry.getKey()).getContainer(); int containerEffect = applyWindowContainerChange(wc, entry.getValue()); effects |= containerEffect; // Lifecycle changes will trigger ensureConfig for everything. if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { haveConfigChanges.add(wc); } if (syncId >= 0) { mBLASTSyncEngine.addToSyncSet(syncId, wc); } } if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { // Already calls ensureActivityConfig Loading @@ -475,10 +503,38 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } finally { mService.continueWindowLayout(); if (syncId >= 0) { setSyncReady(syncId); } } } } finally { Binder.restoreCallingIdentity(ident); } return syncId; } @Override public void transactionReady(int id, SurfaceControl.Transaction sc) { final ITaskOrganizer organizer = mTaskOrganizersByPendingSyncId.get(id); if (organizer == null) { Slog.e(TAG, "Got transaction complete for unexpected ID"); } try { organizer.transactionReady(id, sc); } catch (RemoteException e) { } mTaskOrganizersByPendingSyncId.remove(id); } int startSyncWithOrganizer(ITaskOrganizer organizer) { int id = mBLASTSyncEngine.startSyncSet(this); mTaskOrganizersByPendingSyncId.put(id, organizer); return id; } void setSyncReady(int id) { mBLASTSyncEngine.setReady(id); } }
services/core/java/com/android/server/wm/WindowContainer.java +46 −1 Original line number Diff line number Diff line Loading @@ -94,7 +94,8 @@ import java.util.function.Predicate; * changes are made to this class. */ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> implements Comparable<WindowContainer>, Animatable { implements Comparable<WindowContainer>, Animatable, BLASTSyncEngine.TransactionReadyListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM; Loading Loading @@ -260,6 +261,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ RemoteToken mRemoteToken = null; BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); SurfaceControl.Transaction mBLASTSyncTransaction = new SurfaceControl.Transaction(); boolean mUsingBLASTSyncTransaction = false; BLASTSyncEngine.TransactionReadyListener mWaitingListener; int mWaitingSyncId; WindowContainer(WindowManagerService wms) { mWmService = wms; mPendingTransaction = wms.mTransactionFactory.get(); Loading Loading @@ -1837,6 +1844,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @Override public Transaction getPendingTransaction() { if (mUsingBLASTSyncTransaction) { return mBLASTSyncTransaction; } final DisplayContent displayContent = getDisplayContent(); if (displayContent != null && displayContent != this) { return displayContent.getPendingTransaction(); Loading Loading @@ -2316,4 +2327,38 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return sb.toString(); } } @Override public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { mergedTransaction.merge(mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; mWaitingListener.transactionReady(mWaitingSyncId, mergedTransaction); mWaitingListener = null; mWaitingSyncId = -1; } boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener, int waitingId) { boolean willSync = false; if (!isVisible()) { return willSync; } 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; mBLASTSyncEngine.setReady(localId); return willSync; } }