Android 14 原生HostLayer统一合成与应用嵌入架构详解

Android 14 原生HostLayer统一合成与应用嵌入架构详解

本文系统梳理了在Android 14下,如何通过AOSP源码定制,基于HostLayer和TaskFragmentOrganizer实现原生级的多窗口统一合成与应用嵌入。涵盖架构设计、核心组件交互、AOSP实现细节、输入事件路由、事务同步、SELinux安全策略等关键技术,适用于车载、智能座舱等多窗口复杂场景,助力开发者实现高效、可扩展的系统级UI合成与动画。

第 1 节:架构蓝图

1.1 宿主合成器模型:高层级概述

用户的核心诉求是构建一个复杂的、系统级的自定义用户界面,这超出了标准 Android 应用模型的范畴。为了实现这一目标,必须采用一种混合式架构,将不同的系统职责进行解耦。这种架构的核心思想是:创建一个专用的、高权限的原生服务来处理窗口管理和应用程序生命周期,同时对 SurfaceFlinger 进行定制化修改,以实现最终的统一图形合成。这种职责分离对于保障系统稳定性至关重要,并且与 Android 自身的设计哲学——即关注点分离(Separation of Concerns)——完全一致。

深入分析可以发现,实现此功能最恰当的工具是 TaskFragmentOrganizer API。该 API 由 ActivityTaskManagerService 负责管理,无法从 surfaceflinger 进程中直接访问。任何试图将窗口管理逻辑强行注入 surfaceflinger 的行为,都将严重违反 Android 的架构原则,可能引发致命的死锁问题,并且在未来的 Android 版本升级中极度脆弱。因此,一个全新的原生服务(在此我们称之为 HostManagerService)成为架构上的必然选择。该服务将作为“组织者客户端”(Organizer Client),负责与系统窗口管理器交互。随后,它会通过一个自定义的 Binder 进程间通信(IPC)接口,将必要的句柄(即 SurfaceControl Leash)和元数据传递给 surfaceflinger。这种设计模式构建了一个清晰、可维护且稳定的系统级解决方案。

此模型包含两个主要的新组件:

  • HostManagerService:一个在 AOSP 中新增的 C++ 原生系统服务。它的核心职责是实现 ITaskFragmentOrganizer 接口,将第三方应用程序启动到其管理的 TaskFragment 中,并从系统服务中接收代表这些应用窗口的 SurfaceControl Leash。
  • HostLayer:在 SurfaceFlinger 内部实现的一个新的 Layer 子类。该层不仅是自定义场景图的根节点,还将作为从用户自研 C++ 渲染引擎传入的图形缓冲区的消费者,同时也是所有被嵌入应用窗口 SurfaceControl Leash 的父节点。

1.2 核心组件交互图

为了清晰地展示整个工作流程,以下序列图详细描绘了从应用启动到最终合成的端到端数据流:

  1. 初始化:HostManagerService 在系统启动时被加载,并向 ActivityTaskManagerService 注册其 TaskFragmentOrganizer 实现。
  2. 启动请求:HostManagerService 通过 Binder IPC 调用,请求 ActivityTaskManagerService 启动一个第三方应用(例如,地图应用),并指定将其放置在一个由其管理的 TaskFragment 中。
  3. 任务创建与 Leash 生成:ActivityTaskManagerService 创建该应用的 TaskFragment,并为其生成一个 SurfaceControl Leash。Leash 是一个特殊的 SurfaceControl,它代表了该应用窗口层次结构的根,并且可以安全地交给外部进程(即 HostManagerService)进行控制。
  4. 回调通知:ActivityTaskManagerService 通过 onTaskFragmentAppeared 回调,将该任务的 WindowContainerToken(唯一标识符)和 SurfaceControl Leash 句柄传递给 HostManagerService。
  5. 数据传递至 SurfaceFlinger:HostManagerService 通过一个私有的 Binder IPC 接口,将获取到的 SurfaceControl Leash 句柄以及期望的变换信息(如位置、尺寸、透明度等)发送给 surfaceflinger 进程。
  6. 原子化事务构建:surfaceflinger 在其主合成线程(Composition Thread)中接收到这些信息。
  7. 统一场景图操作:surfaceflinger 构建一个单一的、原子化的 ASurfaceTransaction。此事务将包含多个操作:
    • 将第三方应用的 Leash “重新挂载”(Reparent)到 HostLayer 的 SurfaceControl 之下。
    • 应用所有必要的几何变换(平移、缩放、裁剪)。
    • 设置 HostLayer 自身的内容(来自用户 C++ 渲染器的缓冲区)。
  8. 应用与显示:该 ASurfaceTransaction 被应用。在下一个 VSYNC 信号到来时,surfaceflinger 将这个统一后的场景(包含了自定义渲染内容和嵌入的应用窗口)作为一个整体,提交给硬件合成器(Hardware Composer, HWC)进行最终的屏幕显示。

1.3 与其他 Android UI/嵌入技术的对比分析

为了证明所提出架构的优越性,必须将其与 Android 平台提供的其他嵌入技术进行对比。一个专业的系统工程师会立即评估所有可行方案,而下面的分析将明确为何那些看似简单的方法无法满足本项目的复杂需求。

特性 提议的宿主层架构 TaskView (系统) 画中画 (PiP) SurfaceView
嵌入控制权 完全自定义 系统控制 系统控制,有限自定义 仅限应用内内容
性能隔离 极高(独立 Layer) 极高(独立 Task) 极高(独立 Task) 高(独立 Layer)
与外部内容的统一动画 支持(通过统一 SurfaceControl.Transaction) 不支持 有限支持(进入/退出动画) 不支持
系统级集成深度 极深(需修改 AOSP) 深(系统核心功能) 中等(系统级 API) 浅(标准应用 API)
实现复杂度 非常高 非常高 中等

