Uso de Binder IPC

Esta página describe los cambios en el controlador de Binder en Android 8, proporciona detalles sobre el uso de Binder IPC y enumera la política SELinux requerida.

Cambios en el controlador de carpeta

A partir de Android 8, el marco de Android y los HAL ahora se comunican entre sí mediante Binder. Dado que esta comunicación aumenta drásticamente el tráfico de Binder, Android 8 incluye varias mejoras diseñadas para mantener la IPC de Binder rápida. Los proveedores de SoC y los OEM deben fusionarse directamente desde las ramas relevantes de android-4.4, android-4.9 y superiores del kernel/proyecto común .

Múltiples dominios de carpeta (contextos)

Común-4.4 y superior, incluido el upstream

Para dividir claramente el tráfico de carpeta entre el código del marco (independiente del dispositivo) y del proveedor (específico del dispositivo), Android 8 introdujo el concepto de contexto de carpeta . Cada contexto de carpeta tiene su propio nodo de dispositivo y su propio administrador de contexto (servicio). Puede acceder al administrador de contexto solo a través del nodo del dispositivo al que pertenece y, al pasar un nodo de carpeta a través de un contexto determinado, es accesible desde ese mismo contexto solo mediante otro proceso, aislando así completamente los dominios entre sí. Para obtener detalles sobre el uso, consulte vndbinder y vndservicemanager .

Recolección de dispersión

Común-4.4 y superior, incluido el upstream

En versiones anteriores de Android, cada dato en una llamada de carpeta se copiaba tres veces:

  • Una vez para serializarlo en un Parcel en el proceso de llamada.
  • Una vez en el controlador del kernel para copiar el Parcel al proceso de destino
  • Una vez para deserializar el Parcel en el proceso de destino

Android 8 utiliza optimización de dispersión para reducir el número de copias de 3 a 1. En lugar de serializar primero los datos en un Parcel , los datos permanecen en su estructura y diseño de memoria originales y el controlador los copia inmediatamente en el proceso de destino. Una vez que los datos están en el proceso de destino, la estructura y el diseño de la memoria son los mismos y los datos se pueden leer sin requerir otra copia.

Bloqueo de grano fino

Común-4.4 y superior, incluido el upstream

En versiones anteriores de Android, el controlador de carpeta usaba un bloqueo global para proteger contra el acceso simultáneo a estructuras de datos críticas. Si bien hubo una disputa mínima por el bloqueo, el problema principal era que si un subproceso de baja prioridad obtenía el bloqueo y luego era reemplazado, podría retrasar seriamente los subprocesos de mayor prioridad que necesitaban obtener el mismo bloqueo. Esto provocó un bloqueo en la plataforma.

Los intentos iniciales de resolver este problema implicaron deshabilitar la preferencia mientras se mantenía el bloqueo global. Sin embargo, esto fue más un truco que una verdadera solución, y finalmente fue rechazado y descartado. Los intentos posteriores se centraron en hacer que el bloqueo fuera más detallado, una versión del cual se ejecuta en dispositivos Pixel desde enero de 2017. Si bien la mayoría de esos cambios se hicieron públicos, se realizaron mejoras sustanciales en versiones posteriores.

Después de identificar pequeños problemas en la implementación del bloqueo detallado, ideamos una solución mejorada con una arquitectura de bloqueo diferente y enviamos los cambios en todas las ramas comunes del kernel. Continuamos probando esta implementación en una gran cantidad de dispositivos diferentes; Como no tenemos conocimiento de ningún problema pendiente, esta es la implementación recomendada para dispositivos que se envían con Android 8.

Herencia de prioridad en tiempo real

Common-4.4 y common-4.9 (próximamente ascendentes)

El controlador de carpeta siempre ha admitido una buena herencia de prioridad. A medida que un número cada vez mayor de procesos en Android se ejecutan con prioridad en tiempo real, en algunos casos ahora tiene sentido que si un subproceso en tiempo real realiza una llamada de carpeta, el subproceso en el proceso que maneja esa llamada también se ejecuta con prioridad en tiempo real. . Para admitir estos casos de uso, Android 8 ahora implementa la herencia de prioridad en tiempo real en el controlador de carpeta.

