El HIDL requiere que todas las interfaces escritas en el HIDL tengan un control de versiones. Después de un HAL interfaz de usuario, se bloquea y se deben realizar más cambios en la a la versión nueva de esa interfaz. Si bien una determinada interfaz publicada no se puede modificado, puede extenderse con otra interfaz.
Estructura del código HIDL
El código HIDL se organiza en reglas definidas por el usuario tipos, interfaces y paquetes:
- Tipos definidos por el usuario (UDT). El HIDL brinda acceso a un conjunto de tipos de datos primitivos que pueden usarse para componer tipos más complejos a través de estructuras, uniones y enumeraciones. Las UDT se pasan a métodos de interfaces de usuario y pueden definirse a nivel de un paquete (común para todos interfaces) o de forma local a una interfaz.
- Interfaces. Como componente básico del HIDL, una interfaz consta de declaraciones de UDT y de métodos. Las interfaces también pueden heredar de otra interfaz.
- Paquetes. Organiza las interfaces HIDL relacionadas y los datos
los tipos en los que operan. Un paquete se identifica con un nombre, una versión y
incluye lo siguiente:
- Archivo de definición de tipo de datos llamado
types.hal
. - Cero o más interfaces, cada una en su propio archivo
.hal
- Archivo de definición de tipo de datos llamado
El archivo de definición de tipo de datos types.hal
contiene solo UDT (todas
las UDT a nivel del paquete se mantienen en un solo archivo). Representaciones en el destino
están disponibles para todas las interfaces del paquete.
Filosofía del control de versiones
Un paquete HIDL (como android.hardware.nfc
), después de
publicado para una versión determinada (como 1.0
), es inmutable. it
no se puede cambiar. Modificaciones en las interfaces del paquete o en cualquier
los cambios en sus UDT solo se pueden producir en otro paquete.
En HIDL, el control de versiones se aplica a nivel de paquete, no a nivel de interfaz y todas las interfaces y UDT de un paquete comparten la misma versión. Paquete versiones siguen la estructura semántica control de versiones sin el nivel de parche ni componentes de metadatos de compilación. En un objeto paquete determinado, un cambio de prioridad de versión secundaria implica la nueva versión de el paquete es retrocompatible con el paquete anterior y un elemento cambio de versión implica que la versión nueva del paquete retrocompatible con el paquete anterior.
Conceptualmente, un paquete puede relacionarse con otro paquete de varias maneras:
- Para nada.
- Extensibilidad retrocompatible a nivel del paquete. Esta
se produce para las nuevas versiones secundarias (próxima revisión incrementada) de un paquete
el nuevo paquete tiene el mismo nombre y la misma versión principal que el paquete anterior, pero
a una versión secundaria superior. Funcionalmente, el nuevo paquete es un superconjunto del antiguo
package, es decir:
- Las interfaces de nivel superior del paquete superior están presentes en el paquete nuevo.
aunque las interfaces pueden tener nuevos métodos, nuevas UDT de interfaz local (las
extensión a nivel de la interfaz que se describe a continuación) y nuevas UDT en
types.hal
- También se pueden agregar interfaces nuevas al paquete nuevo.
- Todos los tipos de datos del paquete superior están presentes en el paquete nuevo. pueden controlarse mediante los métodos (posiblemente reimplementados) del paquete anterior.
- También se pueden agregar nuevos tipos de datos para su uso con cualquier nuevo método de aumento a través de interfaces existentes o a través de interfaces nuevas.
- Las interfaces de nivel superior del paquete superior están presentes en el paquete nuevo.
aunque las interfaces pueden tener nuevos métodos, nuevas UDT de interfaz local (las
extensión a nivel de la interfaz que se describe a continuación) y nuevas UDT en
- Extensibilidad retrocompatible a nivel de la interfaz. La nueva herramienta
también puede extender el paquete original al consistir de manera lógica
que proporcionan funcionalidad adicional, no la principal.
Para ello, es posible que te convenga lo siguiente:
- Las interfaces del paquete nuevo deben recurrir a los tipos de datos del antiguo .
- Las interfaces del paquete nuevo pueden extender las interfaces de uno o más paquetes antiguos paquetes.
- Extiende la incompatibilidad original con versiones anteriores. Este es un el aumento de ingresos de la versión principal del paquete y no es necesario que haya ninguna correlación entre ambos. En la medida en que haya, se puede expresar con una combinación de de la versión anterior del paquete y la herencia de un subconjunto de con interfaces del paquete anterior.
Estructuración de la interfaz
Para lograr una interfaz bien estructurada, agregar nuevos tipos de funcionalidad que no sean parte del diseño original, deberían requerir una modificación en el HIDL interfaz de usuario. Por el contrario, si puedes o esperas realizar un cambio en ambos lados de la interfaz que presenta una nueva funcionalidad sin cambiar la interfaz entonces, la interfaz no está estructurada.
Treble admite componentes de proveedores y sistemas compilados por separado en los que el
vendor.img
en un dispositivo, y el system.img
se puede
compilarse por separado. Todas las interacciones entre vendor.img
y
system.img
debe definirse de forma explícita y exhaustiva para que pueda
seguirán funcionando durante muchos años. Esto incluye muchas plataformas de APIs, pero una gran
es el mecanismo de IPC que usa HIDL para la comunicación entre procesos en la
Límite de system.img
/vendor.img
.
Requisitos
Todos los datos que se pasan por HIDL deben definirse de manera explícita. Para garantizar un y el cliente pueden seguir trabajando juntos incluso cuando se compilan separadas o desarrolladas de forma independiente, los datos deben cumplir con lo siguiente requisitos:
- Se puede describir en HIDL directamente (mediante structs de enumeraciones, etc.) con nombres semánticos y su significado.
- Pueden describirse según una norma pública, como ISO/IEC 7816.
- Puede describirse mediante un estándar de hardware o un diseño físico del hardware.
- Pueden ser datos opacos (como claves públicas, IDs, etc.) si es necesario.
Si se usan datos opacos, solo un lado del HIDL debe leerlos.
interfaz de usuario. Por ejemplo, si el código vendor.img
proporciona un componente en el
system.img
un mensaje de cadena o vec<uint8_t>
esos datos no pueden ser analizados por la system.img
en sí; puede
solo se deben volver a pasar a vendor.img
para interpretar. Cuándo
y pasa un valor de vendor.img
al código del proveedor en
system.img
o a otro dispositivo, el formato de los datos y cómo
debe interpretarse debe describirse exactamente y sigue siendo parte del
interfaz de programación de aplicaciones.
Lineamientos
Deberías poder escribir una implementación o un cliente de una HAL solo con el uso los archivos .hal (es decir, no deberías tener que consultar los datos con los estándares necesarios). Te recomendamos que especifiques el comportamiento requerido exacto. Declaraciones como como “una implementación podría hacer A o B” alentar las implementaciones se relacionan con los clientes con los que se desarrollan.
Diseño del código HIDL
HIDL incluye paquetes principales y de proveedores.
Las interfaces principales del HIDL son las que especifica Google. Los paquetes a los que pertenecen.
comiencen con android.hardware.
y los nombra el subsistema,
potencialmente con niveles anidados de nomenclatura. Por ejemplo, el paquete NFC se llama
android.hardware.nfc
y el paquete de la cámara es
android.hardware.camera
En general, un paquete principal tiene el nombre
android.hardware.
[name1
].[name2
]...
Los paquetes de HIDL tienen una versión además de su nombre. Por ejemplo, el paquete
android.hardware.camera
puede estar en la versión 3.4
. esto es
importante, ya que la versión de un paquete afecta su ubicación en el árbol de fuentes.
Todos los paquetes principales se colocan en hardware/interfaces/
, en la
de compilación. El paquete
android.hardware.
[name1
].[name2
]...
en la versión $m.$n
es inferior
hardware/interfaces/name1/name2/
.../$m.$n/
; paquete
La versión 3.4
de android.hardware.camera
está en el directorio
hardware/interfaces/camera/3.4/.
Existe una asignación hard-coded
entre el prefijo del paquete android.hardware.
y la ruta
hardware/interfaces/
Los paquetes no principales (proveedores) son aquellos producidos por el proveedor del SoC o la ODM. El
de los paquetes que no son principales es vendor.$(VENDOR).hardware.
, en la que
$(VENDOR)
se refiere a un proveedor de SoC, o bien a OEM/ODM. Esto se relaciona con la ruta
vendor/$(VENDOR)/interfaces
en el árbol (esta asignación también es
hard-coded).
Nombres de tipo definidos por el usuario completamente calificados
En el HIDL, cada UDT tiene un nombre completamente calificado que consta del nombre de la UDT,
el nombre del paquete en el que se define la UDT y la versión del paquete. El
el nombre completamente calificado se usa solo cuando se declaran instancias del tipo y
no donde se define el tipo en sí. Por ejemplo, supongamos que package
La versión 1.0
de android.hardware.nfc,
define una struct
con el nombre NfcData
. En el sitio de la declaración (ya sea en
types.hal
o dentro de la declaración de una interfaz), la declaración
simplemente dice:
struct NfcData { vec<uint8_t> data; };
Cuando declares una instancia de este tipo (ya sea dentro de una estructura de datos o como parámetro del método), usa el nombre del tipo completamente calificado:
android.hardware.nfc@1.0::NfcData
La sintaxis general es
PACKAGE@VERSION::UDT
, donde:
PACKAGE
es el nombre separado por puntos de un paquete HIDL. (p.ej.,android.hardware.nfc
).VERSION
es la versión mayor.secundaria separada por puntos. formato del paquete (p.ej.,1.0
).UDT
es el nombre separado por puntos de una UDT de HIDL. Dado que el HIDL admite UDT anidadas y las interfaces HIDL pueden contener UDT (un tipo de declaración anidada), se usan puntos para acceder a los nombres.
Por ejemplo, si la siguiente declaración anidada se definió en el lenguaje
tipos de archivo en la versión del paquete android.hardware.example
1.0
// types.hal package android.hardware.example@1.0; struct Foo { struct Bar { // … }; Bar cheers; };
El nombre completamente calificado de Bar
es
android.hardware.example@1.0::Foo.Bar
Si, además de estar en
en el paquete anterior, la declaración anidada estaba en una interfaz llamada
IQuux
:
// IQuux.hal package android.hardware.example@1.0; interface IQuux { struct Foo { struct Bar { // … }; Bar cheers; }; doSomething(Foo f) generates (Foo.Bar fb); };
El nombre completamente calificado de Bar
es
android.hardware.example@1.0::IQuux.Foo.Bar
En ambos casos, Bar
solo puede denominarse Bar
.
dentro del alcance de la declaración de Foo
. En el paquete o
nivel de interfaz, debes hacer referencia a Bar
a través de Foo
:
Foo.Bar
, como en la declaración del método doSomething
arriba. Como alternativa, puedes declarar el método de forma más detallada de la siguiente manera:
// IQuux.hal doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);
Valores de enumeración completamente calificados
Si una UDT es de tipo enum, cada valor del tipo enum tiene un
nombre completamente calificado que comienza con el nombre completamente calificado del tipo enum,
seguido de dos puntos y, luego, el nombre del valor de enumeración. Por ejemplo:
se supone que el paquete android.hardware.nfc,
versión 1.0
define un tipo de enumeración NfcStatus
:
enum NfcStatus { STATUS_OK, STATUS_FAILED };
Cuando se hace referencia a STATUS_OK
, el nombre completamente calificado es el siguiente:
android.hardware.nfc@1.0::NfcStatus:STATUS_OK
La sintaxis general es
PACKAGE@VERSION::UDT:VALUE
,
En el ejemplo anterior, se ilustra lo siguiente:
PACKAGE@VERSION::UDT
es el tiene exactamente el mismo nombre completamente calificado para el tipo enum.VALUE
es el nombre del valor.
Reglas de inferencia automática
No es necesario especificar un nombre de UDT completamente calificado. Un nombre de UDT puede omite con seguridad lo siguiente:
- El paquete, p.ej.,
@1.0::IFoo.Type
- Paquete y versión, p.ej.,
IFoo.Type
El HIDL intenta completar el nombre usando reglas de interferencia automática (regla inferior número significa mayor prioridad).
Regla 1
Si no se proporcionan el paquete y la versión, se intenta realizar una búsqueda de nombre local. Ejemplo:
interface Nfc { typedef string NfcErrorMessage; send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m); };
NfcErrorMessage
se busca de forma local, y typedef
por encima de ella. NfcData
también se busca de forma local, pero tal como es
no definidos localmente, se usan las reglas 2 y 3. @1.0::NfcStatus
proporciona una versión, por lo que la regla 1 no se aplica.
Regla 2
Si la regla 1 falla y falta un componente del nombre completamente calificado
(paquete, versión o paquete y versión), el componente se autocompleta con
información del paquete actual. El compilador HIDL luego busca
archivo actual (y todas las importaciones) para encontrar el nombre calificado completamente completado.
Con el ejemplo anterior, supone la declaración de ExtendedNfcData
.
se realizó en el mismo paquete (android.hardware.nfc
) con la misma
versión (1.0
) como NfcData
, de la siguiente manera:
struct ExtendedNfcData { NfcData base; // … additional members };
El compilador HIDL completa el nombre del paquete y el nombre de la versión del
Paquete actual para producir el nombre de la UDT completamente calificado
android.hardware.nfc@1.0::NfcData
Como el nombre existe en
paquete actual (suponiendo que se haya importado correctamente), se utiliza para el
declaración.
Un nombre en el paquete actual se importa solo si se cumple una de las siguientes condiciones: verdadero:
- Se importa de manera explícita con una sentencia
import
. - Se define en
types.hal
en el paquete actual.
Se sigue el mismo proceso si NfcData
fue calificado solo por
el número de versión:
struct ExtendedNfcData { // autofill the current package name (android.hardware.nfc) @1.0::NfcData base; // … additional members };
Regla 3
Si la regla 2 no produce una coincidencia (la UDT no está definida en el
), el compilador HIDL busca una coincidencia dentro de todos los paquetes importados.
Con el ejemplo anterior, supongamos que ExtendedNfcData
se declara en
versión 1.1
del paquete android.hardware.nfc
,
1.1
importa 1.0
como corresponde (consulta
extensiones a nivel de paquete) y la definición
especifica solo el nombre de la UDT:
struct ExtendedNfcData { NfcData base; // … additional members };
El compilador busca cualquier UDT llamada NfcData
y la encuentra en
android.hardware.nfc
en la versión 1.0
, lo que da como resultado un
UDT completamente calificada de android.hardware.nfc@1.0::NfcData
. Si hay más
más de una coincidencia para una UDT parcialmente calificada, el compilador HIDL
arroja un error.
Ejemplo
Con la regla 2, se da prioridad a un tipo importado definido en el paquete actual sobre un tipo importado desde otro paquete:
// hardware/interfaces/foo/1.0/types.hal package android.hardware.foo@1.0; struct S {}; // hardware/interfaces/foo/1.0/IFooCallback.hal package android.hardware.foo@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/types.hal package android.hardware.bar@1.0; typedef string S; // hardware/interfaces/bar/1.0/IFooCallback.hal package android.hardware.bar@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/IBar.hal package android.hardware.bar@1.0; import android.hardware.foo@1.0; interface IBar { baz1(S s); // android.hardware.bar@1.0::S baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback };
S
se interpola comoandroid.hardware.bar@1.0::S
y se encuentra enbar/1.0/types.hal
(porquetypes.hal
se actualiza automáticamente importados).IFooCallback
se interpola comoandroid.hardware.bar@1.0::IFooCallback
con la regla 2, pero No se puede encontrar porque no se importóbar/1.0/IFooCallback.hal
. automáticamente (comotypes.hal
). Por lo tanto, la regla 3 lo resuelveandroid.hardware.foo@1.0::IFooCallback
, que se importa a través deimport android.hardware.foo@1.0;
).
tipos.hal
Cada paquete HIDL contiene un archivo types.hal
que contiene UDT
que se comparten entre todas las interfaces que participan en ese paquete. Tipos de HIDL
siempre son públicos; independientemente de si una UDT se declara en
types.hal
o dentro de una declaración de interfaz, estos tipos son
accesibles fuera del alcance en el que se definen. types.hal
no está diseñado para describir la API pública de un paquete, sino para alojar UDT
que usan todas las interfaces dentro del paquete. Debido a la naturaleza del HIDL, todas las UDT
son parte de la interfaz.
types.hal
consta de UDT y sentencias import
.
Debido a que types.hal
está disponible para todas las interfaces de la
(es una importación implícita), estas sentencias import
se
a nivel del paquete por definición. Las UDT en types.hal
también pueden incorporar
Así, se importan las UDT y las interfaces.
Por ejemplo, para IFoo.hal
:
package android.hardware.foo@1.0; // whole package import import android.hardware.bar@1.0; // types only import import android.hardware.baz@1.0::types; // partial imports import android.hardware.qux@1.0::IQux.Quux; // partial imports import android.hardware.quuz@1.0::Quuz;
Se importaron los siguientes elementos:
android.hidl.base@1.0::IBase
(implícita)android.hardware.foo@1.0::types
(implícita)- Todo el contenido de
android.hardware.bar@1.0
(incluidos todos interfaces y sutypes.hal
) types.hal
deandroid.hardware.baz@1.0::types
(las interfaces deandroid.hardware.baz@1.0
no se importan)IQux.hal
ytypes.hal
deandroid.hardware.qux@1.0
Quuz
desdeandroid.hardware.quuz@1.0
(suponiendo queQuuz
se define entypes.hal
, se define Se analiza el archivotypes.hal
, pero es de tipos distintos deQuuz
no se importan).
Control de versiones a nivel de la interfaz
Cada interfaz dentro de un paquete reside en su propio archivo. El paquete que
a la que pertenece la interfaz se declara en la parte superior de la interfaz a través del
package
. Después de la declaración del paquete, cero o más
las importaciones a nivel de la interfaz (parciales o de paquete completo). Por ejemplo:
package android.hardware.nfc@1.0;
En HIDL, las interfaces pueden heredar de otras interfaces mediante
Palabra clave extends
. Para que una interfaz extienda otra,
debe tener acceso a él mediante una sentencia import
. El nombre del
que se extiende (la interfaz base) sigue las reglas de type-name
calificación que se explicó anteriormente. Una interfaz solo puede heredar contenido de una interfaz.
El HIDL no admite la herencia múltiple.
Los siguientes ejemplos de control de versiones de uprev usan el siguiente paquete:
// types.hal package android.hardware.example@1.0 struct Foo { struct Bar { vec<uint32_t> val; }; }; // IQuux.hal package android.hardware.example@1.0 interface IQuux { fromFooToBar(Foo f) generates (Foo.Bar b); }
Reglas de Uprev
Para definir un paquete package@major.minor
, puede ser A o todos los de B.
debe ser verdadero:
Regla A | "Es una versión secundaria inicial": Todas las versiones secundarias anteriores,
package@major.0 , package@major.1 , ...,
No se debe definir package@major.(minor-1) .
|
---|
Regla B | Todas las siguientes afirmaciones son verdaderas:
|
---|
Debido a la regla A:
- El paquete puede comenzar con cualquier número de versión secundario (por ejemplo,
android.hardware.biometrics.fingerprint
comienza en@2.1
). - El requisito "
android.hardware.foo@1.0
no está definido" significa el directoriohardware/interfaces/foo/1.0
ni siquiera debería existir.
Sin embargo, la regla A no afecta a un paquete con el mismo nombre, sino con un
diferente versión principal (por ejemplo,
android.hardware.camera.device
tiene @1.0
y
Se definió @3.2
; No es necesario que @3.2
interactúe con
@1.0
). Por lo tanto, @3.2::IExtFoo
puede extender
@1.0::IFoo
Si el nombre del paquete es diferente,
package@major.minor::IBar
se puede extender desde una interfaz con un
nombre diferente (por ejemplo, android.hardware.bar@1.0::IBar
puede
extender android.hardware.baz@2.2::IBaz
). Si una interfaz no
declarar explícitamente un supertipo con la palabra clave extend
,
extiende android.hidl.base@1.0::IBase
(excepto IBase
)
a sí mismo).
B.2 y B.3 deben seguirse al mismo tiempo. Por ejemplo, incluso si
android.hardware.foo@1.1::IFoo
extiende
android.hardware.foo@1.0::IFoo
para pasar la regla B.2, si un
android.hardware.foo@1.1::IExtBar
extiende
android.hardware.foo@1.0::IBar
, todavía no es un uprev válido.
Interfaces de Uprev
Para aumentar los ingresos de android.hardware.example@1.0
(definidos anteriormente) a
@1.1
// types.hal package android.hardware.example@1.1; import android.hardware.example@1.0; // IQuux.hal package android.hardware.example@1.1 interface IQuux extends @1.0::IQuux { fromBarToFoo(Foo.Bar b) generates (Foo f); }
Este es un nivel de paquete import
de la versión 1.0
de
android.hardware.example
en types.hal
Si bien no hay
Las UDT se agregan en la versión 1.1
del paquete; las referencias a las UDT en
la versión 1.0
, por lo que la importación a nivel del paquete
en types.hal
. (El mismo efecto se podría haber logrado con un
importación a nivel de interfaz en IQuux.hal
).
En extends @1.0::IQuux
, en la declaración de
IQuux
, especificamos la versión de IQuux
que se implementará
heredado (es necesario desambiguar porque IQuux
se usa para
declarar una interfaz y heredar de una interfaz). Como las declaraciones
es simplemente nombres que heredan todos los atributos de paquetes y versiones en el sitio del
la declaración, la desambiguación debe realizarse en el nombre de la interfaz base; nosotros
también podría haber usado la UDT completamente calificada, pero
redundantes.
La nueva interfaz IQuux
no vuelve a declarar el método.
fromFooToBar()
que hereda de @1.0::IQuux
; simplemente
Enumera el método nuevo que agrega fromBarToFoo()
. En HIDL, heredado
Los métodos no se pueden volver a declarar en las interfaces secundarias, por lo que
la interfaz IQuux
no puede declarar fromFooToBar()
de forma explícita.
Convenciones de Uprev
A veces, los nombres de las interfaces deben cambiar el nombre de la interfaz que se extiende. Recomendaciones que las extensiones de enumeración, structs y uniones tienen el mismo nombre que lo que extienden a menos que sean lo suficientemente diferentes como para justificar un nombre nuevo. Ejemplos:
// in parent hal file enum Brightness : uint32_t { NONE, WHITE }; // in child hal file extending the existing set with additional similar values enum Brightness : @1.0::Brightness { AUTOMATIC }; // extending the existing set with values that require a new, more descriptive name: enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };
Si un método puede tener un nombre semántico nuevo (por ejemplo,
fooWithLocation
) y, luego, se prefiere. De lo contrario, debería ser
de forma similar a lo que extiende. Por ejemplo, el método
foo_1_1
en @1.1::IFoo
puede reemplazar la funcionalidad
del método foo
en @1.0::IFoo
si no hay una
nombre alternativo.
Control de versiones a nivel de paquete
El control de versiones de HIDL se produce a nivel del paquete. después de que se publica un paquete, es inmutable (su conjunto de interfaces y UDT no se puede cambiar). Los paquetes pueden se relacionan entre sí de varias maneras, todas ellas se pueden expresar a través de un combinación de herencia a nivel de interfaz y creación de UDT por composición.
Sin embargo, hay un tipo de relación que está estrictamente definido y debe aplicarse: Herencia retrocompatible a nivel del paquete. En este caso, el parent. Es el paquete del que se heredará. child es el que extiende el superior. Nivel de paquete y las reglas de herencia retrocompatibles son las siguientes:
- Todas las interfaces de nivel superior del paquete superior heredan de las interfaces del del paquete secundario.
- Las interfaces nuevas también se pueden agregar al paquete nuevo (sin restricciones sobre relaciones con otras interfaces en otros paquetes).
- También se pueden agregar nuevos tipos de datos para su uso con cualquier nuevo método de aumento a través de interfaces existentes o a través de interfaces nuevas.
Estas reglas se pueden implementar mediante la herencia a nivel de la interfaz HIDL y la UDT composiciones, pero requieren conocimientos de metanivel para conocer estas relaciones constituyen una extensión de paquete compatible con versiones anteriores. Este conocimiento se infiere de la siguiente manera:
Si un paquete cumple con este requisito, hidl-gen
aplica
y las reglas de retrocompatibilidad.