En esta página, se describen los cambios en el controlador de Binder en Android 8, se proporcionan detalles sobre el uso del IPC de Binder y se enumera la política de SELinux requerida.
Cambios en el controlador de Binder
A partir de Android 8, el framework de Android y las HAL ahora se comunican entre sí con Binder. Como esta comunicación aumenta de forma significativa el tráfico de Binder, Android 8 incluye varias mejoras diseñadas para mantener el IPC de Binder rápido. Los proveedores de SoC y los OEM deben realizar la combinación directamente desde las ramas relevantes de android-4.4, android-4.9 y versiones posteriores del proyecto kernel/common.
Varios dominios (contextos) de Binder
Common-4.4 y versiones posteriores, incluidas las upstreamPara dividir de forma clara el tráfico de Binder entre el código del framework (independiente del dispositivo) y el del proveedor (específico del dispositivo), Android 8 introdujo el concepto de un contexto de Binder. Cada contexto de Binder tiene su propio nodo de dispositivo y su propio administrador de contexto (servicio). Puedes acceder al administrador de contexto solo a través del nodo del dispositivo al que pertenece y, cuando pasas un nodo de Binder a través de un contexto determinado, solo otro proceso puede acceder a él desde ese mismo contexto, lo que aísla por completo los dominios entre sí. Para obtener detalles sobre el uso, consulta vndbinder y vndservicemanager.
Scatter-gather
Common-4.4 y versiones posteriores, incluidas las upstreamEn versiones anteriores de Android, cada dato de una llamada a Binder se copiaba tres veces:
- Una vez para serializarlo en un
Parcel
en el proceso de llamada - Una vez en el controlador de kernel, copia el
Parcel
en el proceso de destino. - Una vez para anular la serialización de
Parcel
en el proceso de destino
Android 8 usa la optimización de dispersión y recopilación para reducir la cantidad de copias de 3 a 1. En lugar de serializar los datos en un Parcel
primero, los datos permanecen en su estructura y diseño de memoria originales, y el controlador los copia de inmediato 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 necesidad de otra copia.
Bloqueo detallado
Common-4.4 y versiones posteriores, incluidas las upstreamEn versiones anteriores de Android, el controlador de Binder usaba un bloqueo global para proteger contra el acceso simultáneo a estructuras de datos críticas. Si bien había una contención mínima por el bloqueo, el problema principal era que, si un subproceso de baja prioridad obtenía el bloqueo y, luego, se le quitaba, podría retrasar seriamente los subprocesos de prioridad más alta que necesitan obtener el mismo bloqueo. Esto causó bloqueos en la plataforma.
Los intentos iniciales para resolver este problema implicaban inhabilitar la usurpación mientras se mantenía el bloqueo global. Sin embargo, esto fue más un hack que una solución real y, finalmente, se rechazó y se descartó. Los intentos posteriores se enfocaron en hacer que el bloqueo fuera más detallado, una versión de la 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 de bloqueo detallado, elaboramos una solución mejorada con una arquitectura de bloqueo diferente y enviamos los cambios en todas las ramas comunes del kernel. Seguimos 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 los dispositivos que se envían con Android 8.
Herencia de prioridad en tiempo real
Common-4.4 y common-4.9 (próximamente en upstream)El controlador de Binder siempre admitía la herencia de prioridad agradable. A medida que una cantidad cada vez mayor de procesos en Android se ejecuta con prioridad en tiempo real, en algunos casos, ahora tiene sentido que, si un subproceso en tiempo real realiza una llamada a Binder, el subproceso en el proceso que controla esa llamada también se ejecute 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 Binder.
Además de la herencia de prioridad a nivel de la transacción, la herencia de prioridad del nodo permite que un nodo (objeto de servicio de Binder) especifique una prioridad mínima a 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 valores agradables, pero Android 8 agrega compatibilidad con la herencia de nodos de políticas de programación en tiempo real.
Cambios en el espacio del usuario
Android 8 incluye todos los cambios en el espacio de usuario necesarios para funcionar con el controlador de Binder actual en el kernel común, con una excepción: la implementación original para inhabilitar 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 Binder (y no por contexto). Por lo tanto, el ioctl no está en la rama común de Android, sino que se envía en nuestros kernels comunes.
El efecto de este cambio es que la herencia de prioridad en tiempo real está inhabilitada de forma predeterminada para todos los nodos. El equipo de rendimiento de Android descubrió que es útil habilitar la herencia de prioridad en tiempo real para todos los nodos del dominio hwbinder
. Para lograr el mismo efecto, elige este cambio en el espacio de usuario.
SHA para kernels comunes
Para obtener los cambios necesarios en el controlador de Binder, sincroniza con el SHA adecuado:
- Common-3.18
cc8b90c121de ANDROID: Binder: No comprueba los permisos de prioridad en el restablecimiento. - Common-4.4
76b376eac7a2 ANDROID: Binder: No comprueba los permisos de prioridad en el restablecimiento. - Common-4.9
ecd972d4f9b5 ANDROID: Binder: No comprueba los permisos de prioridad en el restablecimiento.
Cómo trabajar con IPC de Binder
Históricamente, los procesos de los proveedores han usado la comunicación entre procesos (IPC) de Binder para comunicarse. En Android 8, el nodo del dispositivo /dev/binder
se vuelve exclusivo para los procesos del framework, lo que significa que los procesos del proveedor ya no tienen acceso a él. Los procesos del proveedor pueden acceder a /dev/hwbinder
, pero deben convertir sus interfaces AIDL para usar HIDL. Para los proveedores que quieran seguir usando interfaces AIDL entre procesos de proveedores, Android admite el IPC de Binder como se describe a continuación. En Android 10, AIDL estable permite que todos los procesos usen /dev/binder
y, al mismo tiempo, resuelve las garantías de estabilidad que resolvieron HIDL y /dev/hwbinder
. Para obtener información sobre cómo usar AIDL estable, consulta AIDL para HAL.
vndbinder
Android 8 admite un nuevo dominio de Binder para que lo usen los servicios del proveedor, al que se accede con /dev/vndbinder
en lugar de /dev/binder
. Con la adición de /dev/vndbinder
, Android ahora tiene los siguientes tres dominios de IPC:
Dominio IPC | Descripción |
---|---|
/dev/binder |
IPC entre procesos de framework o app con interfaces AIDL |
/dev/hwbinder |
IPC entre procesos de framework o proveedor con interfaces HIDL
IPC entre procesos de proveedores con interfaces HIDL |
/dev/vndbinder |
IPC entre procesos de proveedores con interfaces AIDL |
Para que aparezca /dev/vndbinder
, asegúrate de que el elemento de configuración del kernel CONFIG_ANDROID_BINDER_DEVICES
esté configurado como "binder,hwbinder,vndbinder"
(este es el valor predeterminado en los árboles de kernel comunes de Android).
Por lo general, los procesos del proveedor no abren el controlador de Binder directamente y, en su lugar, se vinculan con la biblioteca del espacio de usuario libbinder
, que abre el controlador de Binder. Si agregas un método para ::android::ProcessState()
, se selecciona el controlador de Binder para libbinder
. Los procesos de los proveedores deben llamar a este método antes de llamar a ProcessState,
IPCThreadState
o antes de realizar cualquier llamada a Binder en general. Para usarla, realiza la siguiente llamada después de la main()
de un proceso del proveedor (cliente y servidor):
ProcessState::initWithDriver("/dev/vndbinder");
vndservicemanager
Anteriormente, los servicios de Binder se registraban con servicemanager
,
en los que otros procesos podían recuperarlos. En Android 8, los procesos del framework y de la app ahora usan servicemanager
de forma exclusiva, y los procesos del proveedor ya no pueden acceder a él.
Sin embargo, los servicios de proveedores ahora pueden usar vndservicemanager
, una instancia nueva de servicemanager
que usa /dev/vndbinder
en lugar de /dev/binder
y que se compila a partir de las mismas fuentes que el framework servicemanager
. Los procesos de proveedores no necesitan realizar cambios para comunicarse con vndservicemanager
. Cuando un proceso de proveedor abre /dev/vndbinder
, las búsquedas de servicios se dirigen automáticamente a vndservicemanager
.
El objeto binario vndservicemanager
se incluye en los archivos de configuración de make del dispositivo predeterminado de Android.
Política de SELinux
Los procesos de proveedores que desean usar la funcionalidad de Binder para comunicarse entre sí necesitan lo siguiente:
- Acceso a
/dev/vndbinder
{transfer, call}
de Binder se conecta avndservicemanager
.binder_call(A, B)
para cualquier dominio de proveedor A que desee llamar al dominio de proveedor B a través de la interfaz del vinculador de proveedores.- Permiso para los servicios de
{add, find}
envndservicemanager
Para cumplir con los requisitos 1 y 2, usa la macro vndbinder_use()
:
vndbinder_use(some_vendor_process_domain);
Para cumplir con el requisito 3, el binder_call(A, B)
para los procesos del 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 con el requisito 4, debes realizar cambios en la forma en que se manejan los nombres, las etiquetas y las reglas de los servicios.
Para obtener más información sobre SELinux, consulta Linux con seguridad mejorada en Android. Para obtener detalles sobre SELinux en Android 8.0, consulta SELinux para Android 8.0.
Nombres de los servicios
Anteriormente, el proveedor procesaba los nombres de servicio registrados en un archivo service_contexts
y agregaba 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
anterior) se deben agregar 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 de servicemanager
Anteriormente, las reglas otorgaban a los dominios acceso para agregar o encontrar servicios desde servicemanager
. Ejemplo:
allow atfwd atfwd_service:service_manager find; allow some_vendor_app atfwd_service:service_manager add;
En Android 8, esas reglas pueden permanecer en su lugar y usar la misma clase. Ejemplo:
allow atfwd atfwd_service:service_manager find; allow some_vendor_app atfwd_service:service_manager add;