admin管理员组

文章数量:1534214

一、WindowOrganizer发送WCT的两种方式

WindowContainerTransaction通过WindowOrganizer来发送,WindowOrganizer提供了两个方法WindowOrganizer#applyTransaction和WindowOrganizer#applySyncTransaction。

/**
 * Base class for organizing specific types of windows like Tasks and DisplayAreas
 *
 * @hide
 */
@TestApi
public class WindowOrganizer {

    /**
     * Apply multiple WindowContainer operations at once.
     * @param t The transaction to apply.
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
    public void applyTransaction(@NonNull WindowContainerTransaction t) {
        try {
            if (!t.isEmpty()) {
                getWindowOrganizerController().applyTransaction(t);
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Apply multiple WindowContainer operations at once.
     * @param t The transaction to apply.
     * @param callback 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 this callback when ready.
     * @return An ID for the sync operation which will later be passed to transactionReady callback.
     *         This lets the caller differentiate overlapping sync operations.
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
    public int applySyncTransaction(@NonNull WindowContainerTransaction t,
            @NonNull WindowContainerTransactionCallback callback) {
        try {
            return getWindowOrganizerController().applySyncTransaction(t, callback.mInterface);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

	......

    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
    IWindowOrganizerController getWindowOrganizerController() {
        return IWindowOrganizerControllerSingleton.get();
    }

    private static final Singleton<IWindowOrganizerController> IWindowOrganizerControllerSingleton =
            new Singleton<IWindowOrganizerController>() {
                @Override
                protected IWindowOrganizerController create() {
                    try {
                        return ActivityTaskManager.getService().getWindowOrganizerController();
                    } catch (RemoteException e) {
                        return null;
                    }
                }
            };
}

这两个方法,都是先取到一个IWindowOrganizerController对象,然后调用IWindowOrganizerController的相关接口。

IWindowOrganizerController在服务端的实现是WindowOrganizerController,因此最终WindowContainerTransaction跨进程发送到系统服务端的WindowOrganizerController。

WindowOrganizer#applyTransaction和WindowOrganizer#applySyncTransaction分别对应WindowOrganizerController#applyTransaction和WindowOrganizerController#applySyncTransaction:

/**
 * Server side implementation for the interface for organizing windows
 * @see android.window.WindowOrganizer
 */
class WindowOrganizerController extends IWindowOrganizerController.Stub
    implements BLASTSyncEngine.TransactionReadyListener {
    ...... 
    
	@Override
    public void applyTransaction(WindowContainerTransaction t) {
        enforceTaskPermission("applyTransaction()");
        if (t == null) {
            throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
        }
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                applyTransaction(t, -1 /*syncId*/, null /*transition*/);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @Override
    public int applySyncTransaction(WindowContainerTransaction t,
            IWindowContainerTransactionCallback callback) {
        enforceTaskPermission("applySyncTransaction()");
        if (t == null) {
            throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
        }
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                /**
                 * 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
                 * that to the given 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 callback. 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 callback.
                 */
                int syncId = -1;
                if (callback != null) {
                    syncId = startSyncWithOrganizer(callback);
                }
                applyTransaction(t, syncId, null /*transition*/);
                if (syncId >= 0) {
                    setSyncReady(syncId);
                }
                return syncId;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
    
    ......
}

WindowOrganizerController是应用WindowContainerTransaction的地方,以后会在其他笔记中分析。

从发送方式来看,可以把WindowContainerTransaction分为两种,同步的和异步的。

1)、异步的WindowContainerTransaction发送方式很简单,举一个例子,LegaycSplitScreenController#onSplitScreenSupported:

public void onSplitScreenSupported() {
    // Set starting tile bounds based on middle target
    final WindowContainerTransaction tct = new WindowContainerTransaction();
    int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
    mSplitLayout.resizeSplits(midPos, tct);
    mTaskOrganizer.applyTransaction(tct);
}

创建一个WindowContainerTransaction对象,设置好相关参数,直接调用WindowOrganizer#applyTransaction发送即可。

2)、同步WindowContainerTransaction的发送需要借助SyncTransactionQueue,最终会调用WindowOrganizer#applySyncTransaction,本文主要分析SyncTransactionQueue是如何发送同步WindowContainerTransaction的。

二、SyncTransactionQueue介绍

/**
 * Helper for serializing sync-transactions and corresponding callbacks.
 */
public final class SyncTransactionQueue

SyncTransactionQueue,一个用于序列化同步WindowContainerTransaction和相应callback的助手类,有两个关键词,一个是序列化,一个是同步。

SyncTransactionQueue也提供了两个方法来发送WCT,SyncTransactionQueue#queue和SyncTransactionQueue#queueIfWaiting。

结合分屏,看下SyncTransactionQueue是如何工作的。

1 SyncTransactionQueue#queue

分屏的逻辑中,SyncTransactionQueue#queue方法不是直接被调用的,而是间接通过WindowManagerProxy来调用,比如在LegacySplitScreenController#splitPrimaryTask中:

