diff options
| author | 2022-01-25 02:19:36 +0000 | |
|---|---|---|
| committer | 2022-01-25 02:19:36 +0000 | |
| commit | 7481586ab622a1d227396ca678c423799f5bd3ad (patch) | |
| tree | cedb4e764c0b8c0efa6c3c44119d912bc8c6fd3c | |
| parent | 1ddd7f015de43a68d7177a5ffdbaa54918d75e79 (diff) | |
| parent | 1b97f8262c1a3f2b7244522e57198c382f6a923c (diff) | |
Merge changes Ida331585,I255fb336
* changes:
Queue applySyncTransaction calls when there is active BLAST sync
Queue startTransition calls made while already collecting
7 files changed, 293 insertions, 47 deletions
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 07523299c353..8f73b9a12da2 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -319,6 +319,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimationController.java" }, + "-1764792832": { + "message": "Start collecting in Transition: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/TransitionController.java" + }, "-1750384749": { "message": "Launch on display check: allow launch on public display", "level": "DEBUG", @@ -2791,6 +2797,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "800698875": { + "message": "SyncGroup %d: Started when there is other active SyncGroup", + "level": "WARN", + "group": "WM_DEBUG_SYNC_ENGINE", + "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" + }, "806891543": { "message": "Setting mOrientationChangeComplete=true because wtoken %s numInteresting=%d numDrawn=%d", "level": "INFO", @@ -3205,6 +3217,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1333520287": { + "message": "Creating PendingTransition: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/TransitionController.java" + }, "1337596507": { "message": "Sending to proc %s new compat %s", "level": "VERBOSE", @@ -3823,6 +3841,12 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "2034988903": { + "message": "PendingStartTransaction found", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/WindowOrganizerController.java" + }, "2039056415": { "message": "Found matching affinity candidate!", "level": "DEBUG", diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index bb4519cfc679..5c1ddd964325 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -64,6 +64,11 @@ class BLASTSyncEngine { void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction); } + interface SyncEngineListener { + /** Called when there is no more active sync set. */ + void onSyncEngineFree(); + } + /** * Holds state associated with a single synchronous set of operations. */ @@ -137,6 +142,9 @@ class BLASTSyncEngine { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); mActiveSyncs.remove(mSyncId); mWm.mH.removeCallbacks(mOnTimeout); + if (mSyncEngineListener != null && mActiveSyncs.size() == 0) { + mSyncEngineListener.onSyncEngineFree(); + } } private void setReady(boolean ready) { @@ -175,22 +183,54 @@ class BLASTSyncEngine { private final WindowManagerService mWm; private int mNextSyncId = 0; private final SparseArray<SyncGroup> mActiveSyncs = new SparseArray<>(); + private SyncEngineListener mSyncEngineListener; BLASTSyncEngine(WindowManagerService wms) { mWm = wms; } + /** Sets listener listening to whether the sync engine is free. */ + void setSyncEngineListener(SyncEngineListener listener) { + mSyncEngineListener = listener; + } + + /** + * Prepares a {@link SyncGroup} that is not active yet. Caller must call {@link #startSyncSet} + * before calling {@link #addToSyncSet(int, WindowContainer)} on any {@link WindowContainer}. + */ + SyncGroup prepareSyncSet(TransactionReadyListener listener, String name) { + return new SyncGroup(listener, mNextSyncId++, name); + } + int startSyncSet(TransactionReadyListener listener) { return startSyncSet(listener, WindowState.BLAST_TIMEOUT_DURATION, ""); } int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name) { - final int id = mNextSyncId++; - final SyncGroup s = new SyncGroup(listener, id, name); - mActiveSyncs.put(id, s); - ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s", id, listener); + final SyncGroup s = prepareSyncSet(listener, name); + startSyncSet(s, timeoutMs); + return s.mSyncId; + } + + void startSyncSet(SyncGroup s) { + startSyncSet(s, WindowState.BLAST_TIMEOUT_DURATION); + } + + void startSyncSet(SyncGroup s, long timeoutMs) { + if (mActiveSyncs.size() != 0) { + // We currently only support one sync at a time, so start a new SyncGroup when there is + // another may cause issue. + ProtoLog.w(WM_DEBUG_SYNC_ENGINE, + "SyncGroup %d: Started when there is other active SyncGroup", s.mSyncId); + } + mActiveSyncs.put(s.mSyncId, s); + ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s", + s.mSyncId, s.mListener); scheduleTimeout(s, timeoutMs); - return id; + } + + boolean hasActiveSync() { + return mActiveSyncs.size() != 0; } @VisibleForTesting @@ -199,11 +239,11 @@ class BLASTSyncEngine { } void addToSyncSet(int id, WindowContainer wc) { - mActiveSyncs.get(id).addToSync(wc); + getSyncGroup(id).addToSync(wc); } void setReady(int id, boolean ready) { - mActiveSyncs.get(id).setReady(ready); + getSyncGroup(id).setReady(ready); } void setReady(int id) { @@ -211,14 +251,22 @@ class BLASTSyncEngine { } boolean isReady(int id) { - return mActiveSyncs.get(id).mReady; + return getSyncGroup(id).mReady; } /** * Aborts the sync (ie. it doesn't wait for ready or anything to finish) */ void abort(int id) { - mActiveSyncs.get(id).finishNow(); + getSyncGroup(id).finishNow(); + } + + private SyncGroup getSyncGroup(int id) { + final SyncGroup syncGroup = mActiveSyncs.get(id); + if (syncGroup == null) { + throw new IllegalStateException("SyncGroup is not started yet id=" + id); + } + return syncGroup; } void onSurfacePlacement() { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index b13c9a9e3e14..18df316f5476 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -101,6 +101,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe /** The default package for resources */ private static final String DEFAULT_PACKAGE = "android"; + /** The transition has been created but isn't collecting yet. */ + private static final int STATE_PENDING = -1; + /** The transition has been created and is collecting, but hasn't formally started. */ private static final int STATE_COLLECTING = 0; @@ -122,6 +125,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private static final int STATE_ABORT = 3; @IntDef(prefix = { "STATE_" }, value = { + STATE_PENDING, STATE_COLLECTING, STATE_STARTED, STATE_PLAYING, @@ -131,7 +135,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe @interface TransitionState {} final @TransitionType int mType; - private int mSyncId; + private int mSyncId = -1; private @TransitionFlags int mFlags; private final TransitionController mController; private final BLASTSyncEngine mSyncEngine; @@ -171,7 +175,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private IRemoteCallback mClientAnimationStartCallback = null; private IRemoteCallback mClientAnimationFinishCallback = null; - private @TransitionState int mState = STATE_COLLECTING; + private @TransitionState int mState = STATE_PENDING; private final ReadyTracker mReadyTracker = new ReadyTracker(); // TODO(b/188595497): remove when not needed. @@ -179,13 +183,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private boolean mNavBarAttachedToApp = false; private int mRecentsDisplayId = INVALID_DISPLAY; - Transition(@TransitionType int type, @TransitionFlags int flags, long timeoutMs, + Transition(@TransitionType int type, @TransitionFlags int flags, TransitionController controller, BLASTSyncEngine syncEngine) { mType = type; mFlags = flags; mController = controller; mSyncEngine = syncEngine; - mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG); } void addFlag(int flag) { @@ -216,13 +219,24 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return mFlags; } + /** Starts collecting phase. Once this starts, all relevant surface operations are sync. */ + void startCollecting(long timeoutMs) { + if (mState != STATE_PENDING) { + throw new IllegalStateException("Attempting to re-use a transition"); + } + mState = STATE_COLLECTING; + mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG); + } + /** * Formally starts the transition. Participants can be collected before this is started, * but this won't consider itself ready until started -- even if all the participants have * drawn. */ void start() { - if (mState >= STATE_STARTED) { + if (mState < STATE_COLLECTING) { + throw new IllegalStateException("Can't start Transition which isn't collecting."); + } else if (mState >= STATE_STARTED) { Slog.w(TAG, "Transition already started: " + mSyncId); } mState = STATE_STARTED; @@ -235,6 +249,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * Adds wc to set of WindowContainers participating in this transition. */ void collect(@NonNull WindowContainer wc) { + if (mState < STATE_COLLECTING) { + throw new IllegalStateException("Transition hasn't started collecting."); + } if (mSyncId < 0) return; ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Collecting in transition %d: %s", mSyncId, wc); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index fe968ec2e57b..7a031db3a226 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -128,16 +128,46 @@ class TransitionController { throw new IllegalStateException("Shell Transitions not enabled"); } if (mCollectingTransition != null) { - throw new IllegalStateException("Simultaneous transitions not supported yet."); + throw new IllegalStateException("Simultaneous transition collection not supported" + + " yet. Use {@link #createPendingTransition} for explicit queueing."); } + Transition transit = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine); + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s", transit); + moveToCollecting(transit); + return transit; + } + + /** Starts Collecting */ + private void moveToCollecting(@NonNull Transition transition) { + if (mCollectingTransition != null) { + throw new IllegalStateException("Simultaneous transition collection not supported."); + } + mCollectingTransition = transition; // Distinguish change type because the response time is usually expected to be not too long. - final long timeoutMs = type == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS; - mCollectingTransition = new Transition(type, flags, timeoutMs, this, - mAtm.mWindowManager.mSyncEngine); - ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s", + final long timeoutMs = + transition.mType == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS; + mCollectingTransition.startCollecting(timeoutMs); + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start collecting in Transition: %s", mCollectingTransition); dispatchLegacyAppTransitionPending(); - return mCollectingTransition; + } + + /** Creates a transition representation but doesn't start collecting. */ + @NonNull + PendingStartTransition createPendingTransition(@WindowManager.TransitionType int type) { + if (mTransitionPlayer == null) { + throw new IllegalStateException("Shell Transitions not enabled"); + } + final PendingStartTransition out = new PendingStartTransition(new Transition(type, + 0 /* flags */, this, mAtm.mWindowManager.mSyncEngine)); + // We want to start collecting immediately when the engine is free, otherwise it may + // be busy again. + out.setStartSync(() -> { + moveToCollecting(out.mTransition); + }); + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating PendingTransition: %s", + out.mTransition); + return out; } void registerTransitionPlayer(@Nullable ITransitionPlayer player, @@ -507,6 +537,15 @@ class TransitionController { proto.end(token); } + /** Represents a startTransition call made while there is other active BLAST SyncGroup. */ + class PendingStartTransition extends WindowOrganizerController.PendingTransaction { + final Transition mTransition; + + PendingStartTransition(Transition transition) { + mTransition = transition; + } + } + static class TransitionMetricsReporter extends ITransitionMetricsReporter.Stub { private final ArrayMap<IBinder, LongConsumer> mMetricConsumers = new ArrayMap<>(); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 400684840467..1205dee595b9 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -85,8 +85,8 @@ import android.view.MagnificationSpec; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; import android.view.SurfaceControl.Builder; +import android.view.SurfaceControlViewHost; import android.view.SurfaceSession; import android.view.TaskTransitionSpec; import android.view.WindowManager; @@ -3331,7 +3331,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "setSyncGroup #%d on %s", group.mSyncId, this); if (group != null) { if (mSyncGroup != null && mSyncGroup != group) { - throw new IllegalStateException("Can't sync on 2 engines simultaneously"); + // This can still happen if WMCore starts a new transition when there is ongoing + // sync transaction from Shell. Please file a bug if it happens. + throw new IllegalStateException("Can't sync on 2 engines simultaneously" + + " currentSyncId=" + mSyncGroup.mSyncId + " newSyncId=" + group.mSyncId); } } mSyncGroup = group; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 455856ce22cd..27024ce77bd2 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -76,6 +76,7 @@ import android.window.TaskFragmentCreationParams; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledConsumer; @@ -94,7 +95,7 @@ import java.util.Map; * @see android.window.WindowOrganizer */ class WindowOrganizerController extends IWindowOrganizerController.Stub - implements BLASTSyncEngine.TransactionReadyListener { + implements BLASTSyncEngine.TransactionReadyListener, BLASTSyncEngine.SyncEngineListener { private static final String TAG = "WindowOrganizerController"; @@ -117,6 +118,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private final HashMap<Integer, IWindowContainerTransactionCallback> mTransactionCallbacksByPendingSyncId = new HashMap(); + /** + * A queue of transaction waiting for their turn to sync. Currently {@link BLASTSyncEngine} only + * supports 1 sync at a time, so we have to queue them. + * + * WMCore has enough information to ensure that it won't end up collecting multiple transitions + * in parallel by itself; however, Shell can start transitions/apply sync transaction at + * arbitrary times via {@link WindowOrganizerController#startTransition} and + * {@link WindowOrganizerController#applySyncTransaction}, so we have to support those coming in + * at any time (even while already syncing). + * + * This is really just a back-up for unrealistic situations (eg. during tests). In practice, + * this shouldn't ever happen. + */ + private final ArrayList<PendingTransaction> mPendingTransactions = new ArrayList<>(); + final TaskOrganizerController mTaskOrganizerController; final DisplayAreaOrganizerController mDisplayAreaOrganizerController; final TaskFragmentOrganizerController mTaskFragmentOrganizerController; @@ -140,6 +156,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub void setWindowManager(WindowManagerService wms) { mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController); mTransitionController.registerLegacyListener(wms.mActivityManagerAppTransitionNotifier); + wms.mSyncEngine.setSyncEngineListener(this); } TransitionController getTransitionController() { @@ -184,6 +201,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + if (callback == null) { + applyTransaction(t, -1 /* syncId*/, null /*transition*/, caller); + return -1; + } + /** * If callback is non-null we are looking to synchronize this transaction by * collecting all the results in to a SurfaceFlinger transaction and then delivering @@ -196,13 +218,25 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub * all the WindowContainers will eventually finish applying their changes and notify * the BLASTSyncEngine which will deliver the Transaction to the callback. */ - int syncId = -1; - if (callback != null) { - syncId = startSyncWithOrganizer(callback); - } - applyTransaction(t, syncId, null /*transition*/, caller); - if (syncId >= 0) { + final BLASTSyncEngine.SyncGroup syncGroup = prepareSyncWithOrganizer(callback); + final int syncId = syncGroup.mSyncId; + if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) { + mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup); + applyTransaction(t, syncId, null /*transition*/, caller); setSyncReady(syncId); + } else { + // Because the BLAST engine only supports one sync at a time, queue the + // transaction. + final PendingTransaction pt = new PendingTransaction(); + // Start sync group immediately when the SyncEngine is free. + pt.setStartSync(() -> + mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup)); + // Those will be post so that it won't interrupt ongoing transition. + pt.setStartTransaction(() -> { + applyTransaction(t, syncId, null /*transition*/, caller); + setSyncReady(syncId); + }); + mPendingTransactions.add(pt); } return syncId; } @@ -220,31 +254,49 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub try { synchronized (mGlobalLock) { Transition transition = Transition.fromBinder(transitionToken); + if (mTransitionController.getTransitionPlayer() == null && transition == null) { + Slog.w(TAG, "Using shell transitions API for legacy transitions."); + if (t == null) { + throw new IllegalArgumentException("Can't use legacy transitions in" + + " compatibility mode with no WCT."); + } + applyTransaction(t, -1 /* syncId */, null, caller); + return null; + } // In cases where transition is already provided, the "readiness lifecycle" of the // transition is determined outside of this transaction. However, if this is a // direct call from shell, the entire transition lifecycle is contained in the // provided transaction and thus we can setReady immediately after apply. - boolean needsSetReady = transition == null && t != null; + final boolean needsSetReady = transition == null && t != null; + final WindowContainerTransaction wct = + t != null ? t : new WindowContainerTransaction(); if (transition == null) { if (type < 0) { throw new IllegalArgumentException("Can't create transition with no type"); } - if (mTransitionController.getTransitionPlayer() == null) { - Slog.w(TAG, "Using shell transitions API for legacy transitions."); - if (t == null) { - throw new IllegalArgumentException("Can't use legacy transitions in" - + " compatibility mode with no WCT."); - } - applyTransaction(t, -1 /* syncId */, null, caller); - return null; + // If there is already a collecting transition, queue up a new transition and + // return that. The actual start and apply will then be deferred until that + // transition starts collecting. This should almost never happen except during + // tests. + if (mService.mWindowManager.mSyncEngine.hasActiveSync()) { + Slog.e(TAG, "startTransition() while one is already collecting."); + final TransitionController.PendingStartTransition pt = + mTransitionController.createPendingTransition(type); + // Those will be post so that it won't interrupt ongoing transition. + pt.setStartTransaction(() -> { + pt.mTransition.start(); + applyTransaction(wct, -1 /*syncId*/, pt.mTransition, caller); + if (needsSetReady) { + pt.mTransition.setAllReady(); + } + }); + mPendingTransactions.add(pt); + return pt.mTransition; } transition = mTransitionController.createTransition(type); } transition.start(); - if (t == null) { - t = new WindowContainerTransaction(); - } - applyTransaction(t, -1 /*syncId*/, transition, caller); + applyTransaction(wct, -1 /*syncId*/, transition, caller); if (needsSetReady) { transition.setAllReady(); } @@ -1036,11 +1088,24 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return mTaskFragmentOrganizerController; } + /** + * This will prepare a {@link BLASTSyncEngine.SyncGroup} for the organizer to track, but the + * {@link BLASTSyncEngine.SyncGroup} may not be active until the {@link BLASTSyncEngine} is + * free. + */ + private BLASTSyncEngine.SyncGroup prepareSyncWithOrganizer( + IWindowContainerTransactionCallback callback) { + final BLASTSyncEngine.SyncGroup s = mService.mWindowManager.mSyncEngine + .prepareSyncSet(this, ""); + mTransactionCallbacksByPendingSyncId.put(s.mSyncId, callback); + return s; + } + @VisibleForTesting int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) { - int id = mService.mWindowManager.mSyncEngine.startSyncSet(this); - mTransactionCallbacksByPendingSyncId.put(id, callback); - return id; + final BLASTSyncEngine.SyncGroup s = prepareSyncWithOrganizer(callback); + mService.mWindowManager.mSyncEngine.startSyncSet(s); + return s.mSyncId; } @VisibleForTesting @@ -1072,6 +1137,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } @Override + public void onSyncEngineFree() { + if (mPendingTransactions.isEmpty()) { + return; + } + + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "PendingStartTransaction found"); + final PendingTransaction pt = mPendingTransactions.remove(0); + pt.startSync(); + // Post this so that the now-playing transition setup isn't interrupted. + mService.mH.post(pt::startTransaction); + } + + @Override public void registerTransitionPlayer(ITransitionPlayer player) { enforceTaskPermission("registerTransitionPlayer()"); final int callerPid = Binder.getCallingPid(); @@ -1329,4 +1407,38 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + result + " when starting " + intent); } } + + /** + * Represents a sync {@link WindowContainerTransaction} call made while there is other active + * {@link BLASTSyncEngine.SyncGroup}. + */ + static class PendingTransaction { + private Runnable mStartSync; + private Runnable mStartTransaction; + + /** + * The callback will be called immediately when the {@link BLASTSyncEngine} is free. One + * should call {@link BLASTSyncEngine#startSyncSet(BLASTSyncEngine.SyncGroup)} here to + * reserve the {@link BLASTSyncEngine}. + */ + void setStartSync(@NonNull Runnable callback) { + mStartSync = callback; + } + + /** + * The callback will be post to the main handler after the {@link BLASTSyncEngine} is free + * to apply the pending {@link WindowContainerTransaction}. + */ + void setStartTransaction(@NonNull Runnable callback) { + mStartTransaction = callback; + } + + private void startSync() { + mStartSync.run(); + } + + private void startTransaction() { + mStartTransaction.run(); + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 141588a87585..ae3ce04df761 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -75,7 +75,9 @@ public class TransitionTests extends WindowTestsBase { private Transition createTestTransition(int transitType) { TransitionController controller = mock(TransitionController.class); final BLASTSyncEngine sync = createTestBLASTSyncEngine(); - return new Transition(transitType, 0 /* flags */, 0 /* timeoutMs */, controller, sync); + final Transition t = new Transition(transitType, 0 /* flags */, controller, sync); + t.startCollecting(0 /* timeoutMs */); + return t; } @Test @@ -466,12 +468,13 @@ public class TransitionTests extends WindowTestsBase { final BLASTSyncEngine sync = new BLASTSyncEngine(mWm); final CountDownLatch latch = new CountDownLatch(1); // When the timeout is reached, it will finish the sync-group and notify transaction ready. - new Transition(TRANSIT_OPEN, 0 /* flags */, 10 /* timeoutMs */, controller, sync) { + final Transition t = new Transition(TRANSIT_OPEN, 0 /* flags */, controller, sync) { @Override public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) { latch.countDown(); } }; + t.startCollecting(10 /* timeoutMs */); assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS))); } |