第 24 章:显示系统
2026/9/2 7:55:56 网站建设 项目流程

Android 显示系统横跨三大进程:system_serversurfaceflinger以及客户端应用,同时衔接两套编程语言(框架层 Java、原生合成器 C++)。它的职责包含物理面板枚举发现、在精准的 VSYNC 垂直同步间隔调度帧刷新、将数百个图形图层合成为单路输出图像。本章将剖析全部核心子系统:负责显示器生命周期的 Java 层DisplayManagerService;管控窗口 Z 轴顺序的DisplayArea层级;从硬件中断一路到Choreographer的 VSYNC 流水线;屏幕旋转与折叠屏管理;显示挖孔与圆角处理;SurfaceFlinger前端重构与CompositionEngine;基于BLASTBufferQueue的缓冲区管理;虚拟显示器与镜像;色彩管理;显示器功耗控制。

已经阅读过第 13 章图形渲染流水线、第 20 章system_server架构的读者,会发现本章是前述基础在显示领域的自然延伸。

24.1 显示系统架构

24.1.1 三层模型

Android 显示子系统划分为 3 个独立层级,每层运行在不同进程与地址空间:

第 1 层 —— 框架层(system_server)DisplayManagerService掌管每一台显示器的生命周期。它通过各类DisplayAdapter实现发现物理显示器,创建LogicalDisplay对象映射物理DisplayDevice实例,并向WindowManagerService通知显示器新增、移除、配置变更事件。

第 2 层 —— 原生合成器(surfaceflinger)SurfaceFlinger接收SurfaceControl.Transaction下发的缓冲区更新,基于 VSYNC 调度合成工作;实际像素混合可以交由硬件合成器 HAL(Overlay 图层),或者 GPU(通过RenderEngine做客户端合成)。

第 3 层 —— 内核(DRM/KMS)Linux DRM 子系统管理显示硬件:模式设置、CRTC / 编码器 / 连接器拓扑,以及触发合成帧缓冲区扫描输出的 page‑flip ioctl。

24.1.2 DisplayManagerService

DisplayManagerService(DMS)是system_server启动阶段注册的系统服务。Android17 版本代码量超 7300 行,属于框架中体量最大的服务之一。其 Javadoc 对架构说明如下:

DisplayManagerService 管理显示器全局生命周期,基于当前接入的物理显示设备决定逻辑显示器如何配置;显示器状态发生变化时,向系统以及应用发送通知。

DMS 运行在DisplayThread(优先级THREAD_PRIORITY_DISPLAY的共享HandlerThread)。全部内部状态受唯一锁SyncRoot保护,所有显示器适配器、逻辑显示器对象均使用同一把锁:

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java private final SyncRoot mSyncRoot = new SyncRoot();

锁顺序约束至关重要:DMS 持有mSyncRoot时可以调用SurfaceFlinger(经由SurfaceControl);绝对不能在持有mSyncRoot的情况下调用WindowManagerService,因为 WMS 持有自身mGlobalLock并且可能回调 DMS。所有存在重入风险的外部调用全部通过 Handler 异步派发。

24.1.3 DisplayAdapter 架构

DMS 通过一组DisplayAdapter实现完成显示器发现。

LocalDisplayAdapter处理SurfaceFlinger热插拔机制上报的物理显示器(内置屏、外接屏),接收EVENT_ADDEVENT_REMOVEEVENT_CHANGE通知,创建绑定SurfaceFlinger显示令牌的LocalDisplayDevice实例。

VirtualDisplayAdapter代表应用创建虚拟显示器,接收携带尺寸、密度、标记、生命周期回调IVirtualDisplayCallbackVirtualDisplayConfig

WifiDisplayAdapter经由WifiDisplayController管理 Miracast(Wi‑Fi Display / WFD)连接。

OverlayDisplayAdapter读取Settings.Global.OVERLAY_DISPLAY_DEVICES配置,创建开发者叠加显示器。