     final WindowContainerTransaction wct = new WindowContainerTransaction();
     // Clear out current windowing mode before reparenting to split task.
     wct.setWindowingMode(topRunningTask.token, WINDOWING_MODE_UNDEFINED);
     wct.reparent(topRunningTask.token, mSplits.mPrimary.token, true /* onTop */);
     mWindowManagerProxy.applySyncTransaction(wct);

我们创建了一个WindowContainerTransaction对象,然后设置了相关属性后,调用WindowManagerProxy#applySyncTransaction:

    /**
     * Utility to apply a sync transaction serially with other sync transactions.
     *
     * @see SyncTransactionQueue#queue
     */
    void applySyncTransaction(WindowContainerTransaction wct) {
        mSyncTransactionQueue.queue(wct);
    }

mSyncTransactionQueue是WindowManagerProxy的SyncTransactionQueue类型的成员变量,那么这里会走到SyncTransactionQueue#queue:

    /**
     * Queues a sync transaction to be sent serially to WM.
     */
    public void queue(WindowContainerTransaction wct) {
        SyncCallback cb = new SyncCallback(wct);
        synchronized (mQueue) {
            if (DEBUG) Slog.d(TAG, "Queueing up " + wct);
            mQueue.add(cb);
            if (mQueue.size() == 1) {
                cb.send();
            }
        }
    }

注释上说,这个方法将一个要序列化发送到WM的同步WindowContainerTransaction进行排队。

内容很简单,基于传入的WindowContainerTransaction创建一个SyncCallback对象,并加到mQueue队列中,如果队列中只有它一个,那么执行SyncCallback#send。

2 SyncTransactionQueue#queueIfWaiting

    /**
     * Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
     * Otherwise just returns without queueing.
     * @return {@code true} if queued, {@code false} if not.
     */
    public boolean queueIfWaiting(WindowContainerTransaction wct) {
        synchronized (mQueue) {
            if (mQueue.isEmpty()) {
                if (DEBUG) Slog.d(TAG, "Nothing in queue, so skip queueing up " + wct);
                return false;
            }
            if (DEBUG) Slog.d(TAG, "Queue is non-empty, so queueing up " + wct);
            SyncCallback cb = new SyncCallback(wct);
            mQueue.add(cb);
            if (mQueue.size() == 1) {
                cb.send();
            }
        }
        return true;
    }

和SyncTransactionQueue#queue的区别是,如果此时mQueue中没有正在等待的SyncCallback,那么直接返回,不进行排队。

3 SyncCallback

上面看到,如果我们想要发送一个WindowContainerTransaction,都是将WindowContainerTransaction封装到一个SyncCallback对象中,然后调用SyncCallback#send,那么有必要看下SyncCallback的作用是什么。

