Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35c049de authored by Evan Rosky's avatar Evan Rosky
Browse files

Move the pendingTransaction stuff into BLASTSyncEngine

The functionality has morphed since its original transitions-only
incarnation. Now it is a general queue mechanism for SyncEngine.

So, move it into BLASTSyncEngine and clean-up the abstraction
a little-bit. This should make it more usable for some upcoming
fixes.

Bug: 222033492
Test: existing tests pass (just a refactor)
Change-Id: Ia057e816ad5635e9681d3273d139e494c18216d9
parent d5826ea5
Loading
Loading
Loading
Loading
+12 −12
Original line number Diff line number Diff line
@@ -3433,12 +3433,6 @@
      "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"
    },
    "1335791109": {
      "message": "createSurface %s: mDrawState=DRAW_PENDING",
      "level": "INFO",
@@ -3733,6 +3727,12 @@
      "group": "WM_DEBUG_IME",
      "at": "com\/android\/server\/wm\/InsetsStateController.java"
    },
    "1667162379": {
      "message": "Creating Pending Transition: %s",
      "level": "VERBOSE",
      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
    },
    "1670933628": {
      "message": " Setting allReady override",
      "level": "VERBOSE",
@@ -3787,6 +3787,12 @@
      "group": "WM_ERROR",
      "at": "com\/android\/server\/wm\/WindowManagerService.java"
    },
    "1730300180": {
      "message": "PendingStartTransaction found",
      "level": "VERBOSE",
      "group": "WM_DEBUG_SYNC_ENGINE",
      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
    },
    "1739298851": {
      "message": "removeWindowToken: Attempted to remove token: %s for non-exiting displayId=%d",
      "level": "WARN",
@@ -4099,12 +4105,6 @@
      "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",
+62 −11
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;

import java.util.ArrayList;

/**
 * Utility class for collecting WindowContainers that will merge transactions.
 * For example to use to synchronously resize all the children of a window container
@@ -64,9 +66,17 @@ class BLASTSyncEngine {
        void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
    }

    interface SyncEngineListener {
        /** Called when there is no more active sync set. */
        void onSyncEngineFree();
    /**
     * Represents the desire to make a {@link BLASTSyncEngine.SyncGroup} while another is active.
     *
     * @see #queueSyncSet
     */
    private static class PendingSyncSet {
        /** Called immediately when the {@link BLASTSyncEngine} is free. */
        private Runnable mStartSync;

        /** Posted to the main handler after {@link #mStartSync} is called. */
        private Runnable mApplySync;
    }

    /**
@@ -142,8 +152,21 @@ class BLASTSyncEngine {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            mActiveSyncs.remove(mSyncId);
            mWm.mH.removeCallbacks(mOnTimeout);
            if (mSyncEngineListener != null && mActiveSyncs.size() == 0) {
                mSyncEngineListener.onSyncEngineFree();

            // Immediately start the next pending sync-transaction if there is one.
            if (mActiveSyncs.size() == 0 && !mPendingSyncSets.isEmpty()) {
                ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "PendingStartTransaction found");
                final PendingSyncSet pt = mPendingSyncSets.remove(0);
                pt.mStartSync.run();
                if (mActiveSyncs.size() == 0) {
                    throw new IllegalStateException("Pending Sync Set didn't start a sync.");
                }
                // Post this so that the now-playing transition setup isn't interrupted.
                mWm.mH.post(() -> {
                    synchronized (mWm.mGlobalLock) {
                        pt.mApplySync.run();
                    }
                });
            }
        }

@@ -183,17 +206,18 @@ class BLASTSyncEngine {
    private final WindowManagerService mWm;
    private int mNextSyncId = 0;
    private final SparseArray<SyncGroup> mActiveSyncs = new SparseArray<>();
    private SyncEngineListener mSyncEngineListener;

    /**
     * A queue of pending sync-sets waiting for their turn to run.
     *
     * @see #queueSyncSet
     */
    private final ArrayList<PendingSyncSet> mPendingSyncSets = new ArrayList<>();

    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}.
