分屏功能的实现需要和系统交互,这篇文章就跟分屏功能关联比较大的三个方面进行了介绍。分别是:
- 提供Task操作及监听Task 状态的TaskOrganizerController
- 提供窗口操作并能实现同步事务处理能力的WindowOrganizerController
- 应用是如何启动在分屏栈的
-
TaskOrganizerController
我们知道Task 是system server 中Activity的容器,Task 的管理都是在服务端,那么TaskOrganizerController类主要提供给客户端,如SystemUI ,远程操作Task 的方法, 如创建RootTask、获取task info、监听Task 的状态等。
分屏中也是使用TaskOrganizerController创建分屏栈,监听分屏栈的变化从而调度分屏的进入和退出,我们先来看下TaskOrganizerController的主要成员和方法吧~
1.1 TaskOrganizerController 对象创建及重要组成
TaskOrganizerController虽然不是单例模式,但是system server 中只创建一个实例,在WindowOrganizerController构造中创建,WindowOrganizerController会在第二章介绍。
- 创建对象
/frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
WindowOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
mGlobalLock = atm.mGlobalLock;
mTaskOrganizerController = new TaskOrganizerController(mService);
mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm, this);
mTransitionController = new TransitionController(atm);
}
- binder服务端
TaskOrganizerController继承ITaskOrganizerController.Stub ,是binder服务端。
class TaskOrganizerController extends ITaskOrganizerController.Stub
分屏逻辑中,SystemUI 主要使用了下面的远程调用
/frameworks/base/core/java/android/window/ITaskOrganizerController.aidl
interface ITaskOrganizerController {
/**
* Register a TaskOrganizer to manage all the tasks with supported windowing modes.
*
* @return a list of the tasks that should be managed by the organizer, not including tasks
* created via {@link #createRootTask}.
*/
ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer);
/**
* Unregisters a previously registered task organizer.
*/
void unregisterTaskOrganizer(ITaskOrganizer organizer);
/** Creates a persistent root task in WM for a particular windowing-mode. */
void createRootTask(int displayId, int windowingMode, IBinder launchCookie,
boolean removeWithTaskOrganizer);
/** Deletes a persistent root task in WM */
boolean deleteRootTask(in WindowContainerToken task);
在SystemUI 创建时使用 createRootTask创建分屏栈,使用registerTaskOrganizer注册分屏栈监听。
- binder客户端
同时封装了ITaskOrganizer代理对象,当SystemUI关注的task 有变化时,回调SystemUI中方法,
private static class TaskOrganizerCallbacks {
final ITaskOrganizer mTaskOrganizer;
void onTaskAppeared(Task task) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId);
final RunningTaskInfo taskInfo = task.getTaskInfo();
try {
mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task,
"TaskOrganizerController.onTaskAppeared"));
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onTaskAppeared callback", e);
}
}
void onTaskVanished(Task task) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId);
final RunningTaskInfo taskInfo = task.getTaskInfo();
try {
mTaskOrganizer.onTaskVanished(taskInfo);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onTaskVanished callback", e);
}
}
void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) {
if (!task.mTaskAppearedSent) {
// Skip if the task has not yet received taskAppeared().
return;
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId);
if (!task.isOrganized()) {
// This is safe to ignore if the task is no longer organized
return;
}
try {
// Purposely notify of task info change immediately instead of deferring (like
// appear and vanish) to allow info changes (such as new PIP params) to flow
// without waiting.
mTaskOrganizer.onTaskInfoChanged(taskInfo);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e);
}
}
... ...
}
- 内部类TaskOrganizerPendingEventsQueue
Task 有变化需要给应用回调时,不会直接回调
TaskOrganizerPendingEventsQueue主要用于ITaskOrganizer中的方法,而是首先要放到TaskOrganizerPendingEventsQueue中的一个pending 队列中,在窗口绘制时统一派发,这样做的原因是,task change可能会被频繁触发,那么经过TaskOrganizerPendingEventsQueue的add 和dispatch逻辑可以避免同一次窗口绘制中回调接口(如onTaskInfoChanged)被调用多次。
-
TaskOrganizerPendingEventsQueue与ITaskOrganizer代理对象一一对应
- TaskOrganizerPendingEventsQueue中定义了一TaskOrganizerState对象,一个TaskOrganizerState又对应一个TaskOrganizerCallbacks对象,前面说过TaskOrganizerCallbacks中封装了ITaskOrganizer代理对象。
-
对应一个事件队列mPendingTaskEvents
可以看出 一个TaskOrganizerPendingEventsQueue对象负责派发一个TaskOrganizer的事件。
static final class TaskOrganizerPendingEventsQueue {
private final TaskOrganizerState mOrganizerState;
private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>();
void addPendingTaskEvent(PendingTaskEvent event) {
mPendingTaskEvents.add(event);
}
@Nullable
private PendingTaskEvent getPendingTaskEvent(Task task, int type) {
for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
PendingTaskEvent entry = mPendingTaskEvents.get(i);
if (task.mTaskId == entry.mTask.mTaskId && type == entry.mEventType) {
return entry;
}
}
return null;
}
void dispatchPendingEvents() {
if (mPendingTaskEvents.isEmpty()) {
return;
}
for (int i = 0, n = mPendingTaskEvents.size(); i < n; i++) {
dispatchPendingEvent(mPendingTaskEvents.get(i));
}
mPendingTaskEvents.clear();
}
}
-
总结
- System server 中只有一个TaskOrganizerController对象,用于提供系统应用对task 操作的方法
- TaskOrganizerController作为binder 服务端提供创建task 、注册监听等远程方法
- TaskOrganizerController作为binder 客户端回调应用 onTaskInfoChanged 等接口
- 使用TaskOrganizerPendingEventsQueue,防止重复回调和统一回调时机
1.2 交互逻辑
以注册registerTaskOrganizer和中发送客户端回调onTaskInfoChanged为例,分析下TaskOrganizerController中流程。
- registerTaskOrganizer
主要就是往 mTaskOrganizers 和 mTaskOrganizerStates中添加 ITaskOrganizer
@Override
public ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer) {
enforceTaskPermission("registerTaskOrganizer()");
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
final Runnable withGlobalLock = () -> {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
organizer.asBinder(), uid);
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
mTaskOrganizers.add(organizer);
mTaskOrganizerStates.put(organizer.asBinder(),
new TaskOrganizerState(organizer, uid));
}
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
mService.mRootWindowContainer.forAllTasks((task) -> {
boolean returnTask = !task.mCreatedByOrganizer;
//将监听对象ITaskOrganizer更新到task 中,
//如果已经被创建过returnTask为true,则不会调用onTaskAppeared
task.updateTaskOrganizerState(returnTask /* skipTaskAppeared */);
// It is possible for the task to not yet have a surface control, so ensure that
// the update succeeded in setting the organizer for the task before returning
if (task.isOrganized() && returnTask) {
SurfaceControl taskLeash = state.addTaskWithoutCallback(task,
"TaskOrganizerController.registerTaskOrganizer");
taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), taskLeash));
}
});
};
... ...
}
TaskOrganizerState 类中主要有一个TaskOrganizerCallbacks对象,即ITaskOrganizer代理对象,和对应的事件队列TaskOrganizerPendingEventsQueue,这里就可以理解为TaskOrganizerState封装了一个远程TaskOrganizer的相关信息,包括回调对象、关心的task集合以及事件队列。
多个TaskOrganizerState对象的情况,比如Launcher 使用TaskView 会通过TaskOrganizerController创建root task 及注册回调,systemui 分屏同样需要创建分屏栈及监听,这样对应Launcher、SystemUI 在服务端就有两个TaskOrganizerState对象,所以system server 中维护的是一个TaskOrganizerState list.
class TaskOrganizerState {
private final TaskOrganizerCallbacks mOrganizer;
private final ArrayList<Task> mOrganizedTasks = new ArrayList<>();
private final TaskOrganizerPendingEventsQueue mPendingEventsQueue;
private final int mUid;
- onTaskAppeared
进入分屏,应用task 进分屏栈时,会走一次onTaskAppeared
我们可以打开log开关:
adb shell wm logging enable-text WM_DEBUG_WINDOW_ORGANIZER
Task 11 和 24 分别是上下应用的分屏栈
08-05 11:29:52.308 627 650 V WindowManager: Task info changed taskId=4
08-05 11:29:52.308 627 650 V WindowManager: Task info changed taskId=2
08-05 11:29:52.308 627 650 V WindowManager: Task info changed taskId=3
08-05 11:29:52.445 627 660 V WindowManager: Task appeared taskId=24
08-05 11:29:52.445 627 660 V WindowManager: Task info changed taskId=4
08-05 11:29:52.449 627 660 V WindowManager: Task info changed taskId=2
08-05 11:29:52.449 627 660 V WindowManager: Task appeared taskId=11
08-05 11:29:52.449 627 660 V WindowManager: Task info changed taskId=1
08-05 11:29:52.449 627 660 V WindowManager: Task info changed taskId=3
08-05 11:29:52.452 627 650 V WindowManager: Task info changed taskId=11
08-05 11:29:52.585 627 660 V WindowManager: Task info changed taskId=1
08-05 11:29:52.683 627 650 V WindowManager: Task info changed taskId=4
08-05 11:29:52.683 627 650 V WindowManager: Task info changed taskId=11
08-05 11:29:52.683 627 650 V WindowManager: Task info changed taskId=2
08-05 11:29:52.683 627 650 V WindowManager: Task info changed taskId=3
08-05 11:29:52.683 627 650 V WindowManager: Task info changed taskId=24
08-05 11:29:52.782 627 2043 V WindowManager: Task info changed taskId=3
08-05 11:29:52.782 627 2043 V WindowManager: Task info changed taskId=24
08-05 11:29:52.782 627 2043 V WindowManager: Task info changed taskId=2
08-05 11:29:52.782 627 2043 V WindowManager: Task info changed taskId=4
08-05 11:29:52.782 627 2043 V WindowManager: Task info changed taskId=11
08-05 11:29:52.820 627 2043 V WindowManager: Task info changed taskId=24
08-05 11:29:52.820 627 2043 V WindowManager: Task info changed taskId=11
对应task dump:
下面我们来看下onTaskAppeared的调用流程:
进入分屏后从Task.sendTaskAppeared发送回调事件,注意这里mTaskOrganizer判空条件
/frameworks/base/services/core/java/com/android/server/wm/Task.java
private void sendTaskAppeared() {
if (mTaskOrganizer != null) {
mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this);
}
}
setTaskOrganizer 中设置的mTaskOrganizer, 是在应用注册监听的时候registerTaskOrganizer,设置给应用所关心的task的,这里的关心就是由应用创建的task及其children task.如果一个task 被多个应用“关心”,那么他的mTaskOrganizer值会被赋值给最后调用registerTaskOrganizer的应用的ITaskOrganizer代理对象。
该task 可以被监听的条件就是canBeOrganized()为true.即这个task 是由 TaskOrganizerController.createRootTask 创建的,或者parent是mCreatedByOrganizer
private boolean canBeOrganized() {
// All root tasks can be organized
if (isRootTask() || mCreatedByOrganizer) {
return true;
}
// Task could be organized if it's the direct child of a task created by organizer.
final Task parentTask = getParent().asTask();
return parentTask != null && parentTask.mCreatedByOrganizer;
}
- 总结
整体交互逻辑比较简单,这里只要注意下task 的变化的回调是发给“关心”(创建/指定window mode)他的监听应用。下图大概描述了TaskOrganizerController与应用进程和wms 中的task 的关系。
1.3 分屏中使用
前面两个小节我们知道了TaskOrganizerController的作用及工作原理,那么我们继续看下它在分屏中的作用。
-
registerOrganizer
-
ShellTaskOrganizer
- wmshell中ShellTaskOrganizer 继承TaskOrganizer,通过TaskOrganizer调用服务端方法registerTaskOrganizer。ShellTaskOrganizer对象使用dagger依赖注入方式,而且是单例模式,在SystemUI 进程起来时创建。
-
/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
-
@WMSingleton @Provides static ShellTaskOrganizer provideShellTaskOrganizer( Context context, ShellInit shellInit, ShellCommandHandler shellCommandHandler, CompatUIController compatUI, Optional<UnfoldAnimationController> unfoldAnimationController, Optional<RecentTasksController> recentTasksOptional, @ShellMainThread ShellExecutor mainExecutor ) { if (!context.getResources().getBoolean(R.bool.config_registerShellTaskOrganizerOnInit)) { // TODO(b/238217847): Force override shell init if registration is disabled shellInit = new ShellInit(mainExecutor); } return new ShellTaskOrganizer(shellInit, shellCommandHandler, compatUI, unfoldAnimationController, recentTasksOptional, mainExecutor); }
-
ShellTaskOrganizer初始化时注册registerOrganizer
/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@Override
public List<TaskAppearedInfo> registerOrganizer() {
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Registering organizer");
final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
for (int i = 0; i < taskInfos.size(); i++) {
final TaskAppearedInfo info = taskInfos.get(i);
ProtoLog.v(WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
onTaskAppeared(info);
}
return taskInfos;
}
}
- createRootTask
SystemUI 进程启动时,会创建一个全屏栈,两个分屏栈,用于分屏。全屏栈是两个分屏栈的parent,上面的栈监听也是针对这三个root task 及他们的children.
/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider, ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
... ...
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
... ...
/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
SurfaceSession surfaceSession, IconProvider iconProvider) {
mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mSurfaceSession = surfaceSession;
mIconProvider = iconProvider;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
- onTaskAppeared
在root task 被创建或者有应用task 启动到分屏栈时被回调,这里主要是完成了对mRootTaskInfo等分屏栈信息的赋值,触发显示divider ui 以及分屏栈的子task 的reparent工作。
/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
//分屏栈root task 被创建,开始对root task info等变量赋值
if (mRootTaskInfo == null) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
mSplitDecorManager = new SplitDecorManager(
mRootTaskInfo.configuration,
mIconProvider,
mSurfaceSession);
mCallbacks.onRootTaskAppeared();
sendStatusChanged();
mSyncQueue.runInSync(t -> mDimLayer =
SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
//应用栈被移入分屏栈
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
final int taskId = taskInfo.taskId;
mChildrenLeashes.put(taskId, leash);
mChildrenTaskInfo.put(taskId, taskInfo);
mCallbacks.onChildTaskStatusChanged(taskId, true /* present */,
taskInfo.isVisible && taskInfo.isVisibleRequested);
if (ENABLE_SHELL_TRANSITIONS) {
// Status is managed/synchronized by the transition lifecycle.
return;
}
updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
//重点是这里,开始进入分屏逻辑,包括显示devider,整理分屏栈的children
mCallbacks.onChildTaskAppeared(taskId);
sendStatusChanged();
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
}
- onTaskInfoChanged
这个回调被触发的时机很多,包括focus 变化、可见性变化等。onTaskInfoChanged方法中主要关注的是task的可见性变化,这里就不过多关注了
- onTaskVanished
wms中Task的 mTaskOrganizer被重新赋值时回调onTaskVanished,表示此task 已经有新的监听方了,不再受控制了,SystemUI 中收到此回调,如果是分屏root task 则触发退出分屏,如果是children task ,则从children task 中移除。
-
总结:
- SystemUI 中的TaskOrganizer是单例,所以一个进程所创建的root task 都可以监听到
- 在方法onTaskXXX中主要是完成及进入或退出分屏、调整分屏栈等逻辑
- onTaskXXX方法中可以获取taskinfo,包括task包名、top activity、SurfaceControl对象(可用于task动画)等信息
-
WindowOrganizerController
与TaskOrganizerController类似,WindowOrganizerController提供给应用操作窗口的接口。WindowOrganizerController在system server 中也是单例,在ActivityManagerService构造中创建对象。这里我们只关注分屏中使用的WindowOrganizerController.applySyncTransaction方法流程。
2.1 SystemUI 发送事务
Wmshell 中对applySyncTransaction的使用进行了封装SyncTransactionQueue,实现了对窗口操作的事务串行执行,即一个事务执行完后(收到wms 事务已经准备好的回调),再执行下一个,有点像有序广播。
下面是对SyncTransactionQueue常见使用,比如分屏退出场景
private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop,
WindowContainerTransaction wct, @ExitReason int exitReason) {
... ...
if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
setRootForceTranslucent(true, wct);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
onTransitionAnimationComplete();
} else {
// Expand to top side split as full screen for fading out decor animation and dismiss
// another side split(Moving its children to bottom).
mIsExiting = true;
childrenToTop.resetBounds(wct);
wct.reorder(childrenToTop.mRootTaskInfo.token, true);
}
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
false /* reparentLeafTaskIfRelaunch */);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
t.setWindowCrop(mMainStage.mRootLeash, null)
.setWindowCrop(mSideStage.mRootLeash, null);
t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer);
setDividerVisibility(false, t);
... ...
-
WindowContainerTransaction对象
- 一个WindowContainerTransaction对象包含多个对窗口的操作,这里调用了reorder、reparent调整了分屏栈的位置
-
SyncTransactionQueue.queue
将wct(WindowContainerTransaction)入队,这里可以理解为准备发送wct
-
mSyncQueue.runInSync
- 在这次窗口事务发送到WMS,WMS和分屏应用都处理完后执行的操作,比如隐藏divider等。
看下SyncTransactionQueue中比较重要的成员变量
/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
/**
* Helper for serializing sync-transactions and corresponding callbacks.
*/
public final class SyncTransactionQueue {
private static final boolean DEBUG = false;
private static final String TAG = "SyncTransactionQueue";
//发送同步transaction超时,比如超过5300应用或wms 还没有处理完发送的wct 请求,就不等了
// Just a little longer than the sync-engine timeout of 5s
private static final int REPLY_TIMEOUT = 5300;
private final TransactionPool mTransactionPool;
private final ShellExecutor mMainExecutor;
//SyncCallback list,超过一个请求时,不直接发送,入队等待前面请求处理完再出队send
// Sync Transactions currently don't support nesting or interleaving properly, so
// queue up transactions to run them serially.
private final ArrayList<SyncCallback> mQueue = new ArrayList<>();
private SyncCallback mInFlight = null;
//调用runInSync将任务入队,等待wms回调后在出队执行这里的Runnable
private final ArrayList<TransactionRunnable> mRunnables = new ArrayList<>();
//发送wct,接收onTransactionReady回调
private class SyncCallback extends WindowContainerTransactionCallback {
int mId = -1;
final WindowContainerTransaction mWCT;
// Must be sychronized on mQueue
//调用applySyncTransaction发送wct
void send() {
if (mInFlight == this) {
// This was probably queued up and sent during a sync runnable of the last callback.
// Don't queue it again.
return;
}
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);
if (mLegacyTransition != null) {
mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
mLegacyTransition.getAdapter(), this, mWCT);
} else {
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用于判断当前是不是有正在执行的事务
mInFlight = null;
mMainExecutor.removeCallbacks(mOnReplyTimeout);
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
mQueue.remove(this);
//取出mRunnables所有任务执行
onTransactionReceived(t);
if (mLegacyTransition != null) {
try {
mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
} catch (RemoteException e) {
Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
}
} else {
t.apply();
t.close();
}
//还有没发送的事务,继续发送
if (!mQueue.isEmpty()) {
mQueue.get(0).send();
}
}
});
}
}
SyncTransactionQueue中流程就不一一分析了,他的主要作用就是实现wct 事务的同步发送,接收事务处理完成后的回调。可以参考上面代码的注释。
2.2 WMS中处理事务
继续看SystemUI 调用applySyncTransaction后面的工作,由于applySyncTransaction整个流程涉及应用绘制的调度、窗口属性设置、同步事务管理等流程, 涉及代码比较多,我们这里只挑选重点逻辑,以免迷失在代码里。
2.2.1 WMS同步事务处理的准备工作
/frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
public int applySyncTransaction(WindowContainerTransaction t,
IWindowContainerTransactionCallback callback) {
... ...
final BLASTSyncEngine.SyncGroup syncGroup = prepareSyncWithOrganizer(callback);
final int syncId = syncGroup.mSyncId;
if (mTransitionController.isShellTransitionsEnabled()) {
mTransitionController.startLegacySyncOrQueue(syncGroup, (deferred) -> {
applyTransaction(t, syncId, null /* transition */, caller, deferred);
setSyncReady(syncId);
});
} else {
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.
mService.mWindowManager.mSyncEngine.queueSyncSet(
() -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup),
() -> {
applyTransaction(t, syncId, null /*transition*/, caller);
setSyncReady(syncId);
});
}
}
return syncId;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
创建一个BLASTSyncEngine.SyncGroup对象,每次发送同步事务,在服务端会有一个syncId和SyncGroup对应.
这里出现了BLASTSyncEngine,BLASTSyncEngine用于收集每次同步事务请求的WindowContainers信息,保存同步事务请求状态如、以及merge应用端和wms的SurfaceControl.Transaction并发送给SystemUI.
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller,
@Nullable Transition finishTransition) {
int effects = TRANSACT_EFFECTS_NONE;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
try {
if (transition != null) {
transition.applyDisplayChangeIfNeeded();
}
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
final int hopSize = hops.size();
final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
t.getChanges().entrySet().iterator();
while (entries.hasNext()) {
// Make sure we add to the syncSet before performing
// operations so we don't end up splitting effects between the WM
// pending transaction and the BLASTSync transaction.
//
//如果是同步申请syncId >= 0,这里会将本次申请有变化的窗口放入SyncGroup中,设置窗口flag
//并触发重绘
if (syncId >= 0) {
addToSyncSet(syncId, wc);
}
... ...
}
// Hierarchy changes
if (hopSize > 0) {
final boolean isInLockTaskMode = mService.isInLockTaskMode();
for (int i = 0; i < hopSize; ++i) {
//调用wms中方法实现窗口操作
effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
isInLockTaskMode, caller, t.getErrorCallbackToken(),
t.getTaskFragmentOrganizer(), finishTransition);
}
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
// this after hierarchy ops so we have the final organized state.
entries = t.getChanges().entrySet().iterator();
while (entries.hasNext()) {
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
... ...
//只能对被监控的task进行操作,不是对任何task 都可以操作的
if (!task.isOrganized()) {
final Task parent = task.getParent() != null ? task.getParent().asTask() : null;
// Also allow direct children of created-by-organizer tasks to be
// controlled. In the future, these will become organized anyways.
if (parent == null || !parent.mCreatedByOrganizer) {
throw new IllegalArgumentException(
"Can't manipulate non-organized task surface " + task);
}
}
final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
final SurfaceControl sc = task.getSurfaceControl();
sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);
if (surfaceBounds.isEmpty()) {
sft.setWindowCrop(sc, null);
} else {
sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());
}
//将上面对task属性的设置后的transaction merge 到该task 对应的窗口上面
task.setMainWindowSizeChangeTransaction(sft);
}
... ...
}
applyTransaction方法包含了wms中的关键流程:
-
addToSyncSet
- 将本次同步事务修改的WindowContainer 放入对应的SyncGroup对象
- 遍历调用wc.prepareSync(),设置窗口flag SYNC_STATE_WAITING_FOR_DRAW,表示窗口等待应用绘制,等应用绘制结束,通知wms后,该属性再被设为SYNC_STATE_READY。
- 调用requestRedrawForSync 触发应用重新绘制
-
applyHierarchyOp
设置窗口/task 属性,如reparent操作会直接调用task.reparent
-
task.setMainWindowSizeChangeTransaction(sft)
- 调用applyWithNextDraw,将task 的transaction合并到窗口的下一次绘制后的transaction
/**
* Apply the transaction with the next window redraw. A full relayout/finishDrawing
* cycle must occur before completion. This means if you call the function while
* "in relayout", the results may be undefined but at all other times the function
* should sort of transparently work like this:
* 1. Make changes to WM hierarchy (say change app configuration)
* 2. Call applyWithNextDraw
* 3. After finishDrawing, our consumer will be passed the Transaction
* containing the buffer, and we can merge in additional operations.
* See {@link WindowState#mDrawHandlers}
*/
void applyWithNextDraw(Consumer<SurfaceControl.Transaction> consumer) {
if (mSyncState != SYNC_STATE_NONE) {
Slog.w(TAG, "applyWithNextDraw with mSyncState=" + mSyncState + ", " + this
+ ", " + Debug.getCallers(8));
}
mSyncSeqId++;
mDrawHandlers.add(new DrawHandler(mSyncSeqId, consumer));
requestRedrawForSync();
mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
BLAST_TIMEOUT_DURATION);
}
上面方法的注释写的特别好,总结了如何实现应用窗口/task 的transaction 到下一帧绘制:
- 设置窗口属性
- 调用applyWithNextDraw
- 在应用绘制后的回调finishDrawing,合并第一步带有窗口属性的transation
2.2.2 应用端的同步处理
上面流程中会调用requestRedrawForSync修改重绘flag,窗口绘制遍历时会调用应用的resized触发应用重绘.
在应用进程我们需要做下面3件事情:
- 获取带有绘制内容的transaction
- 阻拦应用的buffer送往SF合成
- 将transaction发送到system server
resized后在绘制的时候会触发relayoutWindow,wms 的relayoutWindow 会传mSyncSeqId,就是前面说的同步id
/frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
... ...
relayoutResult = mWindowSession.relayout(mWindow, params,
requestedWidth, requestedHeight, viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
mTempInsets, mTempControls, mRelayoutBundle);
mRelayoutRequested = true;
final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid");
if (maybeSyncSeqId > 0) {
mSyncSeqId = maybeSyncSeqId;
}
... ...
由于上面wms 传的sync id,mReportNextDraw会被赋值为true
private void createSyncIfNeeded() {
// WMS requested sync already started or there's nothing needing to sync
if (isInWMSRequestedSync() || !mReportNextDraw) {
return;
}
final int seqId = mSyncSeqId;
mWmsRequestSyncGroupState = WMS_SYNC_PENDING;
mWmsRequestSyncGroup = new SurfaceSyncGroup("wmsSync-" + mTag, t -> {
mWmsRequestSyncGroupState = WMS_SYNC_MERGED;
//mWmsRequestSyncGroup及其子SurfaceSyncGroup都ready后回调
reportDrawFinished(t, seqId);
});
//这里是重点,这里调用add 会走到getOrCreateSurfaceSyncGroup(),触发后面的监听绘制逻辑
mWmsRequestSyncGroup.add(this, null /* runnable */);
}
以上方法重点是创建了mWmsRequestSyncGroup对象,及通过add调用getOrCreateSurfaceSyncGroup,创建了mActiveSurfaceSyncGroup对象
在主线程绘制前由于 mActiveSurfaceSyncGroup != null, 开始监听RT绘制
private boolean performDraw() {
... ...
addFrameCommitCallbackIfNeeded();
boolean usingAsyncReport = isHardwareEnabled() && mActiveSurfaceSyncGroup != null;
if (usingAsyncReport) {
registerCallbacksForSync(mSyncBuffer, mActiveSurfaceSyncGroup);
} else if (mHasPendingTransactions) {
// These callbacks are only needed if there's no sync involved and there were calls to
// applyTransactionOnDraw. These callbacks check if the draw failed for any reason and
// apply those transactions directly so they don't get stuck forever.
registerCallbackForPendingTransactions();
}
... ...
}
registerCallbacksForSync如代码中的注释,主要是通过监听绘制完成,拿到了带有绘制内容的transaction
private void registerCallbacksForSync(boolean syncBuffer,
final SurfaceSyncGroup surfaceSyncGroup) {
Transaction t = new Transaction();
t.merge(mPendingTransaction);
mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
//RenderThread开始绘制收到此回调
@Override
public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
mergeWithNextTransaction(t, frame);
if (syncBuffer) {
//向BBQ注册一个回调,
//当绘制结束BBQ会将带有绘制内容的transaction通过这个回调接口发送过来
boolean result = mBlastBufferQueue.syncNextTransaction(transaction -> {
//接收到回调后,
//将带有绘制内容的transaction merge到SurfaceSyncGroup.mTransaction
surfaceSyncGroup.addTransaction(transaction);
//将mActiveSurfaceSyncGroup状态置为ready
surfaceSyncGroup.markSyncReady();
});
... ...
}
再来简单看下BLASTBufferQueue 中的逻辑,上面调用了 syncNextTransaction,有一个函数对象的参数,当绘制完成,BLASTBufferQueue acquire buffer 后回调,即将带有绘制能容的transaction 传给了主线程。
queueBuffer 后调用onFrameAvailable通知BufferQueue消费,这里BBQ调用acquireNextBufferLocked拿到buffer后没有直接给SF 送显,而是发回了应用主线程
/frameworks/native/libs/gui/BLASTBufferQueue.cpp
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
... ...
if (syncTransactionSet) {
while (acquireNextBufferLocked(mSyncTransaction) == BufferQueue::NO_BUFFER_AVAILABLE) {
BQA_LOGD("waiting for available buffer");
mCallbackCV.wait(_lock);
}
// Only need a commit callback when syncing to ensure the buffer that's synced has been
// sent to SF
incStrong((void*)transactionCommittedCallbackThunk);
mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
static_cast<void*>(this));
if (mAcquireSingleBuffer) {
//syncNextTransaction传过来的回调
prevCallback = mTransactionReadyCallback;
prevTransaction = mSyncTransaction;
mTransactionReadyCallback = nullptr;
mSyncTransaction = nullptr;
}
} else if (!waitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
}
}
if (prevCallback) {
//syncNextTransaction
prevCallback(prevTransaction);
}
}
这种场景applyTransaction为false,不会继续apply,即不会发给SF 合成
status_t BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
... ...
if (applyTransaction) {
// All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
t->setApplyToken(mApplyToken).apply(false, true);
mAppliedLastTransaction = true;
mLastAppliedFrameNumber = bufferItem.mFrameNumber;
} else {
t->setBufferHasBarrier(mSurfaceControl, mLastAppliedFrameNumber);
mAppliedLastTransaction = false;
}
我们再回忆下前面说的3件事:
- 获取带有绘制内容的transaction
- 阻拦应用的buffer送往SF合成
- 将transaction发送到system server
现在完成两件了,mWmsRequestSyncGroup、mActiveSurfaceSyncGroup状态都为ready 后会调用reportDrawFinished(t, seqId), 在前面的createSyncIfNeeded方法中提到过这点,reportDrawFinished最后调用wms的finishDrawing将transaction 传给wms.
private void reportDrawFinished(@Nullable Transaction t, int seqId) {
try {
mWindowSession.finishDrawing(mWindow, t, seqId);
} catch (RemoteException e) {
... ...
}
自此完成了将应用绘制内容的事务传给wms,并且拦截了SF合成。主要流程如下:
- relayoutWindow 通过wms拿到sync id,以此来判断是否需要同步事务处理
- 创建了mWmsRequestSyncGroup对象及mActiveSurfaceSyncGroup对象,mActiveSurfaceSyncGroup作为mWmsRequestSyncGroup孩子
- registerCallbacksForSync中监听绘制状态,通过BLASTBufferQueue拿到绘制后的事务并拦截SF合成
- 通过finishDrawing将绘制后的事务传给wms
2.2.3 发送同步事务到SystemUI
/frameworks/base/services/core/java/com/android/server/wm/WindowState.java
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction, int syncSeqId) {
... ...
//将之前设置的窗口和task属性设merge到postDrawTransaction
final boolean hasSyncHandlers = executeDrawHandlers(postDrawTransaction, syncSeqId);
// Control the timing to switch the appearance of window with different rotations.
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
if (asyncRotationController != null
&& asyncRotationController.handleFinishDrawing(this, postDrawTransaction)) {
// Consume the transaction because the controller will apply it with fade animation.
// Layout is not needed because the window will be hidden by the fade leash.
postDrawTransaction = null;
skipLayout = true;
} else if (syncActive) {
// Currently in a Sync that is using BLAST.
if (!syncStillPending) {
layoutNeeded = onSyncFinishedDrawing();
}
if (postDrawTransaction != null) {
//将带有应用绘制内容和窗口属性的transaction merge到mSyncTransaction
mSyncTransaction.merge(postDrawTransaction);
// Consume the transaction because the sync group will merge it.
postDrawTransaction = null;
}
} else if (useBLASTSync()) {
// Sync that is not using BLAST
layoutNeeded = onSyncFinishedDrawing();
}
... ...
}
finishDrawing经过两次merge,现在mSyncTransaction拥有了应用绘制内容及窗口属性,下一步要发送到SystemUI 了。
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacementNoTrace() {
... ...
mWmService.mSyncEngine.onSurfacePlacement();
... ...
}
每次窗口绘制都会调用BLASTSyncEngine.onSurfacePlacement()去收集各个窗口的同步事务mSyncTransaction
/frameworks/base/services/core/java/com/android/server/wm/BLASTSyncEngine.java
void onSurfacePlacement() {
mTmpFinishQueue.addAll(mActiveSyncs);
while (!mTmpFinishQueue.isEmpty()) {
--visitBounds;
final SyncGroup group = mTmpFinishQueue.remove(0);
final int grpIdx = mActiveSyncs.indexOf(group);
// Skip if it's already finished:
if (grpIdx < 0) continue;
if (!group.tryFinish()) continue;
... ...
遍历SyncGroup list 调用tryFinish(), tryFinish()主要是检查状态然后调用finishNow merge本次同步事务相关窗口的transaction,调用SystemUI 的onTransactionReady方法传送事务,该事务由SystemUI apply.
private void finishNow() {
SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
//遍历窗口调用finishSync,将各个窗口的同步事务merge到一个事务merged
for (WindowContainer wc : mRootMembers) {
wc.finishSync(merged, this, false /* cancel */);
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
mListener.onTransactionReady(mSyncId, merged);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
mActiveSyncs.remove(this);
}
到此终于完成了同步事务的发送和接收全部流程,由于服务端流程比较复杂,重点方法调用请参考以下时序图:
整个事务的流转流程图如下(图中TX代表transactionX):
-
应用启动在分屏栈
下面是进入分屏时的窗口dump,从dump可以看出,两个分屏栈里各有一个应用,launcher 不进分屏栈。这里注意区别于低版本分屏,只要进分屏,主分屏栈里只有一个应用,其余应用包括launcher都会被移入次分屏栈。
Android 11 是根据window mode:
WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
wms 或SystemUI 触发task.reparent 把应用栈移动到分屏栈,现在已经不用上面两个window mode,
分屏只有一个window mode:
WINDOWING_MODE_MULTI_WINDOW
应用如果想启动在分屏栈上,需要在启动时传入的root task的token
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
private void addActivityOptions(Bundle opts, @Nullable StageTaskListener launchTarget) {
if (launchTarget != null) {
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, launchTarget.mRootTaskInfo.token);
}
// Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
// will be canceled.
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
}
WMS中给应用找启动栈时会检查这个属性
/frameworks/base/core/java/android/app/ActivityOptions.java
mLaunchRootTask = opts.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, android.window.WindowContainerToken.class);
public WindowContainerToken getLaunchRootTask() {
return mLaunchRootTask;
}
下面为在应用启动复用栈或者新建栈的流程,由于我们只关心怎么启动在分屏栈,这里就不一一贴代码了。
- 栈复用
- 新建栈
从上面流程可以看出不管启动Activity 流程中是复用栈还是新建栈,最后都是通过ActivityOptions.getLaunchRootTask获取了SystemUI 设置的分屏栈作为root task,这样就实现了应用启动在分屏栈中。