AOSP 13 分屏源码分析
AOSP 13 分屏源码分析
本文档基于 AOSP 13 源码,梳理系统级分屏(Split Screen)的架构、核心类、进入/退出流程及调试方法。
1. 概述
AOSP 13 的分屏实现已从传统 SystemUI View 迁移到WM Shell(WindowManager Shell)。
- SystemUI:托管 Shell 进程、桥接 Launcher,不包含分屏核心 UI 逻辑
- WM Shell:Stage 管理、分割线、窗口 bounds、过渡动画
- Launcher3 Quickstep:分屏选择 UI、动画、跨进程调用 Shell
- system_server:Task 树、
WINDOWING_MODE_MULTI_WINDOW、应用WindowContainerTransaction
注意:SystemUI 中的
SplitShade*、SplitClockView是通知栏分屏布局,与多窗口分屏无关。WindowManager/Jetpack/下的SplitPairRule属于Activity Embedding(应用内分屏),与系统分屏是两套机制。
2. 整体架构
| 层级 | 职责 |
|---|---|
| Launcher3 | 分屏选择 UI、动画、调用ISplitScreen.startTasks() |
| SystemUI | 启动 WM Shell,把ISplitScreenBinder 传给 Launcher |
| WM Shell | Stage 管理、分割线、窗口 bounds、过渡动画 |
| system_server | Task 树、WINDOWING_MODE_MULTI_WINDOW、应用 WCT |
3. 核心目录
| 路径 | 内容 |
|---|---|
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ | 分屏核心逻辑 |
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/ | 分割线、布局计算 |
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/ | 拖拽进入分屏 |
packages/apps/Launcher3/quickstep/ | 最近任务分屏 UI 与状态机 |
frameworks/base/services/core/java/com/android/server/wm/ | Task/Window 策略 |
frameworks/base/packages/SystemUI/src/com/android/systemui/wmshell/ | Shell 生命周期托管 |
4. Task 层级模型
StageCoordinator定义了分屏的核心规则:
- SideStage 有子任务→ 分屏才算激活
- MainStage 有子任务→ 仅在 Coordinator 激活时
- MainStage 与 SideStage 都可见→ 分割线才显示
- 两个 Stage 放在同一个single-top root task下
实际窗口树结构:
Display └── Split Root Task (single-top, mRootTaskInfo) ├── MainStage root (WINDOWING_MODE_MULTI_WINDOW) ← 默认启动目标 │ └── App Task ├── SideStage root (WINDOWING_MODE_MULTI_WINDOW) ← 用户显式选中的任务 │ └── App Task └── Divider (TYPE_DOCK_DIVIDER, DividerView)启动时通过ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN指定任务进入哪个 Stage。
5. 关键类职责
5.1 WM Shell 层
| 类 | 文件 | 职责 |
|---|---|---|
SplitScreenController | splitscreen/SplitScreenController.java | 顶层入口,实现ISplitScreenAIDL |
StageCoordinator | splitscreen/StageCoordinator.java | 核心编排器:WCT 构建、进入/退出、Stage 协调 |
MainStage | splitscreen/MainStage.java | 主 Stage,默认 launch 目标 |
SideStage | splitscreen/SideStage.java | 副 Stage,用户显式 pin 的任务 |
StageTaskListener | splitscreen/StageTaskListener.java | 通过ShellTaskOrganizer创建MULTI_WINDOWroot task |
SplitLayout | common/split/SplitLayout.java | 计算 bounds、分割线位置、拖拽 dismiss |
DividerView | common/split/DividerView.java | 分割线触摸(拖动/双击) |
SplitWindowManager | common/split/SplitWindowManager.java | 托管TYPE_DOCK_DIVIDER窗口 |
SplitScreenTransitions | splitscreen/SplitScreenTransitions.java | 分屏过渡动画 |
DragAndDropController | draganddrop/DragAndDropController.java | 拖拽进入分屏 |
5.2 Launcher 层
| 类 | 文件 | 职责 |
|---|---|---|
SplitSelectStateController | util/SplitSelectStateController.java | 分屏选择状态机,记录第一个/第二个 App |
RecentsView | views/RecentsView.java | initiateSplitSelect()/confirmSplitSelect() |
SystemUiProxy | SystemUiProxy.java | 封装ISplitScreen跨进程调用 |
SplitScreenSelectState | uioverrides/states/SplitScreenSelectState.java | Overview 中选第二个 App 时的 UI 状态 |
SplitShortcut.kt | splitscreen/SplitShortcut.kt | 桌面/All Apps 长按分屏 |
GroupedTaskView | views/GroupedTaskView.java | 重新启动已有分屏对 |
TopTaskTracker | TopTaskTracker.java | 实现ISplitScreenListener,跟踪 Stage 任务 |
5.3 SystemUI 层
| 类 | 文件 | 职责 |
|---|---|---|
SystemUIService | SystemUIService.java | SystemUI 进程启动,间接启动 WMShell |
WMShell | wmshell/WMShell.java | Shell 生命周期桥接(唤醒、全屏退出) |
SystemUIInitializer | SystemUIInitializer.java | 构建WMComponent,注入 SplitScreen |
5.4 system_server 层
| 类 | 文件 | 职责 |
|---|---|---|
TaskOrganizerController | server/wm/TaskOrganizerController.java | Shell task organizer IPC |
WindowOrganizerController | server/wm/WindowOrganizerController.java | 应用 WCT(reparent、bounds、launch) |
TaskDisplayArea | server/wm/TaskDisplayArea.java | 创建 multi-window root task |
RecentTasks | server/wm/RecentTasks.java | 分屏对分组显示在最近任务 |
6. 跨进程 API:ISplitScreen.aidl
Launcher 与 Shell 通过 AIDL 通信,Binder key 为KEY_EXTRA_SHELL_SPLIT_SCREEN。
核心方法:
// 同时启动两个 task 进入分屏onewayvoidstartTasks(inttaskId1,inBundleoptions1,inttaskId2,inBundleoptions2,intsplitPosition,floatsplitRatio,inRemoteTransitionremoteTransition,inInstanceIdinstanceId);// 启动 Intent + TaskonewayvoidstartIntentAndTask(inPendingIntentpendingIntent,inBundleoptions1,inttaskId,inBundleoptions2,intsidePosition,floatsplitRatio,inRemoteTransitionremoteTransition,inInstanceIdinstanceId);// 退出分屏onewayvoidexitSplitScreen(inttoTopTaskId);// 注册监听onewayvoidregisterSplitScreenListener(inISplitScreenListenerlistener);文件路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
7. 进入分屏流程
7.1 触发方式
| 方式 | 入口 |
|---|---|
| 最近任务菜单 | TaskShortcutFactory→TaskView.initiateSplitSelect() |
| Overview 卡片 Split 按钮 | RecentsView.initiateSplitSelect(TaskView) |
| 桌面/All Apps 长按 | SplitShortcut.kt→SplitScreenSelectState |
| Taskbar 拖拽 | DragAndDropController→DragAndDropPolicy |
| 键盘快捷键 | SplitWithKeyboardShortcutController |
| 重新启动已有分屏对 | GroupedTaskView.launchTasks() |
| 通知拖拽 | DragAndDropController同一套流程 |
| ADB 调试 | SplitScreenShellCommandHandler(cmd splitscreen) |
7.2 两 App 选择流程(最常见)
Step 1 — 选第一个 App
RecentsView.initiateSplitSelect() → SplitSelectStateController.setInitialTaskSelect() → Launcher 进入 SplitScreenSelectState(显示第一个 App 占位 UI)Step 2 — 选第二个 App
RecentsView.confirmSplitSelect() → SplitSelectStateController.launchSplitTasks() → launchTasks(taskId1, intent1, taskId2, intent2, stagePosition, ...)Step 3 — Launcher → Shell
SystemUiProxy.startTasks(taskId1, options1, taskId2, options2, splitPosition, splitRatio, remoteTransition, instanceId) → ISplitScreen.startTasks() [Binder 跨进程]Step 4 — Shell 构建 WCT
StageCoordinator.startTasks()核心逻辑:
setSideStagePosition(splitPosition, wct)— 设置 SideStage 位置(上/左 或 下/右)- 第一个 task → SideStage:
addActivityOptions(options1, mSideStage)+wct.startTask(taskId1, options1) startWithTask()处理第二个 task:mMainStage.activate(wct, false)— 激活 MainStagemSplitLayout.setDivideRatio(splitRatio)— 设置分割比例updateWindowBounds(mSplitLayout, wct)— 计算各 Stage bounds- 第二个 task → MainStage:
addActivityOptions(mainOptions, mMainStage)+wct.startTask(mainTaskId, mainOptions) SplitScreenTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_PAIR_OPEN, ...)
Step 5 — system_server 应用 WCT
WindowOrganizerController.applyTransaction(wct) → Task reparent 到 WINDOWING_MODE_MULTI_WINDOW root → TaskOrganizerController 回调 Shell(ShellTaskOrganizer)Step 6 — 完成进入
StageCoordinator.finishEnterSplitScreen():
mSplitLayout.init()— 初始化 boundssetDividerVisibility(true, t)— 显示分割线updateSurfaceBounds()— 更新 Surface boundsupdateRecentTasksSplitPair()— 注册分屏对到 RecentTasks
7.3 拖拽进入分屏
用户拖拽 App 图标 → DragAndDropController 显示 DragLayout(分屏区域) → DragAndDropPolicy 解析目标(左/右/上/下) → SplitScreenController.startIntent() / startTask() → StageCoordinator.prepareEnterSplitScreen() + launch7.4 将已有 Task 移入分屏
SplitScreenController.enterSplitScreen(taskId, leftOrTop) → moveToStage() → prepareEnterSplitScreen(wct, taskInfo, startPosition)8. 退出分屏
常见退出原因(SplitScreenController定义):
| 常量 | 含义 |
|---|---|
EXIT_REASON_DRAG_DIVIDER | 拖动分割线超过阈值 dismiss 一侧 |
EXIT_REASON_RETURN_HOME | 返回桌面 |
EXIT_REASON_APP_FINISHED | App 结束 |
EXIT_REASON_DEVICE_FOLDED | 折叠屏折叠 |
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW | App 不支持多窗口 |
EXIT_REASON_CHILD_TASK_ENTER_PIP | 子任务进入 PiP |
EXIT_REASON_FULLSCREEN_SHORTCUT | 全屏快捷键 |
EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP | 锁屏 show-on-top |
EXIT_REASON_ROOT_TASK_VANISHED | Root task 消失 |
9. 类关系图
SplitScreen (interface) ↑ implemented by SplitScreenController ├── StageCoordinator (core logic) │ ├── MainStage extends StageTaskListener │ ├── SideStage extends StageTaskListener │ ├── SplitLayout implements SplitLayoutHandler │ │ └── SplitWindowManager → DividerView │ └── SplitScreenTransitions ├── DragAndDropController (drag-to-split) └── ISplitScreenImpl (AIDL stub) StageTaskListener └── ShellTaskOrganizer.createRootTask(MULTI_WINDOW) ↔ TaskOrganizerController (system_server) Launcher side: SplitSelectStateController → SystemUiProxy → ISplitScreen TopTaskTracker extends ISplitScreenListener RecentsView ↔ SplitSelectStateController ↔ SplitScreenSelectState10. 调试方法
10.1 ADB 命令
adb shell cmd splitscreenhelp10.2 Logcat 过滤
adb logcat-sStageCoordinator SplitScreenController SplitSelectStateController10.3 排查优先级
| 问题类型 | 优先查看 |
|---|---|
| 分屏行为、WCT | StageCoordinator.java |
| 用户入口、动画 | SplitSelectStateController.java |
| 分割线交互 | SplitLayout.java+DividerView.java |
| Task 树策略 | TaskOrganizerController.java/WindowOrganizerController.java |
| Launcher 绑定 | SystemUiProxy.java+TouchInteractionService.java |
11. 关键源码索引
| 功能 | 文件 | 关键方法 |
|---|---|---|
| 启动两个 task | StageCoordinator.java | startTasks() |
| 完成进入分屏 | StageCoordinator.java | finishEnterSplitScreen() |
| 准备进入分屏 | StageCoordinator.java | prepareEnterSplitScreen() |
| Launcher 发起分屏 | SplitSelectStateController.java | launchSplitTasks()/launchTasks() |
| 跨进程调用 | SystemUiProxy.java | startTasks() |
| 分屏 API 定义 | ISplitScreen.aidl | startTasks(),exitSplitScreen() |
| 布局计算 | SplitLayout.java | init(),setDivideRatio() |
| 分割线 UI | DividerView.java | 触摸 drag / double-tap |
| Shell 托管 | WMShell.java | initSplitScreen() |