此表格清晰地表明,只有通过修改 AOSP 并构建自定义宿主层的方案,才能同时满足对嵌入控制权、性能隔离和统一动画的全部要求。

1.4 端到端工作流程:从应用启动到嵌入式渲染

整个工作流程是一个精心设计的、跨多个系统服务的协作过程。它始于 HostManagerService 的一个意图,终于屏幕上一个完美融合的像素。

首先,HostManagerService 扮演着“导演”的角色。它决定何时、何地以及如何展示一个第三方应用。它通过标准的 ActivityStarter 接口发起一个 Intent,但这个 Intent 被特殊标记,指示 ActivityTaskManagerService (ATMS) 不要像往常一样创建一个全屏任务,而是创建一个归 HostManagerService 的 TaskFragmentOrganizer 管理的特殊任务片段。

ATMS 在接收到请求后,会走完应用启动的标准流程:创建进程(如果需要)、加载 Activity,但最关键的是,它不会为这个 Activity 创建一个顶级的 WindowContainer。取而代之的是,它创建一个 TaskFragment,并为其生成一个 SurfaceControl Leash。这个 Leash 是一个代理,代表了该应用所有窗口的根 SurfaceControl。

然后,ATMS 通过预先注册的 ITaskFragmentOrganizer Binder 接口,回调 HostManagerService 的 onTaskFragmentAppeared 方法,将这个宝贵的 Leash 句柄传递过去。至此,窗口管理的部分完成,HostManagerService 成功地“捕获”了一个第三方应用的窗口句柄。

接下来,舞台转移到图形合成领域。HostManagerService 将 Leash 句柄以及期望的变换参数(例如,将其缩小并放置在屏幕右上角)通过自定义的 Binder IPC 发送给 surfaceflinger。

在 surfaceflinger 内部,我们自定义的 HostLayer 正在等待。它接收到来自 HostManagerService 的指令。同时,用户的 C++ 渲染引擎正在辛勤工作,通过 BufferQueue 将一帧帧精美的 UI 组件(如卡片、小部件)渲染并提交给 HostLayer。

在 VSYNC 信号的驱动下,surfaceflinger 的主合成循环开始执行。它创建一个 ASurfaceTransaction,这是一个原子操作的集合。在这个事务中,它执行两个关键操作:首先,使用 reparent 命令将应用的 Leash 挂载到 HostLayer 的 SurfaceControl 之下;其次,使用 setPosition、setScale 等命令设置 Leash 的几何属性。同时,它也设置 HostLayer 自身的内容和变换。由于所有这些操作都在同一个事务中,它们的效果将在同一帧中同时生效,实现了视觉上的完美同步。

最后,surfaceflinger 将这个组合好的、包含自定义 UI 和嵌入式应用的最终场景提交给 HWC。HWC 利用硬件覆盖(Hardware Overlay)等优化手段,高效地将最终画面呈现在屏幕上。车载系统中的 CarTaskView 11 正是这一复杂流程在量产产品中的绝佳实践案例。

第 2 节:在 SurfaceFlinger 中实现自定义 HostLayer

本节将深入探讨在 surfaceflinger 进程中进行 AOSP 源码修改的具体技术细节,这是实现整个架构的底层基础。

2.1 在 AOSP 中定义新的 Layer 类型

要在 surfaceflinger 中引入一个新的合成单元,必须定义一个全新的 Layer 类型。这涉及到在 frameworks/native/services/surfaceflinger/ 目录下创建 HostLayer.h 和 HostLayer.cpp 两个文件。这个新的 HostLayer 类应当继承自 BufferLayer,因为 BufferLayer 提供了处理来自生产者的图形缓冲区(即 BufferQueue)所需的基础设施。

Layer 是 SurfaceFlinger 中最核心的合成单元。通过派生一个新的子类,我们可以为其赋予独特的行为和属性。

HostLayer 的主要职责有两个:

  • 作为图形消费者:消费来自用户自研 C++ 渲染引擎的图形缓冲区。
  • 作为父容器:在 SurfaceControl 层次结构中,作为所有被嵌入应用 SurfaceControl Leash 的父节点。

为了实现这些职责,HostLayer.h 的类定义中需要包含以下关键成员:

  • 一个 sp 或 sp 实例,用于从 BufferQueue 中获取缓冲区。
  • 一个 SurfaceControl 实例 (mSurfaceControl),代表 HostLayer 自身在场景图中的节点。
  • 一个容器(例如 std::vector 或 std::map),用于存储从 HostManagerService 传递过来的子 SurfaceControl Leash 的句柄和相关元数据。

分析 AOSP 中现有的 Layer.h 和 Layer.cpp 源码,可以发现 Layer 的生命周期、状态管理和绘制流程都非常复杂。因此,HostLayer 的实现需要谨慎地覆写关键的虚函数,例如 onDraw()(或其现代等价物)、doTransaction() 等,以确保其行为与 SurfaceFlinger 的主循环正确集成。

2.2 管理 Host Layer 的 SurfaceControl 层次结构

SurfaceControl API 是现代 Android 图形系统中用于操作 Layer 属性的核心机制。它提供了一个场景图(Scene Graph)模型,其中每个 SurfaceControl 都是一个节点。

HostLayer 必须拥有并管理其自身的 SurfaceControl,这个 SurfaceControl 将作为我们自定义UI场景的根节点。