    private class SyncCallback extends WindowContainerTransactionCallback {
        int mId = -1;
        final WindowContainerTransaction mWCT;

        SyncCallback(WindowContainerTransaction wct) {
            mWCT = wct;
        }

        // Must be sychronized on mQueue
        void send() {
            if (mInFlight != null) {
                throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
                        + mInFlight.mId + " - " + mInFlight.mWCT);
            }
            mInFlight = this;
            if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
            mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
            if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
            mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
        }

        @BinderThread
        @Override
        public void onTransactionReady(int id,
                @NonNull SurfaceControl.Transaction t) {
            mMainExecutor.execute(() -> {
                synchronized (mQueue) {
                    if (mId != id) {
                        Slog.e(TAG, "Got an unexpected onTransactionReady. Expected "
                                + mId + " but got " + id);
                        return;
                    }
                    mInFlight = null;
                    mMainExecutor.removeCallbacks(mOnReplyTimeout);
                    if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
                    mQueue.remove(this);
                    onTransactionReceived(t);
                    if (!mQueue.isEmpty()) {
                        mQueue.get(0).send();
                    }
                }
            });
        }
    }

SyncCallback是一个内部类,创建的地方在SyncTransactionQueue#queue和SyncTransactionQueue#queueIfWaiting,存储了一个要发给WM 的WindowContainerTransaction对象。

3.1 SyncCallback#send

        // Must be sychronized on mQueue
        void send() {
            if (mInFlight != null) {
                throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
                        + mInFlight.mId + " - " + mInFlight.mWCT);
            }
            mInFlight = this;
            if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
            mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
            if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
            mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
        }

1)、调用WindowOrganizer#applySyncTransaction发送一个同步callback到WM端,由WindowOrganizerController处接收。最终从WM端会返回一个syncId,这个ID保存在SyncCallback的成员变量mId中,和SyncCallback一一对应。

2)、将mInFlight指向这个被发送到WM的callback。

3)、另外发送完之后开启一个防超时线程:

    private final Runnable mOnReplyTimeout = () -> {
        synchronized (mQueue) {
            if (mInFlight != null && mQueue.contains(mInFlight)) {
                Slog.w(TAG, "Sync Transaction timed-out: " + mInFlight.mWCT);
                mInFlight.onTransactionReady(mInFlight.mId, new SurfaceControl.Transaction());
            }
        }
    };

超时后调用SyncCallback#onTransactionReady,防止出现堵塞的情况。

3.2. SyncCallback#onTransactionReady

        @BinderThread
        @Override
        public void onTransactionReady(int id,
                @NonNull SurfaceControl.Transaction t) {
            mMainExecutor.execute(() -> {
                synchronized (mQueue) {
                    if (mId != id) {
                        Slog.e(TAG, "Got an unexpected onTransactionReady. Expected "
                                + mId + " but got " + id);
                        return;
                    }
                    mInFlight = null;
                    mMainExecutor.removeCallbacks(mOnReplyTimeout);
                    if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
                    mQueue.remove(this);
                    onTransactionReceived(t);
                    if (!mQueue.isEmpty()) {
                        mQueue.get(0).send();
                    }
                }
            });
        }

SyncCallback#send中通过WindowOrganizer#applySyncTransaction将WindowContainerTransaction发送给WM端后,WM端就开始应用WindowContainerTransaction,提取WindowContainerTransaction中的设置进行相应处理,这需要一段时间。等WM端处理结束,会由WindowOrganizerController#onTransactionReady回调SyncCallback#onTransactionReceived(具体情况可以参考BLAST机制介绍),并且返回一个集成了对多个WindowContainer操作的Transaction和一个ID。

SyncCallback#onTransactionReceived接收到这个Transaction和ID之后,做了以下几件事:

1)、检查ID的一致性。

2)、将mInFlight置空。

3)、移除掉防延迟mOnReplyTimeout。

