Guía de estilo de código

El estilo del código HIDL se parece al código C++ en el marco de Android, con sangrías de 4 espacios y nombres de archivos en mayúsculas y minúsculas. Las declaraciones, importaciones y cadenas de documentos de paquetes son similares a las de Java, con ligeras modificaciones.

Los siguientes ejemplos de IFoo.hal types.hal ilustran los estilos de código HIDL y proporcionan enlaces rápidos a detalles sobre cada estilo (se han omitido IFooClientCallback.hal , IBar.hal e 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; Evite las abreviaturas excesivas. Trate las siglas como palabras (por ejemplo, utilice INfc en lugar de INFC ).

Estructura de directorios y denominación de archivos.

La estructura del directorio debería verse de la siguiente manera:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (opcional, puede ser más de un nivel)
        • VERSION
          • Android.mk
          • I INTERFACE_1 .hal
          • I INTERFACE_2 .hal
          • I INTERFACE_N .hal
          • types.hal (opcional)

Dónde:

  • ROOT-DIRECTORY es:
    • hardware/interfaces para paquetes HIDL principales.
    • vendor/ VENDOR /interfaces para paquetes de proveedor, donde VENDOR se refiere a un proveedor de SoC o un OEM/ODM.
  • MODULE debe ser una palabra en minúscula que describa el subsistema (por ejemplo, nfc ). Si se necesita más de una palabra, utilice SUBMODULE anidado. Puede haber más de un nivel de anidamiento.
  • VERSION debe ser exactamente la misma versión (mayor.menor) como se describe en Versiones .
  • I INTERFACE_X debe ser el nombre de la interfaz con UpperCamelCase / 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 paquetes

Los nombres de los paquetes deben utilizar el siguiente formato de nombre completo (FQN) (denominado PACKAGE-NAME ):

PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION

Dónde:

  • PACKAGE es el paquete que se asigna al ROOT-DIRECTORY . En particular, PACKAGE es:
    • android.hardware para paquetes HIDL principales (asignación a hardware/interfaces ).
    • vendor. VENDOR .hardware para paquetes de proveedores, donde VENDOR se refiere a un proveedor de SoC o un OEM/ODM (asignación a vendor/ VENDOR /interfaces ).
  • MODULE [. SUBMODULE [. SUBMODULE […]]]@ VERSION son exactamente los mismos nombres de carpeta en la estructura descrita en Estructura de directorios .
  • 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 permiten espacios.

El FQN siempre se utiliza 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 único número entero. HIDL utiliza reglas de control de versiones semánticas .

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;

El PACKAGE-NAME sigue el formato en Nombres de paquetes . El types.hal del paquete actual (si existe) se importa automáticamente (no lo importe explícitamente).

Nombres completos (FQN)

Utilice nombres completos para una importación de tipo definido por el usuario sólo cuando sea necesario. Omita PACKAGE-NAME si el tipo de importación está en el mismo paquete. Un FQN no debe contener espacios. Ejemplo de un nombre completo:

android.hardware.nfc@1.0::INfcClientCallback

En otro archivo en android.hardware.nfc@1.0 , consulte la interfaz anterior como INfcClientCallback . De lo contrario, utilice sólo el nombre completo.

Agrupar y ordenar importaciones

Utilice 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. Importaciones de grupo en el siguiente orden:

  1. Otros paquetes android.hardware (use nombres completos).
  2. Otro vendor. VENDOR Paquetes vendor. VENDOR (use nombres completos).
    • Cada proveedor debe ser un grupo.
    • Ordene a los proveedores alfabéticamente.
  3. Importaciones desde otras interfaces en el mismo paquete (use nombres simples).

Utilice una línea vacía entre grupos. Dentro de cada grupo, ordene 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 interfaz

Los nombres de las interfaces deben comenzar con I , seguido de un nombre UpperCamelCase / PascalCase . Se debe definir una interfaz con el nombre IFoo en el archivo IFoo.hal . Este archivo puede contener definiciones solo para la interfaz IFoo (la interfaz I NAME debe estar en I NAME .hal ).

Funciones

Para nombres de funciones, argumentos y nombres de variables de retorno, utilice lowerCamelCase . Ejemplo:

open(INfcClientCallback clientCallback) generates (int32_t retVal);
oneway pingAlive(IFooCallback cb);

Nombres de campos de estructura/unión

Para nombres de campos de estructura/unión, utilice lowerCamelCase . Ejemplo:

struct FooReply {
    vec<uint8_t> replyData;
}

Escriba nombres

Los nombres de tipos se refieren a definiciones de estructuras/uniones, definiciones de tipos de enumeraciones y typedef s. Para estos nombres, utilice UpperCamelCase / PascalCase . Ejemplos:

enum NfcStatus : int32_t {
    /*...*/
};
struct NfcData {
    /*...*/
};

Valores de enumeración

Los valores de enumeración deben ser UPPER_CASE_WITH_UNDERSCORES . Al pasar valores de enumeración como argumentos de función y devolverlos como retornos de función, utilice el tipo de enumeración real (no el tipo 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 explícitamente después de los dos puntos. Como no depende del compilador, usar el tipo de enumeración real es más claro.

Para nombres completos para valores de enumeración, se utilizan 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. Utilice un nombre completo sólo cuando sea necesario y omita las partes innecesarias. Ejemplo:

android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK

Comentarios

Para un comentario de una sola línea, // , /* */ y /** */ están bien.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Utilice /* */ para comentarios. Si bien HIDL admite // para comentarios, no se recomiendan porque no aparecen en la salida generada.
  • Utilice /** */ para la documentación generada. Estos 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,
        ...
    }
    
  • Inicie comentarios de varias líneas con /** en una línea separada. Utilice * al comienzo de cada línea. Termine 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 nueva línea 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 archivo

Inicie cada archivo con el aviso de licencia correspondiente. Para HAL principales, esta debería ser la licencia AOSP Apache en development/docs/copyright-templates/c.txt . Recuerde actualizar el año y utilizar el estilo /* */ comentarios de varias líneas como se explicó anteriormente.

Opcionalmente, puede colocar una línea vacía después del aviso de licencia, seguida de un registro de cambios/información de versiones. Utilice comentarios de varias líneas de estilo /* */ como se explicó anteriormente, coloque la línea vacía después del registro de cambios y luego siga con la declaración del paquete.

TODO comentarios

Los 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 se permiten sólo durante el desarrollo; no deben existir en interfaces publicadas.

Comentarios de interfaz/función (cadenas de documentos)

Utilice /** */ para cadenas de documentos de una sola y varias líneas. No utilice // para cadenas de documentos.

Las cadenas de documentos para interfaces deben describir los mecanismos generales de la interfaz, la lógica del diseño, el propósito, etc. Las cadenas de documentos para funciones deben ser específicas de la función (la documentación a nivel de 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();
};

Debe agregar @param sy @return s para cada parámetro/valor de retorno:

  • Se debe agregar @param para cada parámetro. Debe ir seguido del nombre del parámetro y luego de la cadena de documentación.
  • Se debe agregar @return para cada valor de retorno. 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);

