)
Android 顯示系統(tǒng)橫跨三大進程:system_server、surfaceflinger以及客戶端應用,同時銜接兩套編程語言(框架層 Java、原生合成器 C++)。它的職責包含物理面板枚舉發(fā)現(xiàn)、在精準的 VSYNC 垂直同步間隔調度幀刷新、將數(shù)百個圖形圖層合成為單路輸出圖像。本章將剖析全部核心子系統(tǒng):負責顯示器生命周期的 Java 層DisplayManagerService;管控窗口 Z 軸順序的DisplayArea層級;從硬件中斷一路到Choreographer的 VSYNC 流水線;屏幕旋轉與折疊屏管理;顯示挖孔與圓角處理;SurfaceFlinger前端重構與CompositionEngine;基于BLASTBufferQueue的緩沖區(qū)管理;虛擬顯示器與鏡像;色彩管理;顯示器功耗控制。已經閱讀過第 13 章圖形渲染流水線、第 20 章system_server架構的讀者,會發(fā)現(xiàn)本章是前述基礎在顯示領域的自然延伸。24.1 顯示系統(tǒng)架構24.1.1 三層模型Android 顯示子系統(tǒng)劃分為 3 個獨立層級,每層運行在不同進程與地址空間:第 1 層 —— 框架層(system_server)DisplayManagerService掌管每一臺顯示器的生命周期。它通過各類DisplayAdapter實現(xiàn)發(fā)現(xiàn)物理顯示器,創(chuàng)建LogicalDisplay對象映射物理DisplayDevice實例,并向WindowManagerService通知顯示器新增、移除、配置變更事件。第 2 層 —— 原生合成器(surfaceflinger)SurfaceFlinger接收SurfaceControl.Transaction下發(fā)的緩沖區(qū)更新,基于 VSYNC 調度合成工作;實際像素混合可以交由硬件合成器 HAL(Overlay 圖層),或者 GPU(通過RenderEngine做客戶端合成)。第 3 層 —— 內核(DRM/KMS)Linux DRM 子系統(tǒng)管理顯示硬件:模式設置、CRTC / 編碼器 / 連接器拓撲,以及觸發(fā)合成幀緩沖區(qū)掃描輸出的 page?flip ioctl。24.1.2 DisplayManagerServiceDisplayManagerService(DMS)是system_server啟動階段注冊的系統(tǒng)服務。Android17 版本代碼量超 7300 行,屬于框架中體量最大的服務之一。其 Javadoc 對架構說明如下:DisplayManagerService 管理顯示器全局生命周期,基于當前接入的物理顯示設備決定邏輯顯示器如何配置;顯示器狀態(tài)發(fā)生變化時,向系統(tǒng)以及應用發(fā)送通知。DMS 運行在DisplayThread(優(yōu)先級THREAD_PRIORITY_DISPLAY的共享HandlerThread)。全部內部狀態(tài)受唯一鎖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 異步派發(fā)。24.1.3 DisplayAdapter 架構DMS 通過一組DisplayAdapter實現(xiàn)完成顯示器發(fā)現(xiàn)。LocalDisplayAdapter處理SurfaceFlinger熱插拔機制上報的物理顯示器(內置屏、外接屏),接收EVENT_ADD、EVENT_REMOVE、EVENT_CHANGE通知,創(chuàng)建綁定SurfaceFlinger顯示令牌的LocalDisplayDevice實例。VirtualDisplayAdapter代表應用創(chuàng)建虛擬顯示器,接收攜帶尺寸、密度、標記、生命周期回調IVirtualDisplayCallback的VirtualDisplayConfig。WifiDisplayAdapter經由WifiDisplayController管理 Miracast(Wi?Fi Display / WFD)連接。OverlayDisplayAdapter讀取Settings.Global.OVERLAY_DISPLAY_DEVICES配置,創(chuàng)建開發(fā)者疊加顯示器。全部適配器向DisplayDeviceRepository上報事件,該倉庫保存權威的活躍DisplayDevice列表,并將變更通知 DMS。24.1.4 LogicalDisplay 與物理映射LogicalDisplay與DisplayDevice的分離是核心設計。LogicalDisplay:系統(tǒng)其余模塊(窗口管理器、應用)看到的顯示器抽象。DisplayDevice:底層物理或者虛擬硬件實體。LogicalDisplay文檔關鍵設計描述:邏輯顯示器與顯示設備是正交概念。邏輯顯示器與顯示設備之間存在映射關系,但可以是多對多,部分實例甚至可以不存在關聯(lián)。實際場景:普通單屏手機為 1:1 映射;折疊設備映射關系動態(tài)變化,折疊 / 展開過程中,同一個默認邏輯顯示器(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,保證異步下發(fā),執(zhí)行時不持有mSyncRoot鎖。24.1.6 DisplayGroup 顯示組顯示器歸入DisplayGroup,同一組共享電源狀態(tài)與亮度。主顯示組包含內置顯示屏;虛擬顯示器可通過VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP創(chuàng)建獨立組,或通過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 允許修改的字段,防止意外覆蓋硬件原始數(shù)值。24.1.8 DisplayBlanker:電源狀態(tài)協(xié)調DisplayBlanker接口作為DisplayPowerController與SurfaceFlinger之間的橋梁,用于顯示器電源狀態(tài)變更。DMS 實現(xiàn)匿名內部類DisplayBlanker,協(xié)調多顯示器狀態(tài)切換。// 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); } } };執(zhí)行順序有嚴格要求:關閉顯示器流程,先設置顯示器狀態(tài),再通知 PowerManager;點亮顯示器流程,先通知 PowerManager,再設置顯示器狀態(tài)。避免系統(tǒng)認為屏幕已經點亮,但硬件實際還在掉電的競態(tài)問題。24.1.9 DisplayModeDirector 與投票系統(tǒng)display/mode包下的DisplayModeDirector是框架側策略引擎。接收多來源高層模式請求(應用setFrameRate調用、用戶最大刷新率設置、性能提示、接近傳感器、設備殼溫),輸出DesiredDisplayModeSpecs交給 DMS 下發(fā)給SurfaceFlinger。整體基于投票抽象:每個輸入源在VotesStorage以固定優(yōu)先級注冊Vote;VoteSummary對單臺顯示器的全部投票做沖突消解,輸出一套尺寸、刷新率約束集合。投票以數(shù)字優(yōu)先級作為 key;VoteSummary做沖突仲裁,高優(yōu)先級系統(tǒng)約束(熱控、低功耗)可以縮小甚至否決應用這類低優(yōu)先級源的請求。所有投票實體類(SizeVote、RefreshRateVote、SupportedRefreshRatesVote、RequestedRefreshRateVote、WorkDurationsVote、HdrPreferenceVote等)與DisplayModeDirector位于同一源碼目錄。注意:真正基于該規(guī)格選擇最終硬件模式的SurfaceFlinger側 C++ 類RefreshRateSelector(24.3.6 節(jié)),框架層不會直接引用它。24.1.10 Handler 消息協(xié)議DMS 基于 Handler 消息完成異步操作:消息常量用途注冊默認適配器MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS (1)開機階段初始化適配器注冊額外適配器MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS (2)開機后動態(tài)注冊適配器下發(fā)顯示器事件MSG_DELIVER_DISPLAY_EVENT (3)通知回調顯示器變更請求遍歷更新MSG_REQUEST_TRAVERSAL (4)觸發(fā) SurfaceFlinger 顯示器配置生效更新視口MSG_UPDATE_VIEWPORT (5)更新輸入視口映射加載亮度配置MSG_LOAD_BRIGHTNESS_CONFIGURATIONS (6)加載亮度曲線參數(shù)幀率覆蓋事件MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE (7)通知幀率覆蓋變更顯示組事件MSG_DELIVER_DISPLAY_GROUP_EVENT (8)通知顯示組新增 / 移除設備狀態(tài)上報MSG_RECEIVED_DEVICE_STATE (9)處理折疊設備狀態(tài)變更批量派發(fā)待處理事件MSG_DISPATCH_PENDING_PROCESS_EVENTS (10)批量事件分發(fā)下發(fā)顯示器快照MSG_DELIVER_DISPLAY_SNAPSHOT (11)新注冊回調一次性獲取全部顯示器當前狀態(tài)MSG_DELIVER_DISPLAY_SNAPSHOT用于讓新注冊監(jiān)聽器一次性拿到完整顯示器集合,定義在DisplayManagerService.java第 314 行。MSG_REQUEST_TRAVERSAL尤為重要:顯示器配置變更后,DMS 必須調度SurfaceFlinger執(zhí)行一次 traversal,應用新顯示器參數(shù)(圖層棧分配、顯示器投影、顯示模式)。24.2 DisplayArea 層級樹24.2.1 什么是 DisplayAreaDisplayContent是代表一整個邏輯顯示器的WindowContainer;在它之下,Android 把窗口組織為DisplayArea容器樹。每一個DisplayArea把具備相同特性、處于同一 Z 軸區(qū)間的窗口歸為一組。類繼承關系: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常量數(shù)值用途FEATURE_ROOTFEATURE_SYSTEM_FIRST0層級樹根節(jié)點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挖孔下方內容區(qū)域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 可自定義節(jié)點,例如車載后排顯示、雙屏特性。24.2.3 DisplayAreaPolicyBuilderDisplayAreaPolicyBuilder接收一組 Feature 定義,構建中間DisplayArea節(jié)點,滿足 Z 軸順序約束。AOSP 默認DefaultDisplayAreaPolicy構建層級示例:構建器工作流程:收集全部 Feature 定義,每個 Feature 面向一批窗口類型(例如 WindowedMagnification 覆蓋到無障礙放大疊加層之下所有窗口)。遍歷每一個 Z 軸槽位(窗口常量定義的 36 層模型),判斷哪些 Feature 生效。在 Feature 邊界與 Z 軸邊界交叉位置創(chuàng)建中間DisplayArea節(jié)點,拆分樹維持正確層級順序。24.2.4 DisplayAreaGroup 多根層級構建器通過DisplayAreaGroup支持多根層級,對車載、折疊設備至關重要。示例代碼來自DisplayAreaPolicyBuilder文檔,創(chuàng)建前后排顯示器獨立根節(jié)點:// 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,依據(jù)窗口類型與啟動參數(shù),將每一個WindowToken路由到對應根節(jié)點。24.2.5 層級校驗規(guī)則DisplayAreaPolicyBuilder.validate()對層級施加嚴格結構約束:Root 與 TDA ID 全局唯一:每一個RootDisplayArea、TaskDisplayArea必須擁有全局唯一 featureId。同一根下 feature ID 唯一:同一個RootDisplayArea子節(jié)點 ID 不可重復;不同根節(jié)點可以復用相同 ID,實現(xiàn)跨根組織者。輸入法容器唯一:層級構建器必須存在且僅有一個輸入法容器。默認 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 模式指定作用窗口類型,支持區(qū)間與排除:// 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()實現(xiàn)生成 DisplayArea 樹的核心算法:算法保證:連續(xù) Z 軸區(qū)間、且 Feature 集合完全一致的范圍,對應一個 DisplayArea。只覆蓋部分 Z 軸區(qū)間的 Feature 生成嵌套 DisplayArea 節(jié)點。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,啟動器、系統(tǒng)組件可以通過該參數(shù)將窗口定向到指定根節(jié)點。24.2.9 DisplayArea OrganizersShell、SystemUI 可以注冊IDisplayAreaOrganizer,接收特定 Feature 的 DisplayArea 出現(xiàn)、變更、消失回調。依靠該機制實現(xiàn):單手模式:注冊FEATURE_ONE_HANDED,對 DisplayArea 做縮放、平移。窗口放大:綁定FEATURE_TOP_LEVEL_ZOOM的 WindowedMagnification 特性。應用縮小:注冊FEATURE_APP_ZOOM_OUT。DisplayAreaOrganizerController管理注冊,分發(fā)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 // ... }該邏輯用于大屏設