全部适配器向DisplayDeviceRepository上报事件,该仓库保存权威的活跃DisplayDevice列表,并将变更通知 DMS。

24.1.4 LogicalDisplay 与物理映射

LogicalDisplayDisplayDevice的分离是核心设计。

  • LogicalDisplay:系统其余模块(窗口管理器、应用)看到的显示器抽象。
  • DisplayDevice:底层物理或者虚拟硬件实体。

LogicalDisplay文档关键设计描述:

逻辑显示器与显示设备是正交概念。逻辑显示器与显示设备之间存在映射关系,但可以是多对多,部分实例甚至可以不存在关联。

实际场景:普通单屏手机为 1:1 映射;折叠设备映射关系动态变化,折叠 / 展开过程中,同一个默认逻辑显示器(ID 0)可以在内屏、外屏物理设备之间切换;该切换逻辑由LogicalDisplayMapper管理。

24.1.5 显示器配置流程

显示器初次接入时,配置流程流经多个组件:

DMS 维护两套事件回调存储结构:

// 以调用进程pid为索引保存回调记录 private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>(); // 以[uid][pid]二维索引保存回调记录 private final SparseArray<SparseArray<CallbackRecord>> mCallbackRecordByPidByUid = new SparseArray<>();

事件通过MSG_DELIVER_DISPLAY_EVENT投递到 Handler,保证异步下发,执行时不持有mSyncRoot锁。

24.1.6 DisplayGroup 显示组

显示器归入DisplayGroup,同一组共享电源状态与亮度。主显示组包含内置显示屏;虚拟显示器可通过VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP创建独立组,或通过VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP归属设备默认显示组。DisplayGroupAllocator分配组 ID。

// LogicalDisplayMapper显示组相关事件 public static final int DISPLAY_GROUP_EVENT_ADDED = 1; public static final int DISPLAY_GROUP_EVENT_CHANGED = 2; public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;

显示组影响电源管理:默认显示组休眠时,组内所有显示器同时熄灭。

24.1.7 DisplayInfo 与覆盖机制

应用可见的DisplayInfo对象由多层覆盖机制逐层构建:

  1. 基础信息:取自主DisplayDeviceDisplayDeviceInfo(物理尺寸、密度、硬件支持模式)。
  2. DMS 覆盖:显示模式选择、用户关闭的 HDR 类型、帧率覆盖。
  3. WMS 覆盖:窗口管理器设置应用可见尺寸(处理过扫描、挖孔、旋转);调用setDisplayInfoOverrideFromWindowManagerLocked()写入。

DisplayInfoOverrides中常量WM_OVERRIDE_FIELDS明确定义 WMS 允许修改的字段,防止意外覆盖硬件原始数值。

24.1.8 DisplayBlanker:电源状态协调