Formato

Las reglas generales de formato incluyen:

  • Longitud de la línea . Cada línea de texto debe tener como máximo 100 columnas.
  • Espacios en blanco . Sin espacios en blanco al final de las líneas; Las líneas vacías no deben contener espacios en blanco.
  • Espacios versus pestañas . Utilice sólo espacios.
  • Tamaño de sangría . Utilice 4 espacios para bloques y 8 espacios para cambios de línea
  • Refuerzo . Excepto por 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 siguiente punto y coma ocupan toda la línea. Ejemplo:
    interface INfc {
        close();
    };
    

Declaración de 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 utilizando el siguiente formato (para conocer el formato de los nombres, consulte 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 encajan. Ejemplo:

interface IFoo {
    /** ... */
    easyMethod(int32_t data) generates (int32_t result);
};

Si no caben en la misma línea, intente colocar los parámetros y los valores de retorno en el mismo nivel de sangría y distinga generate para ayudar al lector a ver rápidamente los parámetros y los valores de retorno. 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 hay espacios entre el nombre de la función y el paréntesis abierto.
  • No hay espacios entre paréntesis y 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, utilice un espacio anterior. Si generates está en la misma línea que el siguiente paréntesis abierto, siga con un espacio.
  • Alinee todos los parámetros y valores de retorno (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

Utilice el siguiente formato para las anotaciones:

@annotate(keyword = value, keyword = {value, value, value})

Ordene las anotaciones en orden alfabético y utilice espacios alrededor de los signos iguales. Ejemplo:

@callflow(key = value)
@entry
@exit

Asegúrese 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, sangría con 8 espacios. Ejemplo:

@annotate(
        keyword = value,
        keyword = {
                value,
                value
        },
        keyword = value)

Si toda la matriz de valores no cabe en la misma línea, coloque saltos de línea después de las llaves abiertas { y después de cada coma dentro de la matriz. Coloque el paréntesis de cierre inmediatamente después del último valor. No pongas las llaves si solo hay un valor.

Si toda la matriz de valores puede caber en la misma línea, no use espacios después de las llaves abiertas y antes de las llaves cerradas y use 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 función. Ejemplos:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Declaraciones de enumeración

Utilice las siguientes reglas para las declaraciones de enumeración:

  • Si las declaraciones de enumeración se comparten con otro paquete, coloque las declaraciones en types.hal en lugar de incrustarlas dentro de una interfaz.
  • Utilice un espacio antes y después de los dos puntos, y un espacio después del tipo subyacente antes de la llave abierta.
  • El último valor de enumeración puede tener o no una coma adicional.

Declaraciones de estructura

Utilice las siguientes reglas para declaraciones de estructuras:

  • Si las declaraciones de estructura se comparten con otro paquete, coloque las declaraciones en types.hal en lugar de incrustarlas dentro de una interfaz.
  • Utilice un espacio después del nombre del tipo de estructura antes de la llave abierta.
  • Alinear los nombres de los campos (opcional). Ejemplo:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }
    

Declaraciones de matriz

No coloque espacios entre lo siguiente:

  • Tipo de elemento y corchete abierto.
  • Abra el corchete y el tamaño de la matriz.
  • Tamaño de la matriz y cerrar corchete.
  • Cierre 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 coloque espacios entre lo siguiente:

  • vec y soporte de ángulo abierto.
  • Abra el corchete angular y el tipo de elemento ( Excepción: el tipo de elemento también es vec ).
  • Tipo de elemento y corchete angular cerrado ( Excepción: el tipo de elemento también es 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;