这个场景图的层次结构是实现统一变换的关键。其结构如下:

  • 根节点:HostLayer 的 mSurfaceControl。
  • 一级子节点:
    • 代表用户自研渲染器输出的 SurfaceControl(如果需要独立于 HostLayer 背景进行变换)。
    • 从 HostManagerService 获取的、代表每个嵌入应用的 SurfaceControl Leash。

当对 HostLayer 的 mSurfaceControl 应用一个变换(例如,缩放或平移)时,这个变换会自动应用到其下的所有子节点,包括所有嵌入的应用窗口。这就实现了对整个自定义桌面进行统一动画的视觉效果,而无需单独操作每一个元素。

HostLayer 在其构造函数或初始化方法中,需要调用 SurfaceComposerClient::createSurface() 来创建其根 SurfaceControl。这个 SurfaceControl 的属性(如 z-order、layerStack)需要被正确设置,以确保它在 SurfaceFlinger 的全局 Layer 列表中的位置是正确的。

2.3 通过 BufferQueue 集成自定义 C++ 渲染引擎

HostLayer 与用户自研渲染引擎之间的通信,遵循了 Android 图形系统中标准的生产者-消费者模型。

BufferQueue 是连接这两者的核心管道。在这个模型中,HostLayer 扮演图形消费者的角色,而用户的 C++ 渲染引擎则是图形生产者。

HostLayer 在初始化时,需要创建一个 BufferQueue 对。它会持有消费者端 (IGraphicBufferConsumer),并将生产者端 (IGraphicBufferProducer) 通过某种机制(例如,一个自定义的 Binder 接口,或者如果渲染引擎也在 surfaceflinger 进程内,则通过直接的指针传递)暴露给用户的渲染引擎。

2.3.1 生产者端实现 (您的渲染引擎)

用户的 C++ 渲染引擎需要实现以下逻辑来作为 BufferQueue 的生产者:

  • 连接:获取到 HostLayer 提供的 IGraphicBufferProducer 接口。
  • 请求缓冲区:在需要渲染新一帧时,调用 dequeueBuffer() 方法从 BufferQueue 请求一个可用的图形缓冲区 (GraphicBuffer)。此调用会返回一个 slot 索引和一个 Fence 文件描述符。Fence 用于同步,确保 GPU 不会在 CPU 填充完数据之前读取该缓冲区。
  • 渲染:将获取到的 GraphicBuffer 包装成一个 HardwareBuffer。然后,通过 EGL 创建一个基于此 HardwareBuffer 的 EGLImage 和一个 EGLSurface,或者直接将其绑定为一个外部纹理。之后,使用 OpenGL ES 命令将 UI 组件(卡片、小部件等)渲染到这个缓冲区中。
  • 提交缓冲区:渲染完成后,调用 queueBuffer() 方法将填充好的缓冲区交还给 BufferQueue。此调用需要传入一个 Fence,表示 GPU 渲染操作完成的信号。SurfaceFlinger(消费者)会等待这个 Fence 触发后,才开始使用这个缓冲区进行合成。

AOSP NDK 示例中的 native-codec 25 和其他原生应用示例 26 提供了在 C++ 中与 ANativeWindow(BufferQueue 的一个封装)交互的良好范例,可以作为实现的参考。

2.3.2 消费者端实现 (HostLayer)

HostLayer 作为消费者,其逻辑与 SurfaceFlinger 的主合成循环紧密耦合。

  • 监听器:HostLayer 需要向其持有的 IGraphicBufferConsumer 注册一个 onFrameAvailable() 监听器。当生产者 (queueBuffer()) 提交一帧新数据时,这个回调函数将被触发。
  • 唤醒 SurfaceFlinger:在 onFrameAvailable() 回调中,HostLayer 必须调用 mFlinger->signalLayerUpdate() 或类似的方法来通知 SurfaceFlinger 的主线程有新的内容需要合成。这会唤醒 SurfaceFlinger 的事件循环。
  • 获取缓冲区:在 SurfaceFlinger 的合成阶段(通常在 handleMessageTransaction() 或 handleMessageInvalidate() 之后),HostLayer 的 latchBuffer() 或类似方法将被调用。在此方法中,它会调用 acquireBuffer() 从 BufferQueue 中获取最新提交的缓冲区。这个调用会返回一个 BufferItem,其中包含了 GraphicBuffer 的句柄、时间戳以及变换矩阵等信息。
  • 设置内容:获取到新的 GraphicBuffer 后,HostLayer 将其设置为当前 Layer 的内容。在 SurfaceFlinger 的绘制阶段 (onDraw()),RenderEngine 会将这个缓冲区的内容(通常是作为一个纹理)绘制到最终的合成目标上。

Android 框架中的 SurfaceFlingerConsumer 类 19 是一个专门为 SurfaceFlinger 内部使用而设计的 BufferQueue 消费者,它封装了大部分底层细节。HostLayer 的实现可以参考甚至直接使用 SurfaceFlingerConsumer,或者参考 SurfaceTexture 的原生实现 28,它们都是 BufferQueue 消费者的优秀范例。

第 3 节:使用 TaskFragmentOrganizer 嵌入应用程序

本节详细阐述架构中“窗口管理”部分的设计与实现,这部分逻辑将存在于我们新创建的 HostManagerService 中。

3.1 原理阐述:为何 TaskFragmentOrganizer 是正确的方法

