system server 中分屏相关工作

140 阅读16分钟

分屏功能的实现需要和系统交互,这篇文章就跟分屏功能关联比较大的三个方面进行了介绍。分别是:

  • 提供Task操作及监听Task 状态的TaskOrganizerController
  • 提供窗口操作并能实现同步事务处理能力的WindowOrganizerController
  • 应用是如何启动在分屏栈的
  1. 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 的关系。

20250910-112728.jpg

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动画)等信息
  1. 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 事务的同步发送,接收事务处理完成后的回调。可以参考上面代码的注释。

20250910-112828.jpg

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 到下一帧绘制:

  1. 设置窗口属性
  2. 调用applyWithNextDraw
  3. 在应用绘制后的回调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):

20250910-113200.jpg

  1. 应用启动在分屏栈

下面是进入分屏时的窗口dump,从dump可以看出,两个分屏栈里各有一个应用,launcher 不进分屏栈。这里注意区别于低版本分屏,只要进分屏,主分屏栈里只有一个应用,其余应用包括launcher都会被移入次分屏栈。

20250910-113057.jpg

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,这样就实现了应用启动在分屏栈中。