为了提高设备安全性,Android 7.0 将单片式 mediaserver
进程分解为多个进程,并将权限和功能限制为每个进程所需的权限和功能。这些更改通过以下方式缓解了媒体框架的安全漏洞:
- 将 AV 管道组件拆分到特定于应用的沙盒进程中。
- 启用可更新的媒体组件(提取器、编解码器等)。
这些更改还通过显著降低大多数媒体相关安全漏洞的严重性,确保最终用户的设备和数据安全,从而提高了最终用户的安全性。
OEM 和 SoC 供应商需要更新其 HAL 和框架更改,以使其与新架构兼容。具体而言,由于供应商提供的 Android 代码通常假设所有内容都在同一进程中运行,因此供应商必须更新其代码以传递在进程之间有意义的本机句柄 (native_handle
)。有关与媒体强化相关的更改的参考实现,请参阅 frameworks/av
和 frameworks/native
。
架构变更
Android 的早期版本使用单一的、单体式的 mediaserver
进程,该进程拥有大量权限(摄像头访问权限、音频访问权限、视频驱动程序访问权限、文件访问权限、网络访问权限等)。Android 7.0 将 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
- 将 FD 封装到 Binder DataSource 对象中,该对象传递给提取器进程,提取器进程使用它通过 Binder IPC 从文件中读取数据。(mediaextractor 不会获得 FD,而是向
mediaserver
发出 Binder 调用以获取数据。) - 检查文件,为文件类型创建适当的提取器(例如 MP3Extractor 或 MPEG4Extractor),并将提取器的 Binder 接口返回给
mediaserver
进程。 - 向提取器发出 Binder IPC 调用,以确定文件中数据的类型(例如 MP3 或 H.264 数据)。
- 调用
mediacodec
进程以创建所需类型的编解码器;接收这些编解码器的 Binder 接口。 - 重复向提取器发出 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 需要额外的复制,但速度更快且功耗更低。
实现
为了支持将 MediaDrm
和 MediaCrypto
组件移至新的 mediadrmserver
进程,供应商必须更改安全缓冲区的分配方法,以允许在进程之间共享缓冲区。
在之前的 Android 版本中,安全缓冲区在 mediaserver
中由 OMX::allocateBuffer
分配,并在同一进程中的解密期间使用,如下所示
图 2. Android 6.0 及更低版本中 mediaserver 的缓冲区分配。
在 Android 7.0 中,缓冲区分配过程已更改为一种新机制,该机制在提供灵活性的同时最大限度地减少了对现有实现的影响。由于 MediaDrm
和 MediaCrypto
堆栈位于新的 mediadrmserver
进程中,缓冲区的分配方式有所不同,供应商必须更新安全缓冲区句柄,以便在 MediaCodec
在 MediaCrypto
上调用解密操作时,可以在 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 实现应使用原生句柄。