直接操纵其他应用的窗口在 Android 系统中是被严格禁止的,因为这会带来严重的安全风险和稳定性问题。然而,系统确实需要为一些特权应用(如桌面启动器、车载系统外壳)提供管理其他应用窗口的能力。为此,Android 提供了 TaskOrganizer 和 TaskFragmentOrganizer 这一组受控的 API 2。它们是系统为特权组件开设的“后门”,允许它们以一种安全、可控的方式来组织和呈现应用任务。

TaskFragmentOrganizer 是 TaskOrganizer 的演进,它提供了在任务(Task)内部进行更细粒度组织的能力,这对于在自定义界面中嵌入应用窗口的场景来说是完美的选择。使用此 API 具有以下不可替代的优势:

  • 稳定的 API 契约:它提供了与 WindowManager 之间稳定且受官方支持的接口,降低了因 Android 版本升级导致实现失效的风险。
  • 正确的生命周期管理:系统会通过这个 API 正确地处理被嵌入应用的 Activity 生命周期、可见性状态转换等,开发者无需手动处理这些极其复杂且容易出错的逻辑。
  • 安全与权限:使用该 API 需要 MANAGE_ACTIVITY_TASKS 权限,确保了只有经过授权的系统级应用才能使用此功能。系统会正确处理焦点、权限和安全边界,避免了诸如 CVE-2024-49737 32 这类因窗口组织逻辑错误导致的安全漏洞。

3.2 实现用于窗口管理的特权原生服务

为了使用 TaskFragmentOrganizer,我们需要在 AOSP 中创建一个新的 C++ 原生服务。这个服务将作为连接高层 WindowManager Java API 和底层 SurfaceFlinger C++ 世界的桥梁。

实现该服务的步骤如下:

  • 定义 Binder 接口:在 frameworks/base/core/java/android/app/ 或类似位置,使用 AIDL 定义一个新的 Binder 接口,例如 IHostManager.aidl。这个接口将暴露 HostManagerService 的功能给系统的其他部分(如果需要)以及我们与 SurfaceFlinger 的私有通信。
  • 实现服务:在 frameworks/native/services/ 目录下创建 HostManagerService.cpp。这个 C++ 文件将实现 AIDL 生成的 BnHostManager 接口。服务的主体逻辑将在这里实现,包括注册 TaskFragmentOrganizer、处理回调以及与 SurfaceFlinger 通信。
  • 注册服务:在服务的 main() 函数中,使用 defaultServiceManager()->addService() 将其实例注册到 ServiceManager 中,使其成为一个系统服务。
  • 定义 SELinux 策略:这是至关重要的一步。必须在 system/sepolicy/ 目录下为 HostManagerService 创建一个 .te 文件。这个策略文件必须授予该服务必要的权限,包括:
    • binder_use、binder_call、add_service:允许其作为 Binder 服务运行并与其他服务通信。
    • manage_activity_tasks 相关的权限:允许其与 ActivityTaskManagerService 交互并组织任务。
    • 与 surfaceflinger 通信的权限:允许其通过自定义 Binder 接口与 surfaceflinger 通信。

同时,需要在 file_contexts 中为服务的可执行文件指定正确的安全上下文,并在 service_contexts 中为服务本身指定上下文 31。

3.3 注册 Organizer 并启动目标 Activity

HostManagerService 的核心功能是与 Android 的 Activity 管理系统进行交互。由于 TaskFragmentOrganizer 是一个 Java API,我们的 C++ 原生服务需要通过 JNI (Java Native Interface) 来调用它。

大致流程如下:

  • JNI 环境设置:服务在启动时需要初始化一个 JNI 环境,以便调用 Java 代码。
  • 创建 Organizer 实例:通过 JNI 调用,在 Java 堆上创建一个 TaskFragmentOrganizer 的匿名子类实例。这个子类需要覆写 onTaskFragmentAppeared、onTaskFragmentInfoChanged 和 onTaskFragmentVanished 等回调方法。这些方法的实现将通过 JNI 回调到 C++ 层的相应处理函数。
  • 注册 Organizer:调用 TaskFragmentOrganizer#registerOrganizer() 方法,将上一步创建的实例注册到系统中。
  • 启动 Activity:当需要嵌入一个新应用时,HostManagerService 会构造一个 Intent。然后,通过 JNI 调用 ActivityStarter 或类似的系统内部 API,使用这个 Intent 来启动目标 Activity。在启动时,需要提供一个 WindowContainerTransaction,将该 Activity 放入一个由我们的 Organizer 管理的 TaskFragment 中。

AOSP 中的 CarTaskView.java 12 源码为这一流程提供了极佳的参考。它展示了如何创建一个任务,如何处理 onTaskAppeared 回调,以及如何使用 WindowContainerTransaction 来控制任务的属性。我们可以将 CarTaskView 中的 Java 逻辑,转化为 HostManagerService 中对应的 C++/JNI 实现。

3.4 接收并管理应用的 SurfaceControl Leash

整个嵌入机制中最关键的一环,是在 ITaskFragmentOrganizer.onTaskFragmentAppeared() 回调被触发时。当系统成功地将一个 Activity 放置到我们指定的 TaskFragment 中后,ActivityTaskManagerService 会调用这个回调方法。

此回调方法会提供几个关键参数,其中最重要的是 SurfaceControl leash。这个 “Leash” 是一个 SurfaceControl 句柄,它代表了被嵌入的 TaskFragment 整个窗口树的根。系统将这个 Leash 的控制权交给了我们的 Organizer,允许我们对其进行 reparent(重新挂载)和 transform(变换)操作,而不会破坏应用内部的 SurfaceControl 结构。

