Para mejorar la seguridad del dispositivo, Android 7.0 divide el proceso monolítico del mediaserver
de medios en múltiples procesos con permisos y capacidades restringidas solo a las requeridas por cada proceso. Estos cambios mitigan las vulnerabilidades de seguridad del marco de medios al:
- Dividir los componentes de 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 los proveedores de SoC deben actualizar su HAL y los cambios de marco para hacerlos compatibles con la nueva arquitectura. Específicamente, debido a que el código de Android proporcionado por el proveedor a menudo asume 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 usaban un único proceso de mediaserver
de medios monolítico con una gran cantidad de permisos (acceso a la cámara, acceso al audio, acceso al controlador de video, acceso a archivos, acceso a la red, etc.). Android 7.0 divide el proceso del mediaserver
de medios en varios procesos nuevos, cada uno de los cuales requiere un conjunto de permisos mucho más pequeño:
Esta nueva arquitectura garantiza que, incluso si un proceso se ve comprometido, el código malicioso no tiene acceso al conjunto completo de permisos que antes 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
de medios y, en consecuencia, otorgan al servidor de mediaserver
más permisos de los necesarios. Específicamente, Widevine Classic continúa ejecutándose en el mediaserver
de medios para Android 7.0.
Cambios en el servidor de medios
En Android 7.0, el proceso del mediaserver
de medios existe para controlar la reproducción y la grabación, por ejemplo, pasar y sincronizar búferes entre componentes y procesos. Los procesos se comunican a través del mecanismo Binder estándar.
En una sesión de reproducción de archivo local estándar, la aplicación pasa un descriptor de archivo (FD) al mediaserver
de medios (generalmente a través de la API Java de MediaPlayer) y el mediaserver
de medios:
- Envuelve el FD en un objeto Binder DataSource que se pasa al proceso de extracción, que lo usa para leer el archivo mediante Binder IPC. (El extractor de medios no obtiene el FD, sino que hace que Binder vuelva a llamar al
mediaserver
de medios para obtener los datos). - Examina el archivo, crea el extractor adecuado para el tipo de archivo (por ejemplo, MP3Extractor o MPEG4Extractor) y devuelve una interfaz Binder para el extractor al proceso del
mediaserver
. - Hace llamadas Binder IPC al extractor para determinar el tipo de datos en el archivo (por ejemplo, datos MP3 o H.264).
- Llama al proceso de
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 de
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 donde los datos codificados se envían directamente al dispositivo de salida), o el códec puede procesar los datos decodificados directamente en lugar de devolver un búfer de datos decodificados (reproducción de video).
Cambios en MediaCodecService
El servicio de códec es donde viven los codificadores y decodificadores. Debido a las dependencias de los proveedores, todavía no todos los códecs viven en el proceso de códecs. 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 viven en el
mediaserver
de medios (sin cambios).
Una aplicación (o servidor de medios) 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 decodificación) o para pasar datos decodificados y recuperar datos codificados (para codificación) . La transferencia de datos hacia y desde los códecs ya utiliza la memoria compartida, por lo que el proceso no se modifica.
Cambios en MediaDrmServer
El servidor DRM se usa cuando se reproduce contenido protegido por DRM, como películas en Google Play Movies. Maneja el descifrado de los datos cifrados de manera 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 aún no se utiliza en todos los casos.
Cambios en el servidor de audio
El proceso de AudioServer alberga 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 Implementación de audio .
Cambios en CameraServer
CameraServer controla la cámara y se usa al grabar video para obtener fotogramas de video de la cámara y luego pasarlos al mediaserver
de medios para su posterior manejo. Para obtener detalles sobre los cambios y la guía de implementación de los cambios de CameraServer, consulte Camera Framework Hardening .
Cambios de ExtractorService
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 FD, por lo que realiza llamadas a una interfaz Binder (proporcionada por el mediaserver for
cada sesión de reproducción) para acceder a los archivos.
Una aplicación (o mediaserver
) hace una llamada al proceso 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 aplicación (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 de Binder adicional 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 una copia adicional, pero es más rápido y usa menos energía para búferes de menos de 64 KB.
Implementación
Para respaldar el movimiento de los componentes MediaDrm
y MediaCrypto
al nuevo proceso mediadrmserver
, los proveedores deben cambiar el método de asignación de búferes seguros para permitir que los búferes se compartan entre procesos.
En versiones anteriores de Android, OMX::allocateBuffer
asigna búferes seguros en mediaserver
y se usan 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 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 identificadores de búfer seguros para que puedan transportarse a través de la carpeta cuando MediaCodec
invoca una operación de descifrado en MediaCrypto
.
Uso de 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 en el que se asigna originalmente native_handle
como en el proceso en el que se deserializa. El marco lanza identificadores nativos con native_handle_close()
seguido de native_handle_delete()
y serializa/deserializa native_handle
usando Parcel::writeNativeHandle()/readNativeHandle()
.
Los proveedores de SoC que usan FD para representar búferes seguros pueden completar el FD en native_handle
con su FD. Los proveedores que no usan FD pueden representar búferes seguros usando campos adicionales en native_buffer
.
Configuración de la ubicación de descifrado
Los proveedores deben actualizar el método de descifrado de OEMCrypto que opera en native_handle
para realizar las operaciones específicas del proveedor necesarias para que native_handle
pueda utilizar en el nuevo espacio de proceso (los cambios suelen incluir actualizaciones de las bibliotecas de OEMCrypto).
Dado que 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 identificadores nativos.