Para mejorar la seguridad del dispositivo, Android 7.0 divide el proceso monolítico de mediaserver
en varios procesos con permisos y capacidades restringidos solo a los que requiere cada proceso. Estos cambios mitigan las vulnerabilidades de seguridad del framework multimedia de la siguiente manera:
- División de los componentes de la canalización de AV en procesos en zona de pruebas específicos de la app
- Habilita componentes de medios actualizables (extractores, códecs, etcétera).
Estos cambios también mejoran la seguridad de los usuarios finales, ya que reducen significativamente la gravedad de la mayoría de las vulnerabilidades de seguridad relacionadas con el contenido multimedia, lo que mantiene seguros los dispositivos y los datos del usuario final.
Los OEMs y los proveedores de SoC deben actualizar sus cambios de HAL y de framework para que sean 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 controladores nativos (native_handle
) que tengan significado en todos los procesos. Para obtener una implementación de referencia de los cambios relacionados con el endurecimiento de contenido multimedia, consulta frameworks/av
y frameworks/native
.
Cambios en la arquitectura
Las versiones anteriores de Android usaban un solo proceso monolítico de mediaserver
con muchos permisos (acceso a la cámara, acceso a audio, acceso al controlador de video, acceso a archivos, acceso a red, etcétera). Android 7.0 divide el proceso mediaserver
en varios procesos nuevos que requieren un conjunto mucho más pequeño de permisos:
Figura 1: Cambios en la arquitectura para el endurecimiento de MediaServer
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 tenía mediaserver
. Las políticas de SELinux y seccomp restringen los procesos.
Nota: Debido a las dependencias del proveedor, algunos códecs todavía se ejecutan en mediaserver
y, en consecuencia, le otorgan a mediaserver
más permisos de los necesarios. Específicamente, Widevine Classic sigue ejecutándose en mediaserver
para Android 7.0.
Cambios en MediaServer
En Android 7.0, existe el proceso mediaserver
para controlar la reproducción y la grabación, p.ej., pasar y sincronizar búferes 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 locales estándar, la app pasa un descriptor de archivo (FD) a mediaserver
(por lo general, a través de la API de MediaPlayer de Java) y mediaserver
hace lo siguiente:
- Une el FD en un objeto Binder DataSource que se pasa al proceso del extractor, que lo usa para leer el archivo con el IPC de Binder. (mediaextractor no obtiene el FD, sino que hace que Binder vuelva a llamar a
mediaserver
para obtener los datos). - Examina el archivo, crea el extractor adecuado para el tipo de archivo (p.ej., MP3Extractor o MPEG4Extractor) y muestra una interfaz de Binder para el extractor al proceso
mediaserver
. - Realiza llamadas IPC de Binder al extractor para determinar el tipo de datos en el archivo (p.ej., datos MP3 o H.264).
- Realiza llamadas al proceso
mediacodec
para crear códecs del tipo requerido y recibe interfaces de Binder para estos códecs. - Realiza llamadas repetidas de Binder IPC al extractor para leer muestras codificadas, usa el Binder IPC para enviar datos codificados al proceso
mediacodec
para decodificarlos y recibe datos decodificados.
En algunos casos de uso, no se incluye 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 renderizar los datos decodificados directamente en lugar de mostrar un búfer de datos decodificados (reproducción de video).
Cambios en MediaCodecService
El servicio de códec es donde se encuentran los codificadores y decodificadores. Debido a las dependencias del proveedor, aún no todos los códecs residen en el proceso de códec. En Android 7.0:
- Los decodificadores no seguros y los codificadores de software residen en el proceso del códec.
- Los decodificadores y codificadores de hardware seguros se encuentran en
mediaserver
(sin cambios).
Una app (o mediaserver
) llama al proceso de códec para crear un códec del tipo requerido y, luego, llama a ese códec para pasar datos codificados y recuperar datos decodificados (para la decodificación) o para pasar datos decodificados y recuperar datos codificados (para la codificación). La transferencia de datos desde y hacia los códecs ya usa memoria compartida, por lo que ese proceso no cambia.
Cambios en MediaDrmServer
El servidor de DRM se usa cuando se reproduce contenido protegido por DRM, como las películas en Google Play Películas. Controla la desencriptación de los datos encriptados de forma segura y, por lo tanto, tiene acceso al almacenamiento de certificados y claves, y a otros componentes sensibles. Debido a las dependencias de los proveedores, el proceso de DRM aún no se usa en todos los casos.
Cambios en AudioServer
El proceso de AudioServer aloja componentes relacionados con el audio, como la entrada y salida de audio, el servicio de policymanager que determina el enrutamiento de audio y el servicio de radio FM. Para obtener detalles sobre los cambios en el audio y la orientación para la implementación, consulta Cómo implementar el audio.
Cambios en CameraServer
CameraServer controla la cámara y se usa cuando se graba un video para obtener fotogramas de la cámara y, luego, pasarlos a mediaserver
para su procesamiento. Para obtener detalles sobre los cambios y la guía de implementación de los cambios de CameraServer, consulta Endurecimiento del framework de la cámara.
Cambios en ExtractorService
El servicio de extractor aloja los extractores, que son componentes que analizan los diversos formatos de archivo compatibles con el framework multimedia. El servicio de extractor es el que tiene menos privilegios de todos los servicios; no puede leer los FD, por lo que, en su lugar, realiza llamadas a una interfaz de Binder (que le proporciona mediaserver for
en cada sesión de reproducción) para acceder a los archivos.
Una app (o mediaserver
) realiza una llamada al proceso del extractor 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 app (o mediaserver
) incluye los datos en el paquete de respuesta como parte de la transacción de Binder o usa la 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 usa menos energía para búferes grandes.
- El uso de in-Parcel requiere copias adicionales, pero es más rápido y consume menos energía para búferes de menos de 64 KB.
Implementación
Para admitir el traslado de los componentes MediaDrm
y MediaCrypto
al nuevo proceso mediadrmserver
, los proveedores deben cambiar el método de asignación de los búferes seguros para permitir que se compartan entre procesos.
En versiones anteriores de Android, OMX::allocateBuffer
asigna búferes seguros en mediaserver
y los usa durante la desencriptación en el mismo proceso, como se muestra a continuación:
Figura 2: Asignación de búfer de Android 6.0 y versiones anteriores en mediaserver.
En Android 7.0, el proceso de asignación de búfer cambió a un nuevo mecanismo que proporciona flexibilidad y, al mismo tiempo, minimiza el impacto en las implementaciones existentes. Con las pilas MediaDrm
y MediaCrypto
en el nuevo proceso mediadrmserver
, los búferes se asignan de manera diferente y los proveedores deben actualizar los controladores de búfer seguros para que se puedan transportar a través de Binder cuando MediaCodec
invoque una operación de desencriptación en MediaCrypto
.
Figura 3: Asignación de búfer de Android 7.0 y versiones posteriores en mediaserver.
Cómo usar controladores nativos
OMX::allocateBuffer
debe mostrar un puntero a una estructura native_handle
, que contiene descriptores de archivos (FD) y datos de números enteros adicionales. Un native_handle
tiene todas las ventajas de usar FD, incluida la compatibilidad existente con Binder para la serialización y deserialización, a la vez que permite una mayor flexibilidad para los proveedores que actualmente no usan FD.
Usa native_handle_create()
para asignar el controlador nativo.
El código del framework se apropia de la estructura native_handle
asignada y es responsable de liberar recursos en el proceso en el que se asigna native_handle
originalmente y en el proceso en el que se deserializa. El framework libera controladores nativos con native_handle_close()
seguido de native_handle_delete()
y serializa o deserializa el native_handle
con Parcel::writeNativeHandle()/readNativeHandle()
.
Los proveedores de SoC que usan FD para representar búferes seguros pueden propagar el FD en native_handle
con su FD. Los proveedores que no usan FDs pueden representar búferes seguros con campos adicionales en el native_buffer
.
Cómo establecer la ubicación de desencriptación
Los proveedores deben actualizar el método de desencriptación de OEMCrypto que opera en native_handle
para realizar las operaciones específicas del proveedor necesarias para que native_handle
se pueda usar en el nuevo espacio de proceso (los cambios suelen incluir actualizaciones de las bibliotecas de 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 esta compatibilidad y una llamada OMX_SetParameter
que notifica a la implementación de OMX que debe usar controladores nativos.