这个 Leash 就是我们打通窗口管理和图形合成的“金钥匙”。HostManagerService 在 C++ 层接收到这个 Leash 句柄后,会立即将其通过私有 Binder IPC 发送给 surfaceflinger 进程。一旦 surfaceflinger 内部的 HostLayer 获得了这个句柄,它就拥有了将整个第三方应用的视觉内容无缝集成到自己场景图中的能力。

第 4 节:通过原生 SurfaceControl 事务实现统一合成

本节是整个架构中图形合成逻辑的核心,详细解释如何利用原子化的事务来精确操控统一后的场景图,实现流畅、同步的视觉效果。

4.1 ASurfaceTransaction API:核心合成工具

在 Android 10 (Q) 及更高版本中,SurfaceControl.Transaction(Java API)及其对应的原生 C++ API ASurfaceTransaction,是与 SurfaceFlinger 交互以更新图层状态的唯一正确方式 5。所有对图层视觉属性的修改——包括位置、Z-order、透明度、裁剪区域、父子关系等——都必须被封装到一个事务(Transaction)中,然后通过 ASurfaceTransaction_apply() 进行原子化提交。

这种基于事务的模式取代了旧有的、逐个设置属性的方法,带来了根本性的优势:

  • 原子性:事务中的所有操作要么全部成功应用,要么全部不应用,从而杜绝了中间状态的出现,避免了视觉上的撕裂(tearing)和闪烁。
  • 同步性:所有操作的效果会在同一个 VSYNC 周期内同时生效,这对于实现多个图层(例如我们的 HostLayer 和被嵌入的应用)之间的同步动画至关重要。

我们的实现将重度依赖 NDK 头文件 android/surface_control.h 36 中定义的 C++ API,以及其在 frameworks/native/services/surfaceflinger/ 和 frameworks/native/libs/gui/ 下的实现源码 37。

4.2 Reparenting 操作:将应用 Leash 挂载到 Host Layer

这是实现窗口嵌入的最关键一步。当 SurfaceFlinger 中的 HostLayer 通过 Binder IPC 从 HostManagerService 接收到第三方应用的 SurfaceControl Leash 后,它需要执行 reparent 操作,将这个 Leash 在场景图中的父节点从系统默认位置更改为 HostLayer 自身的 SurfaceControl。

以下是 SurfaceFlinger 内部执行此操作的 C++ 概念代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

// 在 SurfaceFlinger 内部,当接收到来自 HostManagerService 的 IPC 调用时
// ASurfaceControl* hostLayerSc = mHostLayer->getSurfaceControl(); // 获取 HostLayer 的 SurfaceControl
// ASurfaceControl* appLeashSc =...; // 从 IPC 数据中解析出 App Leash 的句柄

// 1. 创建一个新的事务
ASurfaceTransaction* transaction = ASurfaceTransaction_create();

// 2. 将 App Leash 重新挂载到 HostLayer 之下
// 第一个参数是事务句柄,第二个是要操作的子节点,第三个是新的父节点
ASurfaceTransaction_reparent(transaction, appLeashSc, hostLayerSc);

// 3. (可选) 在同一个事务中应用其他变换
ASurfaceTransaction_setPosition(transaction, appLeashSc, 100.0f, 100.0f);
ASurfaceTransaction_setScale(transaction, appLeashSc, 0.5f, 0.5f);

// 4. 原子化应用整个事务
ASurfaceTransaction_apply(transaction);

// 5. 释放事务句柄
ASurfaceTransaction_delete(transaction);

ASurfaceTransaction_reparent 35 调用是整个嵌入机制的魔力所在。它在逻辑上将被嵌入应用的整个视觉树(View hierarchy)“放入”了我们的 HostLayer 容器中。此后,应用 Leash 的所有变换(如位置、缩放)都将是相对于其新父节点 HostLayer 来计算的。WindowManagerService 内部的 WindowContainer.java 的实现逻辑也证实了 reparent 是窗口层次结构变化的核心操作 41。

4.3 应用同步变换(位置、缩放、裁剪、透明度)

ASurfaceTransaction 的强大之处在于它能够将多个操作捆绑在一起。为了实现 HostLayer 上的自定义组件与被嵌入应用之间的无缝、同步动画,我们必须将所有相关的变换操作都放入同一个事务中。

例如,当需要将整个自定义桌面(包括所有嵌入的应用)平滑地移动到屏幕另一侧时,SurfaceFlinger 中的逻辑应该是:

  • 创建一个 ASurfaceTransaction。
  • 调用 ASurfaceTransaction_setPosition() 来更新 HostLayer 自身 SurfaceControl 的位置。
  • 不需要单独为每个被嵌入应用的 Leash 调用 setPosition()。因为它们已经是 HostLayer 的子节点,它们的位置会随着父节点的位置变化而自动相对移动。
  • 如果需要对某个嵌入的应用进行额外的、相对于 HostLayer 的动画(例如,当桌面移动时,某个应用卡片有一个额外的弹出效果),则可以为该应用的 Leash 单独调用 ASurfaceTransaction_setPosition() 或 ASurfaceTransaction_setScale(),并将这些操作添加到同一个事务中。
  • 最后,调用 ASurfaceTransaction_apply()。

由于所有这些变换指令都在一个事务包中,SurfaceFlinger 会确保它们在同一个 VSYNC 信号到来时被一次性计算并应用。这从根本上消除了不同视觉元素之间可能出现的延迟或抖动,保证了动画的流畅性和一致性。车载系统中的 CarTaskView 12 使用了更高层级的 WindowContainerTransaction API,该 API 在 WindowManagerService 内部最终也会被分解为底层的 SurfaceControl 事务来达到同样的效果 42。