@@ -275,4 +299,31 @@ class BLASTSyncEngine {
            mActiveSyncs.valueAt(i).onSurfacePlacement();
        }
    }

    /**
     * Queues a sync operation onto this engine. It will wait until any current/prior sync-sets
     * have finished to run. This is needed right now because currently {@link BLASTSyncEngine}
     * only supports 1 sync at a time.
     *
     * Code-paths should avoid using this unless absolutely necessary. Usually, we use this for
     * difficult edge-cases that we hope to clean-up later.
     *
     * @param startSync will be called immediately when the {@link BLASTSyncEngine} is free to
     *                  "reserve" the {@link BLASTSyncEngine} by calling one of the
     *                  {@link BLASTSyncEngine#startSyncSet} variants.
     * @param applySync will be posted to the main handler after {@code startSync} has been
     *                  called. This is posted so that it doesn't interrupt any clean-up for the
     *                  prior sync-set.
     */
    void queueSyncSet(@NonNull Runnable startSync, @NonNull Runnable applySync) {
        final PendingSyncSet pt = new PendingSyncSet();
        pt.mStartSync = startSync;
        pt.mApplySync = applySync;
        mPendingSyncSets.add(pt);
    }

    /** @return {@code true} if there are any sync-sets waiting to start. */
    boolean hasPendingSyncSets() {
        return !mPendingSyncSets.isEmpty();
    }
}
+5 −36
Original line number Diff line number Diff line
@@ -99,9 +99,6 @@ class TransitionController {
    // TODO(b/188595497): remove when not needed.
    final StatusBarManagerInternal mStatusBar;

    /** Pending transitions from Shell that are waiting the SyncEngine to be free. */
    private final ArrayList<PendingStartTransition> mPendingTransitions = new ArrayList<>();

    TransitionController(ActivityTaskManagerService atm,
            TaskSnapshotController taskSnapshotController) {
        mAtm = atm;
@@ -146,7 +143,7 @@ class TransitionController {
    }

    /** Starts Collecting */
    private void moveToCollecting(@NonNull Transition transition) {
    void moveToCollecting(@NonNull Transition transition) {
        if (mCollectingTransition != null) {
            throw new IllegalStateException("Simultaneous transition collection not supported.");
        }
@@ -160,26 +157,6 @@ class TransitionController {
        dispatchLegacyAppTransitionPending();
    }

    /** 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));
        mPendingTransitions.add(out);
        // We want to start collecting immediately when the engine is free, otherwise it may
        // be busy again.
        out.setStartSync(() -> {
            mPendingTransitions.remove(out);
            moveToCollecting(out.mTransition);
        });
        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating PendingTransition: %s",
                out.mTransition);
        return out;
    }

    void registerTransitionPlayer(@Nullable ITransitionPlayer player,
            @Nullable IApplicationThread appThread) {
        try {
@@ -601,24 +578,16 @@ class TransitionController {
        if (!mPlayingTransitions.isEmpty()) {
            state = LEGACY_STATE_RUNNING;
        } else if ((mCollectingTransition != null && mCollectingTransition.getLegacyIsReady())
                || !mPendingTransitions.isEmpty()) {
            // The transition may not be "ready", but we have transition waiting to start, so it
            // can't be IDLE for test purpose. Ideally, we should have a STATE_COLLECTING.
                || mAtm.mWindowManager.mSyncEngine.hasPendingSyncSets()) {
            // The transition may not be "ready", but we have a sync-transaction waiting to start.
            // Usually the pending transaction is for a transition, so assuming that is the case,
            // we can't be IDLE for test purposes. Ideally, we should have a STATE_COLLECTING.
            state = LEGACY_STATE_READY;
        }
        proto.write(AppTransitionProto.APP_TRANSITION_STATE, state);
        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<>();

+25 −91
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ import java.util.function.IntSupplier;
 * @see android.window.WindowOrganizer
 */
class WindowOrganizerController extends IWindowOrganizerController.Stub
        implements BLASTSyncEngine.TransactionReadyListener, BLASTSyncEngine.SyncEngineListener {
        implements BLASTSyncEngine.TransactionReadyListener {

    private static final String TAG = "WindowOrganizerController";

@@ -122,21 +122,6 @@ 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;
@@ -160,7 +145,6 @@ 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() {
@@ -231,16 +215,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                } 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(() -> {
                    mService.mWindowManager.mSyncEngine.queueSyncSet(
                            () -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup),
                            () -> {
                                applyTransaction(t, syncId, null /*transition*/, caller);
                                setSyncReady(syncId);
                            });
                    mPendingTransactions.add(pt);
                }
                return syncId;
            }
@@ -283,19 +263,24 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                    // 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);
                        Slog.w(TAG, "startTransition() while one is already collecting.");
                        final Transition nextTransition = new Transition(type, 0 /* flags */,
                                mTransitionController, mService.mWindowManager.mSyncEngine);
                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                                "Creating Pending Transition: %s", nextTransition);
                        mService.mWindowManager.mSyncEngine.queueSyncSet(
                                // Make sure to collect immediately to prevent another transition
                                // from sneaking in before it. Note: moveToCollecting internally
                                // calls startSyncSet.
                                () -> mTransitionController.moveToCollecting(nextTransition),
                                () -> {
                                    nextTransition.start();
                                    applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
                                    if (needsSetReady) {
                                pt.mTransition.setAllReady();
                                        nextTransition.setAllReady();
                                    }
                                });
                        mPendingTransactions.add(pt);
                        return pt.mTransition;
                        return nextTransition;
                    }
                    transition = mTransitionController.createTransition(type);
                }
@@ -1189,23 +1174,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
        mTransactionCallbacksByPendingSyncId.remove(syncId);
    }

    @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(() -> {
            synchronized (mGlobalLock) {
                pt.startTransaction();
            }
        });
    }

    @Override
    public void registerTransitionPlayer(ITransitionPlayer player) {
        enforceTaskPermission("registerTransitionPlayer()");
@@ -1555,38 +1523,4 @@ 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();
        }
    }
}