媒体框架强化

为了提高设备安全性,Android 7.0 将单片式 mediaserver 进程分解为多个进程,并将权限和功能限制为每个进程所需的权限和功能。这些更改通过以下方式缓解了媒体框架的安全漏洞:

  • 将 AV 管道组件拆分到特定于应用的沙盒进程中。
  • 启用可更新的媒体组件(提取器、编解码器等)。

这些更改还通过显著降低大多数媒体相关安全漏洞的严重性,确保最终用户的设备和数据安全,从而提高了最终用户的安全性。

OEM 和 SoC 供应商需要更新其 HAL 和框架更改,以使其与新架构兼容。具体而言,由于供应商提供的 Android 代码通常假设所有内容都在同一进程中运行,因此供应商必须更新其代码以传递在进程之间有意义的本机句柄 (native_handle)。有关与媒体强化相关的更改的参考实现,请参阅 frameworks/avframeworks/native

架构变更

Android 的早期版本使用单一的、单体式的 mediaserver 进程,该进程拥有大量权限(摄像头访问权限、音频访问权限、视频驱动程序访问权限、文件访问权限、网络访问权限等)。Android 7.0 将 mediaserver 进程拆分为多个新进程,每个进程仅需一小部分权限。

mediaserver hardening

图 1. mediaserver 强化的架构变更

这种新架构确保即使某个进程遭到入侵,恶意代码也无法访问之前由 mediaserver 持有的全套权限。进程受到 SELinux 和 seccomp 策略的限制。

注意: 由于供应商依赖性,某些编解码器仍然在 mediaserver 中运行,因此授予 mediaserver 超出必要的权限。具体而言,Widevine Classic 在 Android 7.0 中继续在 mediaserver 中运行。

MediaServer 变更

在 Android 7.0 中,mediaserver 进程用于驱动播放和录制,例如在组件和进程之间传递和同步缓冲区。进程通过标准的 Binder 机制进行通信。

在标准的本地文件播放会话中,应用将文件描述符 (FD) 传递给 mediaserver(通常通过 MediaPlayer Java API),并且 mediaserver

  1. 将 FD 封装到 Binder DataSource 对象中,该对象传递给提取器进程,提取器进程使用它通过 Binder IPC 从文件中读取数据。(mediaextractor 不会获得 FD,而是向 mediaserver 发出 Binder 调用以获取数据。)
  2. 检查文件,为文件类型创建适当的提取器(例如 MP3Extractor 或 MPEG4Extractor),并将提取器的 Binder 接口返回给 mediaserver 进程。
  3. 向提取器发出 Binder IPC 调用,以确定文件中数据的类型(例如 MP3 或 H.264 数据)。
  4. 调用 mediacodec 进程以创建所需类型的编解码器;接收这些编解码器的 Binder 接口。
  5. 重复向提取器发出 Binder IPC 调用以读取编码样本,使用 Binder IPC 将编码数据发送到 mediacodec 进程进行解码,并接收解码后的数据。

在某些用例中,不涉及编解码器(例如,将编码数据直接发送到输出设备的卸载播放),或者编解码器可以直接渲染解码后的数据,而不是返回解码后的数据缓冲区(视频播放)。

MediaCodecService 变更

编解码器服务是编码器和解码器所在的位置。由于供应商依赖性,并非所有编解码器都已在编解码器进程中运行。在 Android 7.0 中

  • 非安全解码器和软件编码器在编解码器进程中运行。
  • 安全解码器和硬件编码器在 mediaserver 中运行(未更改)。

应用(或 mediaserver)调用编解码器进程以创建所需类型的编解码器,然后调用该编解码器以传入编码数据并检索解码后的数据(用于解码),或传入解码后的数据并检索编码后的数据(用于编码)。与编解码器之间的数据传输已使用共享内存,因此该过程未更改。

MediaDrmServer 变更

