El lenguaje de definición de la interfaz HAL o HIDL es un lenguaje de descripción de interfaz (IDL) que se la interfaz entre una HAL y a sus usuarios. El HIDL permite especificar tipos y llamadas a métodos, que se recopilan en interfaces y paquetes. En términos más generales, el HIDL es un sistema de comunicación entre bases de código que pueden compilarse de forma independiente.
El HIDL está diseñado para usarse en la comunicación entre procesos (IPC). Las HAL creadas con HDL se llamadas HAL Binderized, ya que se pueden comunicar con otras capas de arquitectura usando Binder llamadas de comunicación entre procesos (IPC). Las HAL enlazadas se ejecutan en un proceso independiente del cliente que los usa. Para bibliotecas que deben estar vinculadas a un proceso, una transferencia (no compatible con Java).
El HIDL especifica estructuras de datos y firmas de métodos, organizadas en interfaces (similar a una clase) que se recopilan en paquetes. La sintaxis del HIDL les resulta familiar a C++ y programadores de Java, pero con un conjunto diferente de palabras clave. HIDL también usa anotaciones de estilo Java.
Terminología
En esta sección, se usan los siguientes términos relacionados con el HIDL:
encubierto | Indica que el HIDL se está usando para llamadas de procedimiento remoto entre procesos. que se implementan a través de un mecanismo similar a Binder. Consulta también transferencia. |
---|---|
devolución de llamada, asíncrona | Interfaz que entrega un usuario de HAL, que se pasa a la HAL (con un método HIDL) que la HAL llama para mostrar datos en cualquier momento. |
devolución de llamada, síncrona | Muestra datos al cliente desde la implementación del método HIDL de un servidor. No se usa para métodos que muestran un valor nulo o un solo valor primitivo. |
cliente | Proceso que llama a los métodos de una interfaz en particular. Un framework de HAL o de Android puede ser un cliente de una interfaz y un servidor de otra. Consulta también transferencia. |
extiende | Indica una interfaz que agrega métodos o tipos a otra interfaz. Una interfaz solo puede extender una interfaz más. Puede usarse para un menor de versión en el mismo nombre de paquete o para un paquete nuevo (p.ej., un proveedor para compilar en un paquete anterior. |
genera | Indica un método de interfaz que muestra valores al cliente. Para devolver un valor no primitivo, o más de un valor, una función de devolución de llamada síncrona cuando se genera un modelo de AA. |
interfaz | Colección de métodos y tipos. Traducido a una clase en C++ o Java. Todo los métodos de una interfaz se llaman en la misma dirección: un proceso de cliente invoca métodos implementados por un proceso del servidor. |
ida | Cuando se aplica a un método HIDL, indica que el método no devuelve valores y no se bloquea. |
paquete | Es una colección de interfaces y tipos de datos que comparten una versión. |
transferencia | Modo de HIDL en el que el servidor es una biblioteca compartida, dlopen
por el cliente. En el modo de transferencia, el cliente y el servidor son el mismo proceso, pero
bases de código separadas. Solo se usa para incorporar bases de código heredadas en el modelo HIDL.
Consulta también enlazadas. |
servidor | Proceso que implementa métodos de una interfaz. Consulta también transferencia. |
transporte | Infraestructura de HIDL que traslada datos entre el servidor y el cliente |
version | Es la versión de un paquete. Consiste en dos números enteros, uno mayor y otro menor. Menor los incrementos de versión pueden agregar (pero no cambiar) tipos y métodos. |
Diseño HIDL
El objetivo del HIDL es que el framework de Android se pueda reemplazar sin tener que
volver a compilar las HAL. Los proveedores o creadores del SOC compilan las HAL
/vendor
en el dispositivo, lo que habilita el framework de Android en su propio
por OTA sin volver a compilar las HAL.
El diseño del HIDL equilibra las siguientes inquietudes:
- Interoperabilidad. Crea interfaces interoperables confiables entre procesos que pueden compilarse con varias arquitecturas, cadenas de herramientas y configuraciones de compilación. Las interfaces HIDL tienen control de versiones y no se pueden cambiar después de su publicación.
- Eficiencia. HIDL intenta minimizar la cantidad de copias las operaciones. Los datos definidos por HIDL se entregan al código C++ en el diseño estándar C++. de datos que se pueden usar sin desempaquetarlos. El HIDL también brinda información de memoria caché y, como los RPC son intrínsecamente lentos, el HIDL admite dos maneras de transferir datos sin usar una llamada RPC: memoria compartida y una Fila de mensajes (FMQ).
- Son intuitivas. El HIDL evita problemas delicados sobre la propiedad de la memoria al
usando solo parámetros
in
para RPC (consulta En Android lenguaje de definición de la interfaz (AIDL) valores que no pueden ser eficientemente que se muestran de los métodos se devuelven a través de funciones de devolución de llamada. Tampoco se pasan datos a HIDL para la transferencia ni recibir datos del HIDL cambia la propiedad del datos, la propiedad siempre se mantiene con la función que realiza la llamada. Los datos necesitan se mantienen solo durante la duración de la función llamada y pueden destruirse inmediatamente después de que se muestre la función llamada.
Usar el modo de transferencia
Para actualizar dispositivos con versiones anteriores de Android a Android O, puedes unir las HAL convencionales (y las heredadas) en una nueva interfaz HIDL que entrega HAL en modos vinculados y del mismo proceso (transferencia). Esta unión está transparente para la HAL y el framework de Android.
El modo de transferencia solo está disponible para implementaciones y clientes de C++. Los dispositivos que ejecutan versiones anteriores de Android no tienen HAL escritas en Java, por lo que Las HAL de Java están vinculadas de forma inherente.
Archivos de encabezado de transferencia
Cuando se compila un archivo .hal
, hidl-gen
genera un
archivo de encabezado de transferencia adicional BsFoo.h
, además de los encabezados
se usan para la comunicación con Binder; este encabezado define las funciones
dlopen
ed. Dado que las HAL de transferencia se ejecutan en el mismo proceso,
los métodos de transferencia se invocan, en la mayoría de los casos, los métodos de transferencia se invocan
llamada a función (mismo subproceso). Los métodos oneway
se ejecutan en su propio subproceso
ya que no están diseñadas para esperar a que la HAL las procese (esto significa que cualquier HAL
que usa métodos oneway
en modo de transferencia debe ser seguro para subprocesos).
Dado un IFoo.hal
, BsFoo.h
une el valor generado por HIDL
para proporcionar funciones adicionales (como hacer que oneway
transacciones se ejecutan en otro subproceso). Este archivo es similar a
BpFoo.h
. Sin embargo, en lugar de pasar llamadas a IPC mediante Binder, el método
se invocan directamente las funciones deseadas. Futuras implementaciones de HAL
puede proporcionar varias implementaciones, como FooFast HAL y una
HAL de FooPreciso. En esos casos, se crearía un archivo para cada implementación adicional
crear (p.ej., PTFooFast.cpp
y
PTFooAccurate.cpp
).
Vinculación de HAL de transferencia
Puedes enlazar implementaciones de HAL que admiten el modo de transferencia. Dado un
Interfaz a.b.c.d@M.N::IFoo
de la HAL, se crean dos paquetes:
a.b.c.d@M.N::IFoo-impl
Contiene la implementación de la HAL y expone la funciónIFoo* HIDL_FETCH_IFoo(const char* name)
. Activada dispositivos heredados, este paquete sedlopen
y la implementación se se creó una instancia conHIDL_FETCH_IFoo
. Puedes generar el código base usandohidl-gen
y-Lc++-impl
, y-Landroidbp-impl
a.b.c.d@M.N::IFoo-service
Abre la HAL de transferencia y se registra como un servicio vinculado, lo que permite la misma implementación de HAL. para usar tanto en modo de transferencia como en Binder.
Con el tipo IFoo
, puedes llamar a sp<IFoo>
IFoo::getService(string name, bool getStub)
para obtener acceso a una instancia
de IFoo
. Si getStub
es verdadero, getService
intenta abrir la HAL solo en modo de transferencia. Si getStub
es
false, getService
intenta encontrar un servicio vinculado. si esa
falla, intenta encontrar el servicio de transferencia. El getStub
el parámetro de configuración nunca se debe usar, excepto en
defaultPassthroughServiceImplementation
(los dispositivos que se lanzan con
Android O son dispositivos totalmente vinculados, por lo que abrir un servicio en modo de transferencia
no está permitido).
Gramática HIDL
Por diseño, el lenguaje HIDL es similar al C (pero no usa el
preprocesador). Todos los signos de puntuación que no se describen a continuación (excepto el uso obvio
de =
y |
) es parte de la gramática.
Nota: Para obtener más información sobre el estilo de código HIDL, consulta la Guía de estilo del código.
/** */
indica un comentario de documentación. Estas se pueden aplicar solo para declaraciones de tipo, método, campo y enum./* */
indica un comentario de varias líneas.//
indica un comentario hasta el final de la línea. Además de//
, los saltos de línea son los mismos que cualquier otro espacio en blanco.- En la gramática de ejemplo que aparece a continuación, el texto desde
//
hasta el final de línea no forma parte de la gramática, sino que es un comentario sobre ella. [empty]
significa que el término puede estar vacío.?
después de un literal o término significa que es opcional....
indica una secuencia que contiene cero o más elementos con separando la puntuación según se indica. No hay argumentos variádicos en el HIDL.- La coma separa los elementos de secuencia.
- El punto y coma termina cada elemento, incluido el último.
- MAYÚSCULA es un no terminal.
italics
es una familia de tokens, comointeger
oidentifier
(C estándar de análisis de datos).constexpr
es una expresión constante de estilo C (como1 + 1
y1L << 3
).import_name
es un nombre de paquete o interfaz, calificado. como se describe en HIDL Control de versiones.words
en minúscula son tokens literales.
Ejemplo:
ROOT = PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... } // not for types.hal | PACKAGE IMPORTS ITEM ITEM... // only for types.hal; no method definitions ITEM = ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?; | safe_union identifier { UFIELD; UFIELD; ...}; | struct identifier { SFIELD; SFIELD; ...}; // Note - no forward declarations | union identifier { UFIELD; UFIELD; ...}; | enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar | typedef TYPE identifier; VERSION = integer.integer; PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION; PREAMBLE = interface identifier EXTENDS EXTENDS = <empty> | extends import_name // must be interface, not package GENERATES = generates (FIELD, FIELD ...) // allows the Binder interface to be used as a type // (similar to typedef'ing the final identifier) IMPORTS = [empty] | IMPORTS import import_name; TYPE = uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t | float | double | bool | string | identifier // must be defined as a typedef, struct, union, enum or import // including those defined later in the file | memory | pointer | vec<TYPE> | bitfield<TYPE> // TYPE is user-defined enum | fmq_sync<TYPE> | fmq_unsync<TYPE> | TYPE[SIZE] FIELD = TYPE identifier UFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...}; | struct identifier { FIELD; FIELD; ...}; | union identifier { FIELD; FIELD; ...}; | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SIZE = // Must be greater than zero constexpr ANNOTATIONS = [empty] | ANNOTATIONS ANNOTATION ANNOTATION = | @identifier | @identifier(VALUE) | @identifier(ANNO_ENTRY, ANNO_ENTRY ...) ANNO_ENTRY = identifier=VALUE VALUE = "any text including \" and other escapes" | constexpr | {VALUE, VALUE ...} // only in annotations ENUM_ENTRY = identifier | identifier = constexpr