4.4 同步原语:使用 BLASTSyncEngine 确保无抖动动画

虽然在我们的架构中,所有与 HostLayer 相关的 SurfaceControl 事务都由 SurfaceFlinger 单一进程发起,因此内部同步相对简单,但了解 Android 更广泛的同步机制——BLAST (Buffer Layering and Synchronization for Transactions)——对于理解系统行为至关重要。

BLAST 的核心是 BLASTSyncEngine,它是一个位于 WindowManagerService 中的同步引擎。它的主要职责是协调来自不同进程的 SurfaceControl 事务,确保它们能够同步地应用。例如,当一个应用(进程 A)和一个系统组件(进程 B)都需要更新屏幕时,BLASTSyncEngine 可以确保它们各自提交的事务在同一帧中生效。

在我们的设计中,通过正确使用 TaskFragmentOrganizer API,我们隐式地利用了 BLAST 的能力。HostManagerService 通过 WindowContainerTransaction 与 WindowManagerService 交互,而 WindowManagerService 内部会使用 BLASTSyncEngine 来协调这些窗口层级的变化与 SurfaceFlinger 的合成周期 42。这确保了即使窗口状态的改变源于 HostManagerService,其视觉效果的呈现也能与 SurfaceFlinger 的其他合成操作完美同步。

第 5 节:确保正确的输入事件路由

一个嵌入式应用如果不能正确响应用户的触摸、点击等交互,那它就是无用的。本节将阐述如何确保输入事件被准确无误地路由到被变换和嵌入的应用窗口中。

5.1 InputDispatcher 如何在变换后的层次结构中解析触摸目标

一个核心且关键的认知是:输入事件的正确分发,不是一个需要我们手动解决的独立问题,而是正确管理 SurfaceControl 层次结构的直接结果。

Android 的输入管线 43 的终点是 InputDispatcher,它位于 InputFlinger 服务中 44。当用户触摸屏幕时, InputDispatcher 的核心任务是进行命中测试(hit test),以确定哪个窗口应该接收这个触摸事件。这个测试过程遵循以下原则:

  • 遍历最终场景图:InputDispatcher 遍历的是 SurfaceFlinger 最终合成并呈现在屏幕上的 SurfaceControl 层次结构,而不是应用逻辑中的视图树 45。
  • Z-Order 优先:遍历从 Z-order 最高的图层(最顶层)开始,依次向下。一旦找到一个可触摸且包含触摸点的窗口,通常就会停止搜索(除非有特殊标志如 FLAG_NOT_TOUCHABLE)。
  • 应用最终变换:在进行命中测试时,InputDispatcher 会使用每个 SurfaceControl 的最终世界变换矩阵(world transform matrix)。这意味着,即使我们通过 ASurfaceTransaction 将一个应用窗口缩小、旋转并移动到了屏幕的任意位置,InputDispatcher 在计算时也会使用这个变换后的新位置和新边界。

因此,只要我们通过 ASurfaceTransaction 正确地设置了被嵌入应用 Leash 的父子关系和几何变换,InputDispatcher 就能自动地、准确地将发生在该应用新位置上的触摸事件路由给它。我们完全不需要在自己的代码中进行任何手动的输入坐标变换。

5.2 WindowContainer 和 InputWindowHandle 的角色

WindowManagerService (WMS) 为系统中的每一个窗口(在 WMS 内部由 WindowState 表示,它是一个 WindowContainer)创建一个对应的 InputWindowHandle 5。这个 InputWindowHandle 是一个数据结构,它包含了 InputDispatcher 所需的关于该窗口的所有输入相关元数据,例如:

  • 窗口的 SurfaceControl 句柄。
  • 可触摸区域 (touchableRegion)。
  • 窗口的可见性 (visible) 和焦点状态 (hasFocus)。
  • 窗口的变换信息。
  • 分发超时时间 (dispatchingTimeout)。

当 WindowContainer 的层次结构发生变化时(例如,通过 reparent 操作),或者其属性(如边界、可见性、焦点)改变时,WMS 会负责更新相应的 InputWindowHandle,并将更新后的信息同步给 InputDispatcher 41。

这再次凸显了使用 TaskFragmentOrganizer API 的重要性。通过这个官方 API 来管理嵌入式任务,我们确保了 WMS 能够感知到我们对嵌入窗口所做的所有操作(如 reparenting、reordering),并因此能够正确地更新其 InputWindowHandle。如果绕过 WMS 直接在 SurfaceFlinger 中操纵图层,InputDispatcher 将得不到正确的窗口元数据,从而导致输入事件分发失败。

5.3 管理嵌入式任务的触摸遮挡和焦点

触摸遮挡:从 Android 12 开始,系统引入了对不可信触摸(untrusted touches)的拦截机制,以防止点击劫持攻击。当一个窗口被另一个应用的窗口遮挡时,下方的窗口可能不会收到触摸事件 47。在我们的架构中,由于 TaskFragmentOrganizer 是一种“受信任”的系统级关系,系统通常会正确处理这种遮挡。我们的 HostLayer 和其上的自定义UI组件会被视为系统UI的一部分,因此可以正确地与被嵌入的应用窗口共存。我们可以通过设置 HostLayer 上覆盖区域的 SurfaceControl 的 alpha 值或 FLAG_NOT_TOUCHABLE 标志来精细控制触摸行为。

