尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

第 24 章:显示系统

第 24 章:显示系统 Android 显示系统横跨三大进程:system_server、surfaceflinger以及客户端应用,同时衔接两套编程语言(框架层 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 DisplayManagerServiceDisplayManagerService(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_ADD、EVENT_REMOVE、EVENT_CHANGE通知,创建绑定SurfaceFlinger显示令牌的LocalDisplayDevice实例。VirtualDisplayAdapter代表应用创建虚拟显示器,接收携带尺寸、密度、标记、生命周期回调IVirtualDisplayCallback的VirtualDisplayConfig。WifiDisplayAdapter经由WifiDisplayController管理 Miracast(Wi‑Fi Display / WFD)连接。OverlayDisplayAdapter读取Settings.Global.OVERLAY_DISPLAY_DEVICES配置,创建开发者叠加显示器。全部适配器向DisplayDeviceRepository上报事件,该仓库保存权威的活跃DisplayDevice列表,并将变更通知 DMS。24.1.4 LogicalDisplay 与物理映射LogicalDisplay与DisplayDevice的分离是核心设计。LogicalDisplay:系统其余模块(窗口管理器、应用)看到的显示器抽象。DisplayDevice:底层物理或者虚拟硬件实体。LogicalDisplay文档关键设计描述:逻辑显示器与显示设备是正交概念。逻辑显示器与显示设备之间存在映射关系,但可以是多对多,部分实例甚至可以不存在关联。实际场景:普通单屏手机为 1:1 映射;折叠设备映射关系动态变化,折叠 / 展开过程中,同一个默认逻辑显示器(ID 0)可以在内屏、外屏物理设备之间切换;该切换逻辑由LogicalDisplayMapper管理。24.1.5 显示器配置流程显示器初次接入时,配置流程流经多个组件:DMS 维护两套事件回调存储结构:// 以调用进程pid为索引保存回调记录 private final SparseArrayCallbackRecord mCallbacks = new SparseArray(); // 以[uid][pid]二维索引保存回调记录 private final SparseArraySparseArrayCallbackRecord 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对象由多层覆盖机制逐层构建:基础信息:取自主DisplayDevice的DisplayDeviceInfo(物理尺寸、密度、硬件支持模式)。DMS 覆盖:显示模式选择、用户关闭的 HDR 类型、帧率覆盖。WMS 覆盖:窗口管理器设置应用可见尺寸(处理过扫描、挖孔、旋转);调用setDisplayInfoOverrideFromWindowManagerLocked()写入。DisplayInfoOverrides中常量WM_OVERRIDE_FIELDS明确定义 WMS 允许修改的字段,防止意外覆盖硬件原始数值。24.1.8 DisplayBlanker:电源状态协调DisplayBlanker接口作为DisplayPowerController与SurfaceFlinger之间的桥梁,用于显示器电源状态变更。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以固定优先级注册Vote;VoteSummary对单台显示器的全部投票做冲突消解,输出一套尺寸、刷新率约束集合。投票以数字优先级作为 key;VoteSummary做冲突仲裁,高优先级系统约束(热控、低功耗)可以缩小甚至否决应用这类低优先级源的请求。所有投票实体类(SizeVote、RefreshRateVote、SupportedRefreshRatesVote、RequestedRefreshRateVote、WorkDurationsVote、HdrPreferenceVote等)与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 什么是 DisplayAreaDisplayContent是代表一整个逻辑显示器的WindowContainer;在它之下,Android 把窗口组织为DisplayArea容器树。每一个DisplayArea把具备相同特性、处于同一 Z 轴区间的窗口归为一组。类继承关系:DisplayArea文档说明三种类型,保障 Z 轴顺序正确性:DisplayArea 分为三类:BELOW_TASKS:仅可存放 BELOW_TASKS 类型 DisplayArea 以及位于任务下层的 WindowTokenABOVE_TASKS:仅可存放 ABOVE_TASKS 类型 DisplayArea 以及位于任务上层的 WindowTokenANY:可以存放任意 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 DisplayAreaPolicyBuilderDisplayAreaPolicyBuilder接收一组 Feature 定义,构建中间DisplayArea节点,满足 Z 轴顺序约束。AOSP 默认DefaultDisplayAreaPolicy构建层级示例:构建器工作流程:收集全部 Feature 定义,每个 Feature 面向一批窗口类型(例如 WindowedMagnification 覆盖到无障碍放大叠加层之下所有窗口)。遍历每一个 Z 轴槽位(窗口常量定义的 36 层模型),判断哪些 Feature 生效。在 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);selectRootForWindowFunc是BiFunctionInteger, Bundle, RootDisplayArea,依据窗口类型与启动参数,将每一个WindowToken路由到对应根节点。24.2.5 层级校验规则DisplayAreaPolicyBuilder.validate()对层级施加严格结构约束:Root 与 TDA ID 全局唯一:每一个RootDisplayArea、TaskDisplayArea必须拥有全局唯一 featureId。同一根下 feature ID 唯一:同一个RootDisplayArea子节点 ID 不可重复;不同根节点可以复用相同 ID,实现跨根组织者。输入法容器唯一:层级构建器必须存在且仅有一个输入法容器。默认 TDA 唯一:有且仅有一个TaskDisplayAreaID 等于FEATURE_DEFAULT_TASK_CONTAINER。ID 范围上限:ID 不能超过FEATURE_VENDOR_LAST(20001)。合法窗口层:根层级顶层必须包含窗口层(FEATURE_TOP_LEVEL_ZOOM或FEATURE_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 OrganizersShell、SystemUI 可以注册IDisplayAreaOrganizer,接收特定 Feature 的 DisplayArea 出现、变更、消失回调。依靠该机制实现:单手模式:注册FEATURE_ONE_HANDED,对 DisplayArea 做缩放、平移。窗口放大:绑定FEATURE_TOP_LEVEL_ZOOM的 WindowedMagnification 特性。应用缩小:注册FEATURE_APP_ZOOM_OUT。DisplayAreaOrganizerController管理注册,分发onDisplayAreaAppeared、onDisplayAreaInfoChanged、onDisplayAreaVanished回调。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 // ... }该逻辑用于大屏设
返回列表