Además de la herencia de prioridad a nivel de transacción, la herencia de prioridad de nodo permite que un nodo (objeto de servicio de carpeta) especifique una prioridad mínima con la que se deben ejecutar las llamadas a este nodo. Las versiones anteriores de Android ya admitían la herencia de prioridad de nodos con buenos valores, pero Android 8 agrega soporte para la herencia de nodos de políticas de programación en tiempo real.

Cambios en el espacio de usuario

Android 8 incluye todos los cambios en el espacio de usuario necesarios para trabajar con el controlador de carpeta actual en el kernel común con una excepción: la implementación original para deshabilitar la herencia de prioridad en tiempo real para /dev/binder usaba un ioctl . El desarrollo posterior cambió el control de la herencia de prioridad a un método más detallado que es por modo de carpeta (y no por contexto). Por lo tanto, ioctl no está en la rama común de Android y, en cambio, se envía en nuestros núcleos comunes .

El efecto de este cambio es que la herencia de prioridad en tiempo real está deshabilitada de forma predeterminada para cada nodo. El equipo de rendimiento de Android consideró beneficioso habilitar la herencia de prioridad en tiempo real para todos los nodos en el dominio hwbinder . Para lograr el mismo efecto, seleccione este cambio en el espacio de usuario.

SHA para núcleos comunes

Para obtener los cambios necesarios en el controlador de carpeta, sincronice con el SHA apropiado:

  • Común-3.18
    cc8b90c121de ANDROID: carpeta: no verifique los permisos anteriores al restaurar.
  • Común-4.4
    76b376eac7a2 ANDROID: carpeta: no verifique los permisos anteriores al restaurar.
  • Común-4.9
    ecd972d4f9b5 ANDROID: carpeta: no verifique los permisos anteriores al restaurar.

Usando carpeta IPC

Históricamente, los procesos de proveedores han utilizado la comunicación entre procesos de carpeta (IPC) para comunicarse. En Android 8, el nodo del dispositivo /dev/binder se vuelve exclusivo para los procesos del marco, lo que significa que los procesos del proveedor ya no tienen acceso a él. Los procesos de proveedores pueden acceder a /dev/hwbinder , pero deben convertir sus interfaces AIDL para usar HIDL. Para los proveedores que desean continuar usando interfaces AIDL entre procesos de proveedores, Android admite Binder IPC como se describe a continuación. En Android 10, Stable AIDL permite que todos los procesos utilicen /dev/binder y al mismo tiempo resuelve las garantías de estabilidad HIDL y /dev/hwbinder resueltas. Para saber cómo utilizar AIDL estable, consulte AIDL para HAL .

vndbinder

Android 8 admite un nuevo dominio de carpeta para que lo utilicen los servicios del proveedor, al que se accede mediante /dev/vndbinder en lugar de /dev/binder . Con la adición de /dev/vndbinder , Android ahora tiene los siguientes tres dominios IPC:

Dominio IPC Descripción
/dev/binder IPC entre procesos de marco/aplicación con interfaces AIDL
/dev/hwbinder IPC entre procesos de marco/proveedor con interfaces HIDL
IPC entre procesos de proveedores con interfaces HIDL
/dev/vndbinder IPC entre procesos proveedor/proveedor con Interfaces AIDL

Para que aparezca /dev/vndbinder , asegúrese de que el elemento de configuración del kernel CONFIG_ANDROID_BINDER_DEVICES esté configurado en "binder,hwbinder,vndbinder" (este es el valor predeterminado en los árboles del kernel comunes de Android).