输入焦点:输入焦点决定了哪个窗口接收键盘事件、遥控器D-pad事件等。TaskFragmentOrganizer API 提供了设置哪个被嵌入任务拥有输入焦点的方法。HostManagerService 可以根据用户的交互逻辑(例如,用户点击了某个嵌入的应用卡片),通过 WindowContainerTransaction 来请求将焦点转移到该应用的 WindowContainerToken。WMS 在收到请求后,会处理焦点切换的复杂逻辑,并更新所有相关窗口的 InputWindowHandle 的焦点状态,InputDispatcher 随之将后续的焦点事件正确路由。

5.4 将变换传播到输入系统

总结来说,将视觉变换传播到输入系统的过程是隐式且自动的,开发者需要关注的仅仅是正确地构建和应用 SurfaceControl 事务。整个流程可以概括为:

  • 开发者/HostManagerService:通过 WindowContainerTransaction (高层) 或直接通过私有 IPC (底层) 发起对 SurfaceControl 的变换请求。
  • SurfaceFlinger:应用 ASurfaceTransaction,更新 SurfaceControl 的视觉状态(位置、大小、父子关系)。
  • WindowManagerService:响应 WindowContainer 的变化,更新对应的 InputWindowHandle 的输入状态(可触摸区域、变换、焦点)。
  • InputDispatcher:在下一次输入事件到来时,读取 InputWindowHandle 的最新状态,并结合 SurfaceControl 的 Z-order,进行准确的命中测试和事件分发。

第 6 节:AOSP 实施指南

本节提供了一个实践性的、可操作的清单,旨在指导 AOSP 开发者完成整个系统的集成和构建。

6.1 构建系统集成 (Soong/Blueprint)

要在 AOSP 构建系统中添加我们的新模块并修改现有模块,需要编辑相应的 Android.bp 文件。

为 HostManagerService 创建 Android.bp:

1
2
3
4
5
6
7
8
9
cc_binary {
name: "hostmanagerservice",
srcs:,
shared_libs:,
init_rc: ["hostmanagerservice.rc"],
cflags:,
vendor: true, // Or product: true, depending on partition
}

为 HostLayer 修改 libsurfaceflinger.bp:
在 frameworks/native/services/surfaceflinger/Android.bp 文件中,找到 libsurfaceflinger 的定义,并在 srcs 列表中加入我们的新文件:

1
2
3
4
5
6
7
8
9
10
cc_library_shared {
name: "libsurfaceflinger",
srcs: [
...
"HostLayer.cpp", // Add our new layer implementation
...
],
...
}

表 2:关键 AOSP 文件修改清单
文件路径
目的
关键修改点
frameworks/native/services/surfaceflinger/
添加 HostLayer 实现
创建 HostLayer.h, HostLayer.cpp
frameworks/native/services/surfaceflinger/SurfaceFlinger.h
在 SurfaceFlinger 中集成 HostLayer
添加创建和管理 HostLayer 实例的成员和方法
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
同上
实现 HostLayer 的创建、销毁和合成逻辑
frameworks/native/services/
添加 HostManagerService
创建 hostmanager/ 目录,包含 HostManagerService.cpp, IHostManager.aidl 等
system/sepolicy/
为新服务定义安全策略
创建 hostmanagerservice.te, file_contexts, service_contexts 条目
device///
启动新服务
创建 hostmanagerservice.rc 文件,定义服务启动方式

6.2 为原生服务定义 SELinux 策略

对于任何新添加的系统服务,SELinux 策略都是不可或缺的,否则服务将被安全策略阻止运行。以下是 hostmanagerservice.te 文件中可能需要的关键规则示例 34:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# hostmanagerservice.te

type hostmanagerservice, domain;
type hostmanagerservice_exec, exec_type, file_type, vendor_file_type;

init_daemon_domain(hostmanagerservice)
binder_use(hostmanagerservice)
binder_call(hostmanagerservice, system_server)
binder_call(hostmanagerservice, surfaceflinger)

# Allow hostmanagerservice to register itself with servicemanager
add_service(hostmanagerservice, hostmanager_service)

# Allow communication with ActivityTaskManagerService (part of system_server)
allow hostmanagerservice activity_service:service_manager find;

# Allow communication with SurfaceFlinger
allow hostmanagerservice surfaceflinger_service:service_manager find;

# Add other necessary permissions based on specific interactions
# e.g., allow hostmanagerservice system_file:file read;

表 3:HostManagerService 所需的关键 SELinux 权限
服务 (Source)
目标域 (Target)
类 (Class)
权限 (Permission)
目的
hostmanagerservice
servicemanager
service_manager
add
注册自身服务
hostmanagerservice
activity_service
service_manager
find
查找并连接到 ActivityTaskManagerService
hostmanagerservice
surfaceflinger_service
service_manager
find
查找并连接到 SurfaceFlinger
hostmanagerservice
system_server
binder
call, transfer
与 ActivityTaskManagerService 进行 Binder 通信
hostmanagerservice
surfaceflinger
binder
call, transfer
与 SurfaceFlinger 进行 Binder 通信

6.3 C++ 代码实现概览

以下是关键组件的 C++ 骨架代码,用于展示核心逻辑。

HostLayer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "BufferLayer.h"
#include <gui/SurfaceControl.h>
#include <vector>

namespace android {

class HostLayer : public BufferLayer {
public:
HostLayer(SurfaceFlinger* flinger, const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags);
virtual ~HostLayer();

const char* getType() const override { return "HostLayer"; }

// Method to add a child SurfaceControl leash
void addChildLeash(const sp<SurfaceControl>& leash);

// Method to apply transformations to child leashes
void applyChildTransform(const sp<SurfaceControl>& leash, const mat4& matrix);

protected:
void onDraw(const RenderArea& renderArea) const override;

private:
// Container for child leashes
std::vector<sp<SurfaceControl>> mChildLeashes;
};

} // namespace android

