El estilo de código de HIDL se asemeja al código C++ en el framework de Android, con sangría de 4 espacios y nombres de archivo en mayúsculas y minúsculas. Las declaraciones de paquetes, las importaciones y los docstrings son similares a los de Java, con ligeras modificaciones.
En los siguientes ejemplos de IFoo.hal
y types.hal
, se ilustran los estilos de código de HIDL y se proporcionan vínculos rápidos a los detalles de cada estilo (se omitieron IFooClientCallback.hal
, IBar.hal
y IBaz.hal
).
hardware/interfaces/foo/1.0/IFoo.hal |
---|
/* * (License Notice) */ package android.hardware.foo@1.0; import android.hardware.bar@1.0::IBar; import IBaz; import IFooClientCallback; /** * IFoo is an interface that… */ interface IFoo { /** * This is a multiline docstring. * * @return result 0 if successful, nonzero otherwise. */ foo() generates (FooStatus result); /** * Restart controller by power cycle. * * @param bar callback interface that… * @return result 0 if successful, nonzero otherwise. */ powerCycle(IBar bar) generates (FooStatus result); /** Single line docstring. */ baz(); /** * The bar function. * * @param clientCallback callback after function is called * @param baz related baz object * @param data input data blob */ bar(IFooClientCallback clientCallback, IBaz baz, FooData data); }; |
hardware/interfaces/foo/1.0/types.hal |
---|
/* * (License Notice) */ package android.hardware.foo@1.0; /** Replied status. */ enum Status : int32_t { OK, /* invalid arguments */ ERR_ARG, /* note, no transport related errors */ ERR_UNKNOWN = -1, }; struct ArgData { int32_t[20] someArray; vec<uint8_t> data; }; |
Convenciones de nombres
Los nombres de las funciones, las variables y los nombres de archivo deben ser descriptivos. Evita las abreviaturas excesivas. Trata los acrónimos como palabras (por ejemplo, usa INfc
en lugar de INFC
).
Estructura del directorio y nombres de archivos
La estructura del directorio debería aparecer de la siguiente manera:
ROOT-DIRECTORY
MODULE
SUBMODULE
(opcional, puede ser más de un nivel)VERSION
Android.mk
IINTERFACE_1.hal
IINTERFACE_2.hal
…
IINTERFACE_N.hal
types.hal
(opcional)
En la que:
ROOT-DIRECTORY
es:hardware/interfaces
para paquetes principales de HIDL.vendor/VENDOR/interfaces
para paquetes de proveedores, en los queVENDOR
hace referencia a un proveedor de SoC o un OEM/ODM.
MODULE
debe ser una palabra en minúsculas que describa el subsistema (por ejemplo,nfc
). Si se necesita más de una palabra, usaSUBMODULE
anidado. Puede haber más de un nivel de anidación.VERSION
debe ser la misma versión (major.minor) que se describe en Versiones.IINTERFACE_X
debe ser el nombre de la interfaz conUpperCamelCase
/PascalCase
(por ejemplo,INfc
), como se describe en Nombres de interfaz.
Ejemplo:
hardware/interfaces
nfc
1.0
Android.mk
INfc.hal
INfcClientCallback.hal
types.hal
Nota: Todos los archivos deben tener permisos no ejecutables (en Git).
Nombres de los paquetes
Los nombres de los paquetes deben usar el siguiente formato de nombre completamente calificado (FQN) (denominado PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
En la que:
PACKAGE
es el paquete que se asigna aROOT-DIRECTORY
. En particular,PACKAGE
tiene las siguientes características:android.hardware
para paquetes HIDL principales (asignación ahardware/interfaces
).vendor.VENDOR.hardware
para paquetes de proveedores, en los queVENDOR
hace referencia a un proveedor de SoC o un OEM/ODM (asignación avendor/VENDOR/interfaces
).
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
son los mismos nombres de carpetas de la estructura que se describe en Estructura de directorio.- Los nombres de los paquetes deben estar en minúsculas. Si tienen más de una palabra, las palabras deben usarse como submódulos o escribirse en
snake_case
. - No se permite usar espacios.
El FQDN siempre se usa en las declaraciones de paquetes.
Versiones
Las versiones deben tener el siguiente formato:
MAJOR.MINOR
Tanto la versión MAJOR como la MINOR deben ser un solo número entero. HIDL usa reglas de control de versión semántica.
Importaciones
Una importación tiene uno de los siguientes tres formatos:
- Importaciones de paquetes completos:
import PACKAGE-NAME;
- Importaciones parciales:
import PACKAGE-NAME::UDT;
(o, si el tipo importado está en el mismo paquete,import UDT;
- Importaciones de solo tipos:
import PACKAGE-NAME::types;
PACKAGE-NAME
sigue el formato que se indica en Nombres de paquetes. El types.hal
del paquete actual (si existe) se importa automáticamente (no lo importes de forma explícita).
Nombres completamente calificados (FQN)
Usa nombres completamente calificados para una importación de tipo definida por el usuario solo cuando sea necesario.
Omite PACKAGE-NAME
si el tipo de importación está en el mismo paquete. Un FQDN no debe contener espacios. Ejemplo de un nombre completamente calificado:
android.hardware.nfc@1.0::INfcClientCallback
En otro archivo de android.hardware.nfc@1.0
, haz referencia a la interfaz anterior como INfcClientCallback
. De lo contrario, usa solo el nombre completamente calificado.
Cómo agrupar y ordenar las importaciones
Usa una línea vacía después de la declaración del paquete (antes de las importaciones). Cada importación debe ocupar una sola línea y no debe tener sangría. Agrupa las importaciones en el siguiente orden:
- Otros paquetes
android.hardware
(usa nombres completamente calificados). - Otros paquetes
vendor.VENDOR
(usa nombres completamente calificados).- Cada proveedor debe ser un grupo.
- Ordena los proveedores alfabéticamente.
- Importaciones de otras interfaces en el mismo paquete (usa nombres simples).
Usa una línea vacía entre los grupos. Dentro de cada grupo, ordena las importaciones alfabéticamente. Ejemplo:
import android.hardware.nfc@1.0::INfc; import android.hardware.nfc@1.0::INfcClientCallback; /* Importing the whole module. */ import vendor.barvendor.bar@3.1; import vendor.foovendor.foo@2.2::IFooBar; import vendor.foovendor.foo@2.2::IFooFoo; import IBar; import IFoo;
Nombres de las interfaces
Los nombres de las interfaces deben comenzar con una I
, seguida de un nombre UpperCamelCase
/PascalCase
. Se debe definir una interfaz con el nombre IFoo
en el archivo IFoo.hal
. Este archivo solo puede contener definiciones para la interfaz IFoo
(la interfaz INAME
debe estar en INAME.hal
).
Funciones
Para los nombres de las funciones, los argumentos y los nombres de las variables que se muestran, usa lowerCamelCase
. Ejemplo:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Nombres de campos de struct y unión
Para los nombres de campos de struct o unión, usa lowerCamelCase
. Ejemplo:
struct FooReply { vec<uint8_t> replyData; }
Nombres de tipos
Los nombres de tipo se refieren a definiciones de uniones o estructuras, definiciones de tipos de enum y typedef
. Para estos nombres, usa UpperCamelCase
/PascalCase
. Ejemplos:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Enum. de valores
Los valores de enumeración deben ser UPPER_CASE_WITH_UNDERSCORES
. Cuando pases valores de enum como argumentos de la función y los muestres como resultados de la función, usa el tipo de enum real (no el tipo de número entero subyacente). Ejemplo:
enum NfcStatus : int32_t { HAL_NFC_STATUS_OK = 0, HAL_NFC_STATUS_FAILED = 1, HAL_NFC_STATUS_ERR_TRANSPORT = 2, HAL_NFC_STATUS_ERR_CMD_TIMEOUT = 3, HAL_NFC_STATUS_REFUSED = 4 };
Nota: El tipo subyacente de un tipo de enum se declara de forma explícita después de los dos puntos. Como no depende del compilador, usar el tipo de enumeración real es más claro.
Para los nombres completamente calificados de los valores de enum, se usa un dos puntos entre el nombre del tipo de enum y el nombre del valor de enum:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
No debe haber espacios dentro de un nombre completamente calificado. Usa un nombre completamente calificado solo cuando sea necesario y omite las partes innecesarias. Ejemplo:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
Comentarios
Para un comentario de una sola línea, //
, /* */
y /** */
son adecuados.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
-
Usa
/* */
para los comentarios. Si bien HIDL admite//
para comentarios, se desaconsejan porque no aparecen en el resultado generado. - Usa
/** */
para la documentación generada. Se pueden aplicar solo a las declaraciones de tipo, método, campo y valor de enumeración. Ejemplo:/** Replied status */ enum TeleportStatus { /** Object entirely teleported. */ OK = 0, /** Methods return this if teleportation is not completed. */ ERROR_TELEPORT = 1, /** * Teleportation could not be completed due to an object * obstructing the path. */ ERROR_OBJECT = 2, ... }
- Comienza los comentarios de varias líneas con
/**
en una línea separada. Usa*
al comienzo de cada línea. Termina el comentario con*/
en una línea independiente y alinea los asteriscos. Ejemplo:/** * My multi-line * comment */
- El aviso de licencia y los registros de cambios deben comenzar una línea nueva con
/*
(un asterisco), usar*
al principio de cada línea y colocar*/
en la última línea por sí solo (los asteriscos deben alinearse). Ejemplo:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
Comentarios de archivos
Comienza cada archivo con el aviso de licencia correspondiente. Para los HAL principales, esta debe ser la licencia de Apache de AOSP en development/docs/copyright-templates/c.txt
.
Recuerda actualizar el año y usar comentarios de varias líneas de estilo /* */
, como se explicó anteriormente.
De forma opcional, puedes colocar una línea vacía después del aviso de la licencia, seguida de información del registro de cambios o del control de versiones. Usa comentarios de varias líneas de estilo /* */
como se explicó anteriormente, coloca la línea vacía después del registro de cambios y, luego, sigue con la declaración del paquete.
Comentarios de TODO
Los comentarios "TODO" deben incluir la cadena TODO
en mayúscula, seguida de dos puntos. Ejemplo:
// TODO: remove this code before foo is checked in.
Los comentarios TODO solo se permiten durante el desarrollo; no deben existir en las interfaces publicadas.
Comentarios de interfaz y función (docstrings)
Usa /** */
para docstrings de una o varias líneas. No uses //
para las docstrings.
Las cadenas de documentos para las interfaces deben describir los mecanismos generales de la interfaz, la justificación del diseño, el propósito, etcétera. Las cadenas de documentos para las funciones deben ser específicas de la función (la documentación a nivel del paquete se incluye en un archivo README en el directorio del paquete).
/** * IFooController is the controller for foos. */ interface IFooController { /** * Opens the controller. * * @return status HAL_FOO_OK if successful. */ open() generates (FooStatus status); /** Close the controller. */ close(); };
Debes agregar @param
y @return
para cada parámetro o valor que se muestra:
- Se debe agregar
@param
para cada parámetro. Debe ir seguido del nombre del parámetro y, luego, de la docstring. - Se debe agregar
@return
para cada valor que se muestra. Debe ir seguido del nombre del valor que se muestra y, luego, de la docstring.
Ejemplo:
/** * Explain what foo does. * * @param arg1 explain what arg1 is * @param arg2 explain what arg2 is * @return ret1 explain what ret1 is * @return ret2 explain what ret2 is */ foo(T arg1, T arg2) generates (S ret1, S ret2);
Reglas de formato
Entre las reglas de formato generales, se incluyen las siguientes:
- Longitud de la línea: Cada línea de texto debe tener, como máximo, 100 columnas.
- Espacios en blanco: No debe haber espacios en blanco al final de las líneas. Las líneas vacías no deben contener espacios en blanco.
- Espacios en lugar de tabulaciones. Usa solo espacios.
- Indent size: Usa 4 espacios para los bloques y 8 espacios para las uniones de líneas.
- Fijación: Excepto por los valores de anotación, una llave abierta va en la misma línea que el código anterior, pero una llave cerrada y el siguiente punto y coma ocupan toda la línea. Ejemplo:
interface INfc { close(); };
Declaración del paquete
La declaración del paquete debe estar en la parte superior del archivo después del aviso de licencia, debe ocupar toda la línea y no debe tener sangría. Los paquetes se declaran con el siguiente formato (para el formato de nombres, consulta Nombres de paquetes):
package PACKAGE-NAME;
Ejemplo:
package android.hardware.nfc@1.0;
Declaraciones de funciones
El nombre de la función, los parámetros, generates
y los valores que se muestran deben estar en la misma línea si caben. Ejemplo:
interface IFoo { /** ... */ easyMethod(int32_t data) generates (int32_t result); };
Si no caben en la misma línea, intenta colocar los parámetros y los valores devueltos
en el mismo nivel de sangría y distingue generate
para ayudar al
lector a ver rápidamente los parámetros y los valores devueltos. Ejemplo:
interface IFoo { suchALongMethodThatCannotFitInOneLine(int32_t theFirstVeryLongParameter, int32_t anotherVeryLongParameter); anEvenLongerMethodThatCannotFitInOneLine(int32_t theFirstLongParameter, int32_t anotherVeryLongParameter) generates (int32_t theFirstReturnValue, int32_t anotherReturnValue); superSuperSuperSuperSuperSuperSuperLongMethodThatYouWillHateToType( int32_t theFirstVeryLongParameter, // 8 spaces int32_t anotherVeryLongParameter ) generates ( int32_t theFirstReturnValue, int32_t anotherReturnValue ); /* method name is even shorter than 'generates' */ foobar(AReallyReallyLongType aReallyReallyLongParameter, AReallyReallyLongType anotherReallyReallyLongParameter) generates (ASuperLongType aSuperLongReturnValue, // 4 spaces ASuperLongType anotherSuperLongReturnValue); }
Detalles adicionales:
- Un paréntesis abierto siempre está en la misma línea que el nombre de la función.
- No debe haber espacios entre el nombre de la función y el paréntesis abierto.
- No hay espacios entre los paréntesis y los parámetros excepto cuando hay saltos de línea entre ellos.
- Si
generates
está en la misma línea que el paréntesis de cierre anterior, usa un espacio anterior. Sigenerates
está en la misma línea que el siguiente paréntesis abierto, sigue con un espacio. - Alinea todos los parámetros y muestra los valores (si es posible).
- La sangría predeterminada es de 4 espacios.
- Los parámetros ajustados se alinean con los primeros parámetros de la línea anterior. De lo contrario, tienen una sangría de 8 espacios.
Anotaciones
Usa el siguiente formato para las anotaciones:
@annotate(keyword = value, keyword = {value, value, value})
Ordena las anotaciones en orden alfabético y usa espacios alrededor de los signos iguales. Ejemplo:
@callflow(key = value) @entry @exit
Asegúrate de que una anotación ocupe toda la línea. Ejemplos:
/* Good */ @entry @exit /* Bad */ @entry @exit
Si las anotaciones no caben en la misma línea, aplica una sangría de 8 espacios. Ejemplo:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
Si todo el array de valores no cabe en la misma línea, coloca saltos de línea después de los corchetes abiertos {
y después de cada coma dentro del array. Coloca el paréntesis de cierre inmediatamente después del último valor. No coloques los corchetes si solo hay un valor.
Si todo el array de valores puede caber en la misma línea, no uses espacios después de los corchetes abiertos ni antes de los corchetes de cierre, y usa un espacio después de cada coma. Ejemplos:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
NO debe haber líneas vacías entre las anotaciones y la declaración de la función. Ejemplos:
/* Good */ @entry foo(); /* Bad */ @entry foo();
Declaraciones de enum
Usa las siguientes reglas para las declaraciones de enum:
- Si las declaraciones de enum se comparten con otro paquete, coloca las declaraciones en
types.hal
en lugar de incorporarlas en una interfaz. - Usa un espacio antes y después de los dos puntos, y un espacio después del tipo subyacente antes de la llave de apertura.
- Es posible que el último valor de enumeración no tenga una coma adicional.
Declaraciones de struct
Usa las siguientes reglas para las declaraciones de struct:
- Si las declaraciones de struct se comparten con otro paquete, colócalas en
types.hal
en lugar de incorporarlas en una interfaz. - Usa un espacio después del nombre del tipo de struct antes de la llave de apertura.
- Alinea los nombres de los campos (opcional). Ejemplo:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Declaraciones de arrays
No coloques espacios entre los siguientes elementos:
- Tipo de elemento y corchete de apertura.
- Corchete abierto y tamaño del array
- Tamaño del array y corchete cuadrado de cierre.
- Corchete de cierre y el siguiente corchete de apertura, si existe más de una dimensión
Ejemplos:
/* Good */ int32_t[5] array; /* Good */ int32_t[5][6] multiDimArray; /* Bad */ int32_t [ 5 ] [ 6 ] array;
Vectores
No coloques espacios entre los siguientes elementos:
vec
y un corchete angular de apertura.- Corchete angular de apertura y tipo de elemento (Excepción: el tipo de elemento también es un
vec
). - Tipo de elemento y corchete angular de cierre (Excepción: el tipo de elemento también es un
vec
)
Ejemplos:
/* Good */ vec<int32_t> array; /* Good */ vec<vec<int32_t>> array; /* Good */ vec< vec<int32_t> > array; /* Bad */ vec < int32_t > array; /* Bad */ vec < vec < int32_t > > array;