Para mejorar la seguridad del dispositivo, Android 7.0 divide el proceso mediaserver
monolítico en múltiples procesos con permisos y capacidades restringidos solo a los requeridos por cada proceso. Estos cambios mitigan las vulnerabilidades de seguridad del marco de medios al:
- Dividir los componentes de la canalización AV en procesos de espacio aislado específicos de la aplicación.
- Habilitación de componentes multimedia actualizables (extractores, códecs, etc.).
Estos cambios también mejoran la seguridad de los usuarios finales al reducir significativamente la gravedad de la mayoría de las vulnerabilidades de seguridad relacionadas con los medios, manteniendo seguros los dispositivos y los datos de los usuarios finales.
Los fabricantes de equipos originales y proveedores de SoC deben actualizar sus cambios de marco y HAL para hacerlos compatibles con la nueva arquitectura. Específicamente, debido a que el código de Android proporcionado por el proveedor a menudo supone que todo se ejecuta en el mismo proceso, los proveedores deben actualizar su código para pasar identificadores nativos ( native_handle
) que tienen significado en todos los procesos. Para obtener una implementación de referencia de los cambios relacionados con el refuerzo de medios, consulte frameworks/av
y frameworks/native
.
Cambios arquitectónicos
Las versiones anteriores de Android utilizaban un proceso mediaserver
único y monolítico con muchos permisos (acceso a la cámara, acceso a audio, acceso al controlador de video, acceso a archivos, acceso a la red, etc.). Android 7.0 divide el proceso mediaserver
en varios procesos nuevos, cada uno de los cuales requiere un conjunto mucho más pequeño de permisos:
Esta nueva arquitectura garantiza que incluso si un proceso se ve comprometido, el código malicioso no tenga acceso al conjunto completo de permisos que anteriormente tenía mediaserver
. Los procesos están restringidos por las políticas de SElinux y seccomp.
Nota: Debido a las dependencias de los proveedores, algunos códecs aún se ejecutan en el mediaserver
y, en consecuencia, otorgan mediaserver
más permisos de los necesarios. Específicamente, Widevine Classic continúa ejecutándose en el mediaserver
de Android 7.0.
Cambios en el servidor de medios
En Android 7.0, el proceso mediaserver
existe para controlar la reproducción y la grabación, por ejemplo, pasar y sincronizar buffers entre componentes y procesos. Los procesos se comunican a través del mecanismo estándar de Binder.
En una sesión de reproducción de archivos local estándar, la aplicación pasa un descriptor de archivo (FD) al mediaserver
(generalmente a través de la API Java de MediaPlayer) y al mediaserver
:
- Envuelve el FD en un objeto Binder DataSource que se pasa al proceso de extracción, que lo utiliza para leer el archivo mediante Binder IPC. (El mediaextractor no obtiene el FD, sino que realiza llamadas de Binder al
mediaserver
para obtener los datos). - Examina el archivo, crea el extractor apropiado para el tipo de archivo (por ejemplo, MP3Extractor o MPEG4Extractor) y devuelve una interfaz Binder para el extractor al proceso
mediaserver
. - Realiza llamadas Binder IPC al extractor para determinar el tipo de datos en el archivo (por ejemplo, datos MP3 o H.264).
- Llama al proceso
mediacodec
para crear códecs del tipo requerido; recibe interfaces Binder para estos códecs. - Realiza repetidas llamadas de Binder IPC al extractor para leer muestras codificadas, utiliza Binder IPC para enviar datos codificados al proceso
mediacodec
para su decodificación y recibe datos decodificados.
En algunos casos de uso, no interviene ningún códec (como una reproducción descargada en la que los datos codificados se envían directamente al dispositivo de salida), o el códec puede representar los datos decodificados directamente en lugar de devolver un búfer de datos decodificados (reproducción de vídeo).
Cambios en el servicio MediaCodec
El servicio de códec es donde viven los codificadores y decodificadores. Debido a las dependencias de los proveedores, no todos los códecs se encuentran todavía en el proceso de códec. En Android 7.0:
- Los decodificadores y codificadores de software no seguros viven en el proceso de códec.
- Los decodificadores seguros y los codificadores de hardware se encuentran en el
mediaserver
(sin cambios).
Una aplicación (o mediaserver
) llama al proceso de códec para crear un códec del tipo requerido, luego llama a ese códec para pasar datos codificados y recuperar datos decodificados (para decodificar) o para pasar datos decodificados y recuperar datos codificados (para codificar) . La transferencia de datos hacia y desde códecs ya utiliza memoria compartida, por lo que el proceso no cambia.
Cambios en MediaDrmServer
El servidor DRM se utiliza cuando se reproduce contenido protegido con DRM, como películas en Google Play Movies. Se encarga de descifrar los datos cifrados de forma segura y, como tal, tiene acceso al almacenamiento de certificados y claves y otros componentes confidenciales. Debido a las dependencias de los proveedores, el proceso DRM no se utiliza todavía en todos los casos.
Cambios en el servidor de audio
El proceso AudioServer aloja componentes relacionados con el audio, como la entrada y salida de audio, el servicio de administrador de políticas que determina el enrutamiento de audio y el servicio de radio FM. Para obtener detalles sobre los cambios de audio y la guía de implementación, consulte Implementar audio .
Cambios en el servidor de cámara
CameraServer controla la cámara y se utiliza al grabar video para obtener fotogramas de video de la cámara y luego pasarlos al mediaserver
para su posterior manejo. Para obtener detalles sobre los cambios y orientación de implementación para los cambios de CameraServer, consulte Camera Framework Hardening .
Cambios en el servicio Extractor
El servicio de extracción aloja los extractores , componentes que analizan los distintos formatos de archivo admitidos por el marco de medios. El servicio de extracción es el menos privilegiado de todos los servicios: no puede leer archivos FD, por lo que realiza llamadas a una interfaz de Binder (que le proporciona el mediaserver for
cada sesión de reproducción) para acceder a los archivos.
Una aplicación (o mediaserver
) realiza una llamada al proceso de extracción para obtener un IMediaExtractor
, llama a ese IMediaExtractor
para obtener IMediaSources
para la pista contenida en el archivo y luego llama a IMediaSources
para leer datos de ellos.
Para transferir los datos entre procesos, la aplicación (o mediaserver
) incluye los datos en el paquete de respuesta como parte de la transacción de Binder o utiliza memoria compartida:
- El uso de la memoria compartida requiere una llamada adicional a Binder para liberar la memoria compartida, pero es más rápido y utiliza menos energía para buffers grandes.
- El uso de in-Parcel requiere una copia adicional, pero es más rápido y utiliza menos energía para buffers de menos de 64 KB.
Implementación
Para respaldar el traslado de los componentes MediaDrm
y MediaCrypto
al nuevo proceso mediadrmserver
, los proveedores deben cambiar el método de asignación de buffers seguros para permitir que los buffers se compartan entre procesos.
En versiones anteriores de Android, OMX::allocateBuffer
asigna búferes seguros en mediaserver
y se utilizan durante el descifrado en el mismo proceso, como se muestra a continuación:
En Android 7.0, el proceso de asignación de búfer cambió a un nuevo mecanismo que brinda flexibilidad y al mismo tiempo minimiza el impacto en las implementaciones existentes. Con las pilas MediaDrm
y MediaCrypto
en el nuevo proceso mediadrmserver
, los buffers se asignan de manera diferente y los proveedores deben actualizar los identificadores seguros del buffer para que puedan transportarse a través de la carpeta cuando MediaCodec
invoca una operación de descifrado en MediaCrypto
.
Utilice identificadores nativos
OMX::allocateBuffer
debe devolver un puntero a una estructura native_handle
, que contiene descriptores de archivos (FD) y datos enteros adicionales. Un native_handle
tiene todas las ventajas de usar FD, incluido el soporte de carpeta existente para serialización/deserialización, al tiempo que permite una mayor flexibilidad para los proveedores que actualmente no usan FD.
Utilice native_handle_create()
para asignar el identificador nativo. El código del marco toma posesión de la estructura native_handle
asignada y es responsable de liberar recursos tanto en el proceso donde se asignó originalmente el native_handle
como en el proceso donde se deserializa. El marco libera identificadores nativos con native_handle_close()
seguido de native_handle_delete()
y serializa/deserializa el native_handle
usando Parcel::writeNativeHandle()/readNativeHandle()
.
Los proveedores de SoC que utilizan FD para representar buffers seguros pueden completar el FD en el native_handle
con su FD. Los proveedores que no utilizan FD pueden representar buffers seguros usando campos adicionales en el native_buffer
.
Establecer ubicación de descifrado
Los proveedores deben actualizar el método de descifrado OEMCrypto que opera en el native_handle
para realizar cualquier operación específica del proveedor necesaria para que el native_handle
sea utilizable en el nuevo espacio de proceso (los cambios generalmente incluyen actualizaciones de las bibliotecas OEMCrypto).
Como allocateBuffer
es una operación OMX estándar, Android 7.0 incluye una nueva extensión OMX ( OMX.google.android.index.allocateNativeHandle
) para consultar este soporte y una llamada OMX_SetParameter
que notifica a la implementación OMX que debe usar identificadores nativos.