Normalmente, los procesos del proveedor no abren el controlador de carpeta directamente y en su lugar se vinculan con la biblioteca del espacio de usuario libbinder , que abre el controlador de carpeta. Agregar un método para ::android::ProcessState() selecciona el controlador de carpeta para libbinder . Los procesos de proveedor deben llamar a este método antes de llamar a ProcessState, IPCThreadState o antes de realizar cualquier llamada a Binder en general. Para usarlo, realice la siguiente llamada después de main() de un proceso de proveedor (cliente y servidor):

ProcessState::initWithDriver("/dev/vndbinder");

administradordeserviciovnd

Anteriormente, los servicios de carpeta se registraban con servicemanager , donde otros procesos podían recuperarlos. En Android 8, servicemanager ahora lo utilizan exclusivamente los procesos de marcos y aplicaciones y los procesos de proveedores ya no pueden acceder a él.

Sin embargo, los servicios del proveedor ahora pueden usar vndservicemanager , una nueva instancia de servicemanager que usa /dev/vndbinder en lugar de /dev/binder y que está construida a partir de las mismas fuentes que el framework servicemanager . Los procesos del proveedor no necesitan realizar cambios para hablar con vndservicemanager ; cuando se abre un proceso de proveedor / dev/vndbinder , las búsquedas de servicios van automáticamente a vndservicemanager .

El binario vndservicemanager está incluido en los archivos MAKE predeterminados del dispositivo Android.

Política SELinux

Los procesos de proveedores que quieran utilizar la funcionalidad de carpeta para comunicarse entre sí necesitan lo siguiente:

  1. Acceso a /dev/vndbinder .
  2. Binder {transfer, call} se conecta a vndservicemanager .
  3. binder_call(A, B) para cualquier dominio de proveedor A que quiera llamar al dominio de proveedor B a través de la interfaz de carpeta de proveedor.
  4. Permiso para {add, find} servicios en vndservicemanager .

Para cumplir con los requisitos 1 y 2, utilice la macro vndbinder_use() :

vndbinder_use(some_vendor_process_domain);

Para cumplir con el requisito 3, binder_call(A, B) para los procesos de proveedor A y B que necesitan comunicarse a través de Binder puede permanecer en su lugar y no es necesario cambiarle el nombre.

Para cumplir el requisito 4, debe realizar cambios en la forma en que se manejan los nombres de los servicios, las etiquetas de los servicios y las reglas.

Para obtener detalles sobre SELinux, consulte Linux con seguridad mejorada en Android . Para obtener detalles sobre SELinux en Android 8.0, consulte SELinux para Android 8.0 .

Nombres de servicios

Anteriormente, el proveedor procesa los nombres de los servicios registrados en un archivo service_contexts y agrega las reglas correspondientes para acceder a ese archivo. Ejemplo de archivo service_contexts de device/google/marlin/sepolicy :

AtCmdFwd                              u:object_r:atfwd_service:s0
cneservice                            u:object_r:cne_service:s0
qti.ims.connectionmanagerservice      u:object_r:imscm_service:s0
rcs                                   u:object_r:radio_service:s0
uce                                   u:object_r:uce_service:s0
vendor.qcom.PeripheralManager         u:object_r:per_mgr_service:s0

En Android 8, vndservicemanager carga el archivo vndservice_contexts . Los servicios de proveedores que migran a vndservicemanager (y que ya están en el archivo service_contexts antiguo) deben agregarse al nuevo archivo vndservice_contexts .

Etiquetas de servicio

Anteriormente, las etiquetas de servicio como u:object_r:atfwd_service:s0 se definían en un archivo service.te . Ejemplo:

type atfwd_service,      service_manager_type;

En Android 8, debes cambiar el tipo a vndservice_manager_type y mover la regla al archivo vndservice.te . Ejemplo:

type atfwd_service,      vndservice_manager_type;

Reglas del administrador de servicio

Anteriormente, las reglas otorgaban acceso a los dominios para agregar o buscar servicios desde servicemanager . Ejemplo:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;

En Android 8, dichas reglas pueden permanecer vigentes y usar la misma clase. Ejemplo:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;