Guía de estilo de código

El estilo de código HIDL se asemeja al código C++ en el marco de trabajo de Android, con sangrías de 4 espacios y nombres de archivos en 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.

Los siguientes ejemplos de IFoo.hal y types.hal ilustran los estilos de código HIDL y proporcionan vínculos rápidos a los detalles de 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; evitar el exceso de abreviaturas. Trate los acrónimos como palabras (p. ej., use INfc en lugar de INFC ).

Estructura de directorios y nombres de archivos

La estructura del directorio debe aparecer 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 básicos de HIDL.
    • vendor/ VENDOR /interfaces para paquetes de proveedores, donde VENDOR se refiere 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, use SUBMODULE anidado. Puede haber más de un nivel de anidamiento.
  • VERSION debe ser exactamente la misma versión (principal.menor) que 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 usar el siguiente formato de nombre completo (FQN) (denominado PACKAGE-NAME ):

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

Dónde:

  • PACKAGE es el paquete que se asigna a ROOT-DIRECTORY . En particular, PACKAGE es:
    • android.hardware para paquetes HIDL básicos (asignación a hardware/interfaces ).
    • vendor. VENDOR .hardware para paquetes de proveedores, donde VENDOR hace referencia 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 del 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 formato de snake_case .
  • No se permiten espacios.

El FQN siempre se usa en 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 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 Package names . 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 definida por el usuario solo 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 bajo android.hardware.nfc@1.0 , consulte la interfaz anterior como INfcClientCallback . De lo contrario, use solo el nombre completo.

Agrupar y ordenar importaciones

Use 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:

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

Utilice una línea vacía entre los 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 una I , seguida 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 de IFoo (la interfaz I NAME debe estar en I NAME .hal ).

Funciones

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

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

Nombres de campo de estructura/unión

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

struct FooReply {
    vec<uint8_t> replyData;
}

Escriba nombres

Los nombres de tipo se refieren a definiciones de estructura/unión, definiciones de tipo de enumeración y typedef s. Para estos nombres, use 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 devoluciones de función, use 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 totalmente calificados para valores de enumeración, se usan 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 solo 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 */
  • Use /* */ para comentarios. Si bien HIDL admite // para comentarios, se desaconsejan 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,
        ...
    }
    
  • Comience los comentarios de varias líneas con /** en una línea separada. Use * 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 nueva línea con /* (un solo asterisco), usar * al comienzo de cada línea y colocar */ en la última línea solo (los asteriscos deben alinearse). Ejemplo:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */
    

Comentarios del archivo

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

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

COMENTARIOS POR HACER

TODO debe 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; no deben existir en las interfaces publicadas.

Comentarios de interfaz/función (docstrings)

Use /** */ para cadenas de documentos de una o varias líneas. No use // para cadenas de documentos.

Las cadenas de documentación para las interfaces deben describir los mecanismos generales de la interfaz, la justificación del diseño, el propósito, etc. Las cadenas de documentación para las funciones deben ser específicas de la función (la documentación a nivel del paquete va en un archivo LÉAME 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 s y @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 devuelto. Debe ir seguido del nombre del valor devuelto 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);

Formateo

Las reglas generales de formato incluyen:

  • Longitud de línea . Cada línea de texto debe tener como máximo 100 columnas.
  • Espacios en blanco . Sin espacios en blanco finales en las líneas; las líneas vacías no deben contener espacios en blanco.
  • Espacios vs pestañas . Use solo espacios.
  • Tamaño de sangría . Use 4 espacios para bloques y 8 espacios para envolturas de línea
  • arriostramiento A excepción de 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 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 el formato de nombre, 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 devueltos 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 devueltos en el mismo nivel de sangría y distinga 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 hay espacios entre el nombre de la función y el paréntesis de apertura.
  • 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, use 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 use 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, sangrar 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 cerrar las llaves 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 la 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 del colon 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 las declaraciones de estructuras:

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

Declaraciones de matriz

No ponga espacios entre lo siguiente:

  • Tipo de elemento y corchete abierto.
  • Abra el corchete cuadrado y el tamaño de la matriz.
  • Tamaño de matriz y cierre de corchetes.
  • 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 ponga 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 un vec ).
  • Tipo de elemento y paréntesis angular cerrado ( 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;