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