4)、将该SyncCallback从mQueue中移除。

5)、调用SyncTransactionQueue#onTransactionReceived完成Transaction的apply。

6)、如果mQueue中还有其他正在等待的SyncCallback,调用他们的send方法。

4 SyncTransactionQueue#onTransactionReceived

// Synchronized on mQueue
private void onTransactionReceived(@NonNull SurfaceControl.Transaction t) {
    Slog.i("SyncTransactionQueue", "SyncTransactionQueue#onTransactionReceived");
    if (DEBUG) Slog.d(TAG, "  Running " + mRunnables.size() + " sync runnables");
    for (int i = 0, n = mRunnables.size(); i < n; ++i) {
        mRunnables.get(i).runWithTransaction(t);
    }
    mRunnables.clear();
    t.apply();
    t.close();
}

上一步提到了,当WM端完成了WindowContainerTransaction的应用后,会返回一个Transaction对象给SyncCallback#onTransactionReady,然后SyncCallback#onTransactionReady再调用SyncTransactionQueue#onTransactionReceived完成Transaction的apply。

SyncTransactionQueue#onTransactionReceive做了两件事:

1)、执行之前由SyncTransactionQueue#runInSync中添加到mRunnables的TransactionRunnable。

2)、调用Transaction#apply。

注意,直到这里,从WM端传来的Transaction才被apply,同步WindowContainerTransaction才算完成。

进退分屏的过程中,我们会通过WindowContainerTransaction为一些Task设置Bounds,执行一些Task的reparent操作等,这些操作都会引起Task的position、size等属性的改变,这些改变最终会收集到一个Transaction对象中。但是由于整个过程是在WindowOrganizerController#applySyncTransaction的调用中进行的,因此收集了Task的postion和size等变化的Transaction不会马上apply,而是等到所有相关的窗口绘制完成,然后全部merge到一个Transaction中,再由WindowOrganizerController#onTransactionReady发送到SyncCallback#onTransactionReady处,最终在SyncTransactionQueue#onTransactionReceived中完成apply。

这一点很容易导致问题。

三、总结

1)、发送WindowContainerTransaction的方式有两种,异步的WindowOrganizer#applyTransaction和同步的WindowOrganizer#applySyncTransaction。异步WindowContainerTransacton的发送很简单,直接拿到一个WindowOrganizer对象,调用其applyTransaction方法即可。同步WindowContainerTransaction的发送比较麻烦,不能直接调用WindowOrganizer#applySyncTransaction方法,需要借助SyncTransactionQueue。

2)、SyncTransactionQueue#queue和SyncTransactionQueue#queueIfWaiting是SyncTransactionQueue提供的两个用于发送同步WindowContainerTransaction的方法。

3)、通过SyncTransactionQueue发送的WindowContainerTransaction具有序列化和同步的特点。

4)、序列化表现在,在SyncTransactionQueue的参与下,所有的WindowContainerTransaction都会以序列化的方式发送:SyncTransactionQueue内部有一个SyncCallback的队列mQueue,要发送给WM端的WindowContainerTransaction会被封装为一个SyncCallback对象加入到mQueue中,然后按照SyncCallback入队的顺序调用SyncCallback#send方法进行发送,只有当前一个WindowContainerTransaction对应的SyncCallback#onTransactionReady回调完成,下一个WindowContainerTransaction对应的SyncCallback#send才会被调用。

5)、同步表现在,SyncTransactionQueue作为BLAST机制的一部分实现了同步:WindowContainerTransaction在WM端应用后,WM端会将所有涉及到的WindowContainer的修改合入到一个Transaction中,等待所有涉及的窗口绘制完成,再回调SyncCallback#onTransactionReady,最终在SyncTransactionQueue#onTransactionReceived中完成这个从WM端传过来的Transaction的apply,直到这时,同步WindowContainerTransaction应用的整个过程才算结束。

本文标签: androidSyncTransactionQueueWCT