媒体框架强化

为了提高设备安全性,Android 7.0 将单体式 mediaserver 进程分解为多个进程,同时仅向各个进程提供所需的权限和功能。这些变更通过以下方式减少了媒体框架安全漏洞:

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

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

原始设备制造商 (OEM) 和 SoC 供应商需要更新其 HAL 和框架变更,使之与新架构兼容。具体来说,由于供应商提供的 Android 代码通常假定所有内容均在同一进程中运行,因此,供应商必须更新其代码以传递对于各个进程均有意义的原生句柄 (native_handle)。如需了解媒体强化相关变更的参考实现,请参阅 frameworks/avframeworks/native

架构变更

旧版 Android 使用单体式 mediaserver 进程,该进程具有众多权限(相机访问权限、音频访问权限、视频驱动程序访问权限、文件访问权限、网络访问权限等)。Android 7.0 将 mediaserver 进程拆分为若干新进程,这些进程需要的权限要少得多:

mediaserver 强化

图 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,而会将 Binder 回调至 mediaserver 来获取数据。)
  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 保护的内容(例如 Google Play 电影中的影片)时,会使用 DRM 服务器。该服务器会采用安全方式对加密的数据进行解密,因此可以访问证书和密钥存储以及其他敏感组件。由于供应商依赖关系,DRM 进程尚未应用于所有情况。

AudioServer 变更

AudioServer 进程负责托管音频相关组件,例如音频输入和输出、用于确定音频路由的 policymanager 服务,以及 FM 电台服务。如需详细了解音频变更和实现指南,请参阅实现音频

CameraServer 变更

CameraServer 负责控制相机,并且在录制视频时用于从相机获取视频帧,然后将其传递给 mediaserver 进行进一步处理。如需详细了解 CameraServer 变更和实现指南,请参阅相机框架强化

ExtractorService 变更

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

应用(或 mediaserver)会调用提取器进程来获取 IMediaExtractor,调用该 IMediaExtractor 来获取文件中包含的曲目的 IMediaSources,然后调用 IMediaSources 来读取其中的数据。

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

  • 使用共享内存需要执行额外的 Binder 调用才能释放共享内存,但对于大型缓冲区来说,这样可以加快速度并降低功耗。
  • 使用 in-Parcel 需要执行额外的复制操作,但对于小于 64KB 的缓冲区来说,这样可以加快速度并降低功耗。

实现

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

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

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

在 Android 7.0 中,缓冲区分配进程已变更为一种新机制,可在提供灵活性的同时最大限度地减少对现有实现的影响。因为新的 mediadrmserver 进程中使用了 MediaDrmMediaCrypto 堆栈,因此系统将以不同的方式分配缓冲区,供应商必须更新安全缓冲区句柄,以便当 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 实现应当使用原生句柄的 OMX_SetParameter 调用。