DisplayBlanker接口作为DisplayPowerControllerSurfaceFlinger之间的桥梁,用于显示器电源状态变更。DMS 实现匿名内部类DisplayBlanker,协调多显示器状态切换。

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() { @Override public synchronized void requestDisplayState(int displayId, int state, float brightness, float sdrBrightness) { // Check if ALL displays are inactive or off boolean allInactive = true; boolean allOff = true; // ... iterate over mDisplayStates if (state == Display.STATE_OFF) { requestDisplayStateInternal(displayId, state, brightness, sdrBrightness); } if (stateChanged) { mDisplayPowerCallbacks.onDisplayStateChange(allInactive, allOff); } if (state != Display.STATE_OFF) { requestDisplayStateInternal(displayId, state, brightness, sdrBrightness); } } };

执行顺序有严格要求:关闭显示器流程,先设置显示器状态,再通知 PowerManager;点亮显示器流程,先通知 PowerManager,再设置显示器状态。避免系统认为屏幕已经点亮,但硬件实际还在掉电的竞态问题。

24.1.9 DisplayModeDirector 与投票系统

display/mode包下的DisplayModeDirector是框架侧策略引擎。接收多来源高层模式请求(应用setFrameRate调用、用户最大刷新率设置、性能提示、接近传感器、设备壳温),输出DesiredDisplayModeSpecs交给 DMS 下发给SurfaceFlinger

整体基于投票抽象:每个输入源在VotesStorage以固定优先级注册VoteVoteSummary对单台显示器的全部投票做冲突消解,输出一套尺寸、刷新率约束集合。

投票以数字优先级作为 key;VoteSummary做冲突仲裁,高优先级系统约束(热控、低功耗)可以缩小甚至否决应用这类低优先级源的请求。所有投票实体类(SizeVoteRefreshRateVoteSupportedRefreshRatesVoteRequestedRefreshRateVoteWorkDurationsVoteHdrPreferenceVote等)与DisplayModeDirector位于同一源码目录。

注意:真正基于该规格选择最终硬件模式的SurfaceFlinger侧 C++ 类RefreshRateSelector(24.3.6 节),框架层不会直接引用它。

24.1.10 Handler 消息协议

DMS 基于 Handler 消息完成异步操作:

消息常量用途
注册默认适配器MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS (1)开机阶段初始化适配器
注册额外适配器MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS (2)开机后动态注册适配器
下发显示器事件MSG_DELIVER_DISPLAY_EVENT (3)通知回调显示器变更
请求遍历更新MSG_REQUEST_TRAVERSAL (4)触发 SurfaceFlinger 显示器配置生效
更新视口MSG_UPDATE_VIEWPORT (5)更新输入视口映射
加载亮度配置MSG_LOAD_BRIGHTNESS_CONFIGURATIONS (6)加载亮度曲线参数
帧率覆盖事件MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE (7)通知帧率覆盖变更
显示组事件MSG_DELIVER_DISPLAY_GROUP_EVENT (8)通知显示组新增 / 移除
设备状态上报MSG_RECEIVED_DEVICE_STATE (9)处理折叠设备状态变更
批量派发待处理事件MSG_DISPATCH_PENDING_PROCESS_EVENTS (10)批量事件分发
下发显示器快照MSG_DELIVER_DISPLAY_SNAPSHOT (11)新注册回调一次性获取全部显示器当前状态

MSG_DELIVER_DISPLAY_SNAPSHOT用于让新注册监听器一次性拿到完整显示器集合,定义在DisplayManagerService.java第 314 行。

MSG_REQUEST_TRAVERSAL尤为重要:显示器配置变更后,DMS 必须调度SurfaceFlinger执行一次 traversal,应用新显示器参数(图层栈分配、显示器投影、显示模式)。

24.2 DisplayArea 层级树

24.2.1 什么是 DisplayArea

DisplayContent是代表一整个逻辑显示器的WindowContainer;在它之下,Android 把窗口组织为DisplayArea容器树。每一个DisplayArea把具备相同特性、处于同一 Z 轴区间的窗口归为一组。

类继承关系:

DisplayArea文档说明三种类型,保障 Z 轴顺序正确性:

DisplayArea 分为三类:

  • BELOW_TASKS:仅可存放 BELOW_TASKS 类型 DisplayArea 以及位于任务下层的 WindowToken
  • ABOVE_TASKS:仅可存放 ABOVE_TASKS 类型 DisplayArea 以及位于任务上层的 WindowToken
  • ANY:可以存放任意 DisplayArea、WindowToken 或者 Task 任务容器

24.2.2 Feature IDs 特性 ID

每个DisplayArea携带mFeatureId标识用途,标准 ID 定义于DisplayAreaOrganizer

Feature ID常量数值用途
FEATURE_ROOTFEATURE_SYSTEM_FIRST0层级树根节点
FEATURE_DEFAULT_TASK_CONTAINERFEATURE_SYSTEM_FIRST + 11Task 默认容器
FEATURE_WINDOW_TOKENSFEATURE_SYSTEM_FIRST + 22非 Task 类型 WindowToken 容器
FEATURE_ONE_HANDEDFEATURE_SYSTEM_FIRST + 33单手模式缩放
FEATURE_TOP_LEVEL_ZOOMFEATURE_SYSTEM_FIRST + 44顶层缩放层(AOSP 该特性叫 WindowedMagnification 窗口放大)
FEATURE_FULLSCREEN_MAGNIFICATIONFEATURE_SYSTEM_FIRST + 55全屏放大
FEATURE_HIDE_DISPLAY_CUTOUTFEATURE_SYSTEM_FIRST + 66挖孔下方内容区域
FEATURE_IME_PLACEHOLDERFEATURE_SYSTEM_FIRST + 77输入法占位容器位置
FEATURE_IMEFEATURE_SYSTEM_FIRST + 88输入法实际容器
FEATURE_WINDOWING_LAYERFEATURE_SYSTEM_FIRST + 99兜底窗口层
FEATURE_APP_ZOOM_OUTFEATURE_SYSTEM_FIRST + 1010应用缩小显示支持

厂商自定义特性 ID 从FEATURE_VENDOR_FIRST(10001)FEATURE_VENDOR_LAST(20001);OEM 可自定义节点,例如车载后排显示、双屏特性。

24.2.3 DisplayAreaPolicyBuilder

DisplayAreaPolicyBuilder接收一组 Feature 定义,构建中间DisplayArea节点,满足 Z 轴顺序约束。

AOSP 默认DefaultDisplayAreaPolicy构建层级示例:

构建器工作流程:

  1. 收集全部 Feature 定义,每个 Feature 面向一批窗口类型(例如 WindowedMagnification 覆盖到无障碍放大叠加层之下所有窗口)。
  2. 遍历每一个 Z 轴槽位(窗口常量定义的 36 层模型),判断哪些 Feature 生效。
  3. 在 Feature 边界与 Z 轴边界交叉位置创建中间DisplayArea节点,拆分树维持正确层级顺序。

24.2.4 DisplayAreaGroup 多根层级

构建器通过DisplayAreaGroup支持多根层级,对车载、折叠设备至关重要。示例代码来自DisplayAreaPolicyBuilder文档,创建前后排显示器独立根节点:

// Example from DisplayAreaPolicyBuilder Javadoc: RootDisplayArea firstRoot = new RootDisplayArea(wmService, "FirstRoot", FEATURE_FIRST_ROOT); DisplayAreaPolicyBuilder.HierarchyBuilder firstGroupHierarchy = new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot) .setTaskDisplayAreas(firstTdaList); return new DisplayAreaPolicyBuilder() .setRootHierarchy(rootHierarchy) .addDisplayAreaGroupHierarchy(firstGroupHierarchy) .setSelectRootForWindowFunc(selectRootForWindowFunc) .build(wmService, content);

selectRootForWindowFuncBiFunction<Integer, Bundle, RootDisplayArea>,依据窗口类型与启动参数,将每一个WindowToken路由到对应根节点。

24.2.5 层级校验规则

DisplayAreaPolicyBuilder.validate()对层级施加严格结构约束:

  1. Root 与 TDA ID 全局唯一:每一个RootDisplayAreaTaskDisplayArea必须拥有全局唯一 featureId。
  2. 同一根下 feature ID 唯一:同一个RootDisplayArea子节点 ID 不可重复;不同根节点可以复用相同 ID,实现跨根组织者。
  3. 输入法容器唯一:层级构建器必须存在且仅有一个输入法容器。
  4. 默认 TDA 唯一:有且仅有一个TaskDisplayAreaID 等于FEATURE_DEFAULT_TASK_CONTAINER
  5. ID 范围上限:ID 不能超过FEATURE_VENDOR_LAST(20001)
  6. 合法窗口层:根层级顶层必须包含窗口层(FEATURE_TOP_LEVEL_ZOOMFEATURE_WINDOWING_LAYER);缺失则构建器自动插入FEATURE_WINDOWING_LAYER
// frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java if (!mRootHierarchyBuilder.hasValidWindowingLayer()) { mRootHierarchyBuilder.mFeatures.add(0 /* top level index */, new Feature.Builder(wmService.mPolicy, "WindowingLayer", FEATURE_WINDOWING_LAYER) .setExcludeRoundedCornerOverlay(false).all().build()); }

24.2.6 Feature 定义与窗口类型匹配

每一个 Feature 使用 Builder 模式指定作用窗口类型,支持区间与排除:

// Example: WindowedMagnification targets everything below // the accessibility magnification overlay new Feature.Builder(wmService.mPolicy, "WindowedMagnification", FEATURE_TOP_LEVEL_ZOOM) .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new) .build()

Feature.Builder 方法说明:

  • all():匹配全部窗口类型
  • upTo(type):匹配从最低到该类型(包含该类型)
  • except(type):从集合排除指定类型
  • and(type):向集合追加指定窗口类型
  • setNewDisplayAreaSupplier():自定义 DisplayArea 工厂(例如 Dimmable 用于放大变暗)
  • setExcludeRoundedCornerOverlay():是否排除圆角叠加窗口

24.2.7 构建算法

HierarchyBuilder.build()实现生成 DisplayArea 树的核心算法:

算法保证:

  • 连续 Z 轴区间、且 Feature 集合完全一致的范围,对应一个 DisplayArea。
  • 只覆盖部分 Z 轴区间的 Feature 生成嵌套 DisplayArea 节点。
  • TaskDisplayArea 精确插入在APPLICATION_LAYER(任务下层窗口与任务上层窗口之间 Z 轴位置)。

24.2.8 DefaultSelectRootForWindowFunction

多根场景(车载前排 / 后排屏),DefaultSelectRootForWindowFunction完成窗口 token 路由:

// frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java public RootDisplayArea apply(Integer windowType, Bundle options) { if (mDisplayAreaGroupRoots.isEmpty()) { return mDisplayRoot; } if (options != null) { final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID, FEATURE_UNDEFINED); if (rootId != FEATURE_UNDEFINED) { for (RootDisplayArea root : mDisplayAreaGroupRoots) { if (root.mFeatureId == rootId) return root; } } } return mDisplayRoot; }

路由 key 为ActivityOptionsbundle 中的KEY_ROOT_DISPLAY_AREA_ID,启动器、系统组件可以通过该参数将窗口定向到指定根节点。

24.2.9 DisplayArea Organizers

Shell、SystemUI 可以注册IDisplayAreaOrganizer,接收特定 Feature 的 DisplayArea 出现、变更、消失回调。依靠该机制实现:

  • 单手模式:注册FEATURE_ONE_HANDED,对 DisplayArea 做缩放、平移。
  • 窗口放大:绑定FEATURE_TOP_LEVEL_ZOOM的 WindowedMagnification 特性。
  • 应用缩小:注册FEATURE_APP_ZOOM_OUT

DisplayAreaOrganizerController管理注册,分发onDisplayAreaAppearedonDisplayAreaInfoChangedonDisplayAreaVanished回调。Organizer 会拿到SurfaceControlleash,可以重父或者做变换。

24.2.10 DisplayArea 中的方向处理

DisplayArea通过mSetIgnoreOrientationRequest标志在方向管理中起到关键作用。开启后,该 DisplayArea 会忽略下层应用固定方向请求,应用以黑边(letterbox)形式展示,而不去旋转整块屏幕。

// frameworks/base/services/core/java/com/android/server/wm/DisplayArea.java boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) { if (mSetIgnoreOrientationRequest == ignoreOrientationRequest) { return false; } mSetIgnoreOrientationRequest = ignoreOrientationRequest; // Check whether we should notify Display to update orientation // ... }

该逻辑用于大屏设

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询