【Android 12】【WCT的发送】SyncTransact
一、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应用的整个过程才算结束。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。