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

Commit e10ee3d2 authored by Robert Carr's avatar Robert Carr
Browse files

Introduce BLASTSyncEngine

Test: wmtests/TaskOrganizerTests, TaskOrganizerTests demo apps
Change-Id: I82c2e184e93782549bc4f78e0986a026d7ef8d03
parent b41e7e8f
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -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);
+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();
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -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);
            }
        }
    }

+60 −4
Original line number Diff line number Diff line
@@ -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;
@@ -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 */
@@ -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;
@@ -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);
    }

@@ -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<>();
@@ -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
@@ -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);
    }
}
+46 −1
Original line number Diff line number Diff line
@@ -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;

@@ -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();
@@ -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();
@@ -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