HostManagerService.cpp (核心逻辑部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <binder/IServiceManager.h>
//... other includes for JNI, TaskFragmentOrganizer etc.

namespace android {

class HostManagerService : public BnHostManager {
public:
static void instantiate() {
defaultServiceManager()->addService(String16("my.host.manager"), new HostManagerService());
}

// Binder method called by SurfaceFlinger to get leashes
// Or, more robustly, HostManagerService pushes leashes to SF
status_t reparentSurface(const sp<IBinder>& leash, /* other params */) override;

private:
HostManagerService();
virtual ~HostManagerService();

// JNI-related methods to interact with Java TaskFragmentOrganizer
void registerOrganizerNative();
void launchActivityInTaskFragmentNative(const String16& packageName);

// Callback from JNI when a task appears
static void onTaskFragmentAppeared(JNIEnv* env, jobject thiz, jobject token, jobject leash);
};

} // namespace android

第 7 节:结论与战略性建议

7.1 实施策略总结

本报告详细阐述了一个在 Android 14 系统中实现原生宿主环境的综合架构。该架构的核心是双组件模型:

  • 一个新建的、高权限的 C++ 原生服务 HostManagerService,负责所有与窗口管理相关的任务。
  • 一个在 SurfaceFlinger 中深度定制的 HostLayer,负责所有图形合成任务。

这一架构通过以下关键技术实现其功能:

  • TaskFragmentOrganizer:作为与系统 WindowManager 交互的唯一正确且受支持的接口,用于安全地嵌入和管理第三方应用窗口。
  • ASurfaceTransaction:作为与 SurfaceFlinger 交互的原子化、同步化的接口,用于统一控制和变换所有视觉元素(自定义UI和嵌入的应用)。
  • BufferQueue:作为连接用户自研 C++ 渲染引擎和 HostLayer 的标准图形数据管道。

这种设计将窗口管理的复杂性与图形合成的性能要求明确分离,确保了系统的模块化、稳定性和可维护性。

7.2 关键考量:系统稳定性、安全性与性能

在实施此架构时,必须高度关注以下几个方面:

  • 稳定性:在 surfaceflinger 和 system_server 这两个 Android 核心进程中添加或修改代码具有极高的风险。任何微小的错误,如空指针解引用、线程死锁或内存泄漏,都可能导致整个系统频繁重启(system_server crash loop)或完全无法启动。必须进行详尽的代码审查和压力测试。
  • 安全性:HostManagerService 拥有极高的系统权限(MANAGE_ACTIVITY_TASKS)。必须严格审查其 Binder 接口,防止任何可能被恶意应用利用的漏洞,从而导致权限提升或绕过系统安全策略。SELinux 策略的配置必须遵循最小权限原则。
  • 性能:SurfaceFlinger 的每一次合成都对时间有严格要求(通常是 16.6ms for 60Hz)。过于复杂的场景图、大量的图层或低效的渲染操作都可能导致 SurfaceFlinger 的合成时间超时,从而引发掉帧和界面卡顿。需要持续使用 Systrace 和 Perfetto 等工具对合成性能进行分析和优化。

7.3 使用 WinScope 进行调试与验证

对于此类深度的图形系统定制,WinScope 是不可或缺的调试与验证工具 48。它能够提供对系统图形状态的快照和时序追踪,直观地展示:

  • SurfaceControl 层次结构:可以清晰地看到被嵌入应用的 Leash 是否已正确地 reparent 到 HostLayer 之下。
  • 图层属性:可以检查每个 SurfaceControl 的位置、尺寸、Z-order、透明度和可见性等属性是否与预期一致。
  • 事务历史:可以追踪 WindowContainerTransaction 和 SurfaceControl.Transaction 的应用过程,确认所有变换是否在同一个事务中被原子化地应用。

在开发过程中,应频繁使用 adb shell dumpsys SurfaceFlinger 和 adb shell dumpsys window 命令生成状态转储,并加载到 WinScope 中进行分析。这将是定位和解决诸如“窗口未出现”、“位置不正确”、“动画不同步”等问题的最有效手段。

7.4 前向兼容性与维护策略

在 AOSP 上进行深度定制,最大的挑战之一是保持与未来 Android 版本的兼容性。为了最大化本方案的生命周期,强烈建议遵循以下策略:

  • 坚守公共/系统 API:尽可能地依赖 TaskFragmentOrganizer 和 ASurfaceTransaction 等稳定 API。这些是 Google 为系统级 UI 组件提供的官方扩展点,它们在版本迭代中保持稳定的可能性远高于私有或内部实现细节。
  • 最小化对核心逻辑的修改:对 SurfaceFlinger.cpp 和 WindowManagerService.java 等核心文件的修改应尽可能少。优先采用创建新文件(如 HostLayer.cpp, HostManagerService.cpp)和通过钩子(hooks)或继承来扩展功能的方式,而不是直接修改现有代码。
  • 持续跟进 AOSP 更新:定期将实现同步到最新的 AOSP main 分支,并解决由此产生的编译错误和行为变更。尤其需要关注 WindowManager 和 SurfaceFlinger 子系统的重大重构。

通过采用本报告中提出的、基于官方 API 的模块化架构,并结合严格的开发与测试流程,可以构建一个功能强大、性能卓越且具备良好可维护性的原生宿主环境,为大屏设备带来创新的用户体验。

本文持续更新,最后更新时间:2025年7月18日