El estilo de código de HIDL se asemeja al código de C++ en el framework de Android, con sangrías de 4 espacios y nombres de archivo en combinación de mayúsculas y minúsculas. Las declaraciones de paquetes, las importaciones y las cadenas de documentación son similares a las 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 detalles sobre 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 funciones, variables y archivos deben ser descriptivos. Evita las abreviaturas excesivas. Trata los acrónimos como palabras (por ejemplo, usa INfc
en lugar de INFC
).
Estructura de directorios y nomenclatura de archivos
La estructura de directorios debería verse de la siguiente manera:
ROOT-DIRECTORY
MODULE
SUBMODULE
(opcional, puede haber 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 los paquetes HIDL principales.vendor/VENDOR/interfaces
para paquetes de proveedores, dondeVENDOR
hace referencia a un proveedor de SoC o a 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
anidados. Puede haber más de un nivel de anidación.VERSION
debe ser exactamente la misma versión (principal.secundaria) 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 paquetes deben usar el siguiente formato de nombre completamente calificado (FQN) (al que se hace referencia como PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
En la que:
PACKAGE
es el paquete que se asigna aROOT-DIRECTORY
. En particular,PACKAGE
es lo siguiente:android.hardware
para los paquetes HIDL principales (se asignan ahardware/interfaces
).vendor.VENDOR.hardware
para paquetes de proveedores, dondeVENDOR
hace referencia a un proveedor de SoC o a un OEM/ODM (asignación avendor/VENDOR/interfaces
).
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
son los mismos nombres de carpeta exactos en la estructura que se describe en Estructura de directorios.- Los nombres de los paquetes deben estar en minúsculas. Si tienen más de una palabra, se deben usar como submódulos o escribirse en
snake_case
. - No se permiten espacios.
El FQN siempre se usa en las declaraciones de paquetes.
Versiones
Las versiones deben tener el siguiente formato:
MAJOR.MINOR
Tanto la versión de MAJOR como la de MINOR deben ser un solo número entero. HIDL usa reglas de versionado semántico.
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 solo de tipos:
import PACKAGE-NAME::types;
El 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 la importación de un tipo definido por el usuario solo cuando sea necesario.
Omite PACKAGE-NAME
si el tipo de importación está en el mismo paquete. Un FQN no debe contener espacios. Ejemplo de un nombre completamente calificado:
android.hardware.nfc@1.0::INfcClientCallback
En otro archivo en 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. Importa los grupos 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.
- Ordenar 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 interfaces
Los nombres de las interfaces deben comenzar con un I
, seguido 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 funciones, argumentos y variables de retorno, 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 hacen referencia a definiciones de struct o unión, definiciones de tipo enum y typedef
s. Para estos nombres, usa UpperCamelCase
/PascalCase
. Por ejemplo:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Enum. de valores
Los valores de enumeración deben ser UPPER_CASE_WITH_UNDERSCORES
. Cuando pases valores de enumeración como argumentos de funciones y los muestres como valores de retorno de funciones, usa el tipo de enumeración 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 enumeración 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 enumeración, se usa un dos puntos entre el nombre del tipo de enumeración y el nombre del valor de enumeración:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
No debe haber espacios dentro de un nombre completo. 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 válidos.
// 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 los comentarios, no se recomienda su uso porque no aparecen en el resultado generado. - Usa
/** */
para la documentación generada. Solo se pueden aplicar a 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 separada, alineando los asteriscos. Ejemplo:/** * My multi-line * comment */
- El aviso de licencia y los registros de cambios deben comenzar una línea nueva con
/*
(un solo 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. En el caso de los HALs principales, esta debería ser la licencia de Apache del AOSP en development/docs/copyright-templates/c.txt
.
Recuerda actualizar el año y usar comentarios de varias líneas con el estilo /* */
, como se explicó anteriormente.
De forma opcional, puedes colocar una línea vacía después del aviso de licencia, seguida de un registro de cambios o información de versiones. Usa comentarios de varias líneas con el estilo /* */
, como se explicó anteriormente, coloca la línea vacía después del registro de cambios y, luego, continúa con la declaración del paquete.
Comentarios TODO
Los comentarios TODO deben incluir la cadena TODO
en mayúsculas, seguida de dos puntos. Ejemplo:
// TODO: remove this code before foo is checked in.
Los comentarios TODO solo se permiten durante el desarrollo y no deben existir en las interfaces publicadas.
Comentarios de la interfaz y de las funciones (docstrings)
Usa /** */
para cadenas de documentación de una o varias líneas. No uses //
para las cadenas de documentación.
Las cadenas de documentación de las interfaces deben describir los mecanismos generales de la interfaz, la justificación del diseño, el propósito, etcétera. Las cadenas de documentación de las funciones deben ser específicas de la función (la documentación a nivel del paquete va 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 de devolución:
- Se debe agregar
@param
para cada parámetro. Debe ir seguido del nombre del parámetro y, luego, la cadena de documentación. - Se debe agregar
@return
para cada valor de devolución. Debe ir seguido del nombre del valor de retorno y, luego, de la cadena de documentación.
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
Las reglas generales de formato incluyen lo siguiente:
- Longitud de la línea Cada línea de texto debe tener, como máximo, 100 columnas de longitud.
- Espacios en blanco No debe haber espacios en blanco al final de las líneas, y las líneas vacías no deben contener espacios en blanco.
- Espacios vs. pestañas. Usa solo espacios.
- Tamaño de sangría Usa 4 espacios para los bloques y 8 espacios para las uniones de líneas.
- Soporte. A excepción de los valores de anotación, una llave de apertura va en la misma línea que el código anterior, pero una llave de cierre y el punto y coma siguiente 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 de retorno 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 de devolución en el mismo nivel de sangría y distingue generate
para ayudar al lector a ver rápidamente los parámetros y los valores de devolución. 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 de apertura.
- 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 precedente. Sigenerates
está en la misma línea que el siguiente paréntesis abierto, agrega un espacio. - Alinea todos los parámetros y los valores de devolución (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 alfabéticamente y usa espacios alrededor de los signos igual. 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, agrega 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, inserta saltos de línea después de las llaves de apertura {
y después de cada coma dentro del array. Coloca el paréntesis de cierre inmediatamente después del último valor. No incluyas los corchetes si solo hay un valor.
Si todo el array de valores cabe en la misma línea, no uses espacios después de las llaves de apertura ni antes de las llaves 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 enumeraciones
Usa las siguientes reglas para las declaraciones de enumeraciones:
- Si las declaraciones de enumeración se comparten con otro paquete, colócalas en
types.hal
en lugar de incorporarlas dentro de 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 dentro de una interfaz. - Usa un espacio después del nombre del tipo de struct y antes de la llave de apertura.
- Alinear los nombres de los campos (opcional) Ejemplo:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Declaraciones de matrices
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 de cierre.
- Cierra el corchete y el siguiente corchete abierto, 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 corchete angular de apertura.- Corchete angular de apertura y tipo de elemento (Exception: element type is also a
vec
). - Tipo de elemento y corchete angular de cierre (Exception: element type is also a
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;