DRM 服务器用于播放受 DRM 保护的内容,例如 Google Play 影视中的电影。它以安全的方式处理加密数据的解密,因此可以访问证书和密钥存储以及其他敏感组件。由于供应商依赖性,DRM 进程尚未在所有情况下使用。

AudioServer 变更

AudioServer 进程托管与音频相关的组件,例如音频输入和输出、确定音频路由的 policymanager 服务以及 FM 无线电服务。有关音频变更和实施指南的详细信息,请参阅 实施音频

CameraServer 变更

CameraServer 控制摄像头,并在录制视频时用于从摄像头获取视频帧,然后将这些帧传递给 mediaserver 以进行进一步处理。有关 CameraServer 变更的变更和实施指南的详细信息,请参阅 摄像头框架强化

ExtractorService 变更

提取器服务托管提取器,这些组件解析媒体框架支持的各种文件格式。提取器服务是所有服务中权限最低的服务,它无法读取 FD,因此而是调用 Binder 接口(由 mediaserver for 每个播放会话提供)来访问文件。

应用(或 mediaserver)调用提取器进程以获取 IMediaExtractor,调用该 IMediaExtractor 以获取文件中包含的轨道的 IMediaSources,然后调用 IMediaSources 以从中读取数据。

为了在进程之间传输数据,应用(或 mediaserver)将数据包含在回复 Parcel 中作为 Binder 事务的一部分,或使用共享内存

  • 对于大型缓冲区,使用共享内存需要额外的 Binder 调用来释放共享内存,但速度更快且功耗更低。
  • 对于小于 64KB 的缓冲区,使用内联 Parcel 需要额外的复制,但速度更快且功耗更低。

实现

为了支持将 MediaDrmMediaCrypto 组件移至新的 mediadrmserver 进程,供应商必须更改安全缓冲区的分配方法,以允许在进程之间共享缓冲区。

在之前的 Android 版本中,安全缓冲区在 mediaserver 中由 OMX::allocateBuffer 分配,并在同一进程中的解密期间使用,如下所示

图 2. Android 6.0 及更低版本中 mediaserver 的缓冲区分配。

在 Android 7.0 中,缓冲区分配过程已更改为一种新机制,该机制在提供灵活性的同时最大限度地减少了对现有实现的影响。由于 MediaDrmMediaCrypto 堆栈位于新的 mediadrmserver 进程中,缓冲区的分配方式有所不同,供应商必须更新安全缓冲区句柄,以便在 MediaCodecMediaCrypto 上调用解密操作时,可以在 binder 之间传输这些句柄。

图 3. Android 7.0 及更高版本中 mediaserver 的缓冲区分配。

使用原生句柄

OMX::allocateBuffer 必须返回指向 native_handle 结构的指针,该结构包含文件描述符 (FD) 和其他整数数据。native_handle 具有使用 FD 的所有优点,包括现有的 binder 对序列化/反序列化的支持,同时为当前不使用 FD 的供应商提供了更大的灵活性。

使用 native_handle_create() 分配原生句柄。框架代码拥有分配的 native_handle 结构的所有权,并负责在最初分配 native_handle 的进程和反序列化它的进程中释放资源。框架使用 native_handle_close(),然后使用 native_handle_delete() 释放原生句柄,并使用 Parcel::writeNativeHandle()/readNativeHandle() 序列化/反序列化 native_handle

使用 FD 表示安全缓冲区的 SoC 供应商可以将 FD 填充到 native_handle 中的 FD 中。不使用 FD 的供应商可以使用 native_buffer 中的其他字段来表示安全缓冲区。

设置解密位置

供应商必须更新在 native_handle 上运行的 OEMCrypto 解密方法,以执行任何供应商特定的操作,从而使 native_handle 在新的进程空间中可用(更改通常包括更新 OEMCrypto 库)。

由于 allocateBuffer 是标准的 OMX 操作,因此 Android 7.0 包含一个新的 OMX 扩展 (OMX.google.android.index.allocateNativeHandle) 来查询此支持,以及一个 OMX_SetParameter 调用,该调用通知 OMX 实现应使用原生句柄。