Guida di stile per il codice

Lo stile del codice HIDL assomiglia al codice C++ nel framework Android, con rientri di 4 spazi e nomi di file in formato misto. Le dichiarazioni di pacchetti, le importazioni e le docstring sono simili a quelle in Java, con lievi modifiche.

Gli esempi seguenti per IFoo.hal e types.hal illustrano gli stili di codice HIDL e forniscono link rapidi ai dettagli di ogni stile (IFooClientCallback.hal, IBar.hal e IBaz.hal sono stati omessi).

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;
};

Convenzioni di denominazione

I nomi delle funzioni, delle variabili e dei file devono essere descrittivi; evita abbreviazioni eccessive. Considera gli acronimi come parole (ad esempio, utilizza INfc invece di INFC).

Struttura delle directory e denominazione dei file

La struttura delle directory dovrebbe apparire nel seguente modo:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (facoltativo, può essere più di un livello)
        • VERSION
          • Android.mk
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal (facoltativo)

Dove:

  • ROOT-DIRECTORY è:
    • hardware/interfaces per i pacchetti HIDL principali.
    • vendor/VENDOR/interfaces per i pacchetti del fornitore, dove VENDOR si riferisce a un fornitore di SoC o a un OEM/ODM.
  • MODULE deve essere una parola minuscola che descrive il sottosistema (ad esempio, nfc). Se è necessaria più di una parola, utilizza SUBMODULE nidificati. Può esserci più di un livello di nidificazione.
  • VERSION deve essere esattamente la stessa versione (principale.secondaria) descritta in Versioni.
  • IINTERFACE_X deve essere il nome dell'interfaccia con UpperCamelCase/PascalCase (ad esempio, INfc) come descritto in Nomi delle interfacce.

Esempio:

  • hardware/interfaces
    • nfc
      • 1.0
        • Android.mk
        • INfc.hal
        • INfcClientCallback.hal
        • types.hal

Nota: tutti i file devono disporre di autorizzazioni non eseguibili (in Git).

Nomi pacchetto

I nomi dei pacchetti devono utilizzare il seguente formato di nome completo (FQN) (denominato PACKAGE-NAME):

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

Dove:

  • PACKAGE è il pacchetto mappato a ROOT-DIRECTORY. In particolare, PACKAGE è:
    • android.hardware per i pacchetti HIDL principali (mapping a hardware/interfaces).
    • vendor.VENDOR.hardware per i pacchetti del fornitore, dove VENDOR si riferisce a un fornitore di SoC o a un OEM/ODM (mapping a vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION sono esattamente gli stessi nomi di cartelle nella struttura descritta in Struttura delle directory.
  • I nomi dei pacchetti devono essere in minuscolo. Se sono composte da più di una parola, le parole devono essere utilizzate come moduli secondari o scritte in snake_case.
  • Non sono ammessi spazi.

Il nome di dominio completo viene sempre utilizzato nelle dichiarazioni dei pacchetti.

Versioni

Le versioni devono avere il seguente formato:

MAJOR.MINOR

Sia MAJOR sia la versione MINOR devono essere un singolo numero intero. HIDL utilizza le regole di controllo delle versioni semantico.

Importazioni

Un'importazione può avere uno dei seguenti tre formati:

  • Importazioni dell'intero pacchetto: import PACKAGE-NAME;
  • Importazioni parziali: import PACKAGE-NAME::UDT; (o, se il tipo importato si trova nello stesso pacchetto,import UDT;
  • Importazioni solo tipi: import PACKAGE-NAME::types;

PACKAGE-NAME segue il formato indicato in Nomi dei pacchetti. types.hal (se esiste) del pacchetto attuale viene importato automaticamente (non importarlo in modo esplicito).

Nomi completi (FQN)

Utilizza i nomi completi per l'importazione di un tipo definito dall'utente solo quando necessario. Ometti PACKAGE-NAME se il tipo di importazione si trova nello stesso pacchetto. Un FQN non deve contenere spazi. Esempio di nome completo:

android.hardware.nfc@1.0::INfcClientCallback

In un altro file in android.hardware.nfc@1.0, fai riferimento all'interfaccia sopra indicata come INfcClientCallback. In caso contrario, utilizza solo il nome completo.

Raggruppamento e ordinamento delle importazioni

Utilizza una riga vuota dopo la dichiarazione del pacchetto (prima delle importazioni). Ogni importazione deve occupare una sola riga e non deve essere rientrata. Importazioni di gruppi nel seguente ordine:

  1. Altri pacchetti android.hardware (utilizza i nomi completi).
  2. Altri pacchetti vendor.VENDOR (utilizza i nomi completi).
    • Ogni fornitore deve essere un gruppo.
    • Ordina i fornitori in ordine alfabetico.
  3. Importazioni da altre interfacce nello stesso pacchetto (utilizza nomi semplici).

Utilizza una riga vuota tra i gruppi. All'interno di ogni gruppo, ordina le importazioni in ordine alfabetico. Esempio:

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;

Nomi delle interfacce

I nomi delle interfacce devono iniziare con I, seguito da un nome UpperCamelCase/PascalCase. Nel file IFoo.hal deve essere definita un'interfaccia con il nome IFoo. Questo file può contenere definizioni solo per l'interfaccia IFoo (l'interfaccia INAME deve essere in INAME.hal).

Funzioni

Per i nomi di funzioni, argomenti e variabili restituite, utilizza lowerCamelCase. Esempio:

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

Nomi dei campi di struct e union

Per i nomi dei campi di struct o unione, utilizza lowerCamelCase. Esempio:

struct FooReply {
    vec<uint8_t> replyData;
}

Nomi dei tipi

I nomi dei tipi si riferiscono a definizioni di struct o unione, definizioni di tipi enum e typedef. Per questi nomi, utilizza UpperCamelCase/PascalCase. Esempi:

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

Valori enum

I valori enum devono essere UPPER_CASE_WITH_UNDERSCORES. Quando passi valori enum come argomenti di funzione e li restituisci come valori restituiti dalla funzione, utilizza il tipo enum effettivo (non il tipo intero sottostante). Esempio:

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:il tipo sottostante di un tipo enum viene dichiarato in modo esplicito dopo i due punti. Poiché non dipende dal compilatore, l'utilizzo del tipo di enumerazione effettivo è più chiaro.

Per i nomi completi dei valori enum, viene utilizzato un due punti tra il nome del tipo enum e il nome del valore enum:

PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME

Non devono essere presenti spazi all'interno di un nome completo. Utilizza un nome completo solo quando necessario e ometti le parti non necessarie. Esempio:

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

Commenti

Per un commento di una sola riga, //, /* */ e /** */ vanno bene.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Utilizza /* */ per i commenti. Sebbene HIDL supporti // per i commenti, questi sono sconsigliati perché non vengono visualizzati nell'output generato.
  • Utilizza /** */ per la documentazione generata. Questi possono essere applicati solo a dichiarazioni di tipo, metodo, campo e valore enum. Esempio:
    /** 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,
        ...
    }
  • Inizia i commenti su più righe con /** su una riga separata. Utilizza * all'inizio di ogni riga. Termina il commento con */ su una riga separata, allineando gli asterischi. Esempio:
    /**
     * My multi-line
     * comment
     */
  • L'avviso relativo alla licenza e i log delle modifiche devono iniziare una nuova riga con /* (un singolo asterisco), utilizzare * all'inizio di ogni riga e inserire */ sull'ultima riga da solo (gli asterischi devono essere allineati). Esempio:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */

Commenti dei file

Inizia ogni file con la notifica di licenza appropriata. Per gli HAL principali, questa deve essere la licenza Apache AOSP in development/docs/copyright-templates/c.txt. Ricorda di aggiornare l'anno e di utilizzare i commenti multilinea in stile /* */ come spiegato in precedenza.

Se vuoi, puoi inserire una riga vuota dopo l'avviso relativo alla licenza, seguita da un log delle modifiche/informazioni sul controllo delle versioni. Utilizza commenti multiriga in stile /* */ come spiegato sopra, inserisci la riga vuota dopo il log delle modifiche, quindi segui con la dichiarazione del pacchetto.

Commenti TODO

I TODO devono includere la stringa TODO in maiuscolo seguita da due punti. Esempio:

// TODO: remove this code before foo is checked in.

I commenti TODO sono consentiti solo durante lo sviluppo e non devono essere presenti nelle interfacce pubblicate.

Commenti di interfaccia e funzione (docstring)

Utilizza /** */ per le docstring multilinea e a una sola riga. Non utilizzare // per le docstring.

Le docstring per le interfacce devono descrivere i meccanismi generali dell'interfaccia, la logica di progettazione, lo scopo e così via. Le docstring per le funzioni devono essere specifiche per la funzione (la documentazione a livello di pacchetto va in un file README nella directory del pacchetto).

/**
 * 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();
};

Devi aggiungere @params e @returns per ogni parametro/valore restituito:

  • @param deve essere aggiunto per ogni parametro. Deve essere seguito dal nome del parametro e poi dalla docstring.
  • @return deve essere aggiunto per ogni valore restituito. Deve essere seguito dal nome del valore restituito e poi dalla docstring.

Esempio:

/**
 * 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);

Regole di formattazione

Le regole di formattazione generali includono:

  • Lunghezza della riga. Ogni riga di testo deve contenere al massimo 100 colonne.
  • Spazi vuoti. Nessuno spazio vuoto alla fine delle righe; le righe vuote non devono contenere spazi vuoti.
  • Spazi e tabulazioni. Utilizza solo spazi.
  • Dimensioni rientro. Utilizza 4 spazi per i blocchi e 8 spazi per i ritorni a capo
  • Supporto. Ad eccezione dei valori dell'annotazione, una parentesi aperta si trova sulla stessa riga del codice precedente, mentre una parentesi chiusa e il punto e virgola successivo occupano l'intera riga. Esempio:
    interface INfc {
        close();
    };

Dichiarazione del pacchetto

La dichiarazione del pacchetto deve trovarsi nella parte superiore del file dopo l'avviso della licenza, deve occupare l'intera riga e non deve essere rientrata. I pacchetti vengono dichiarati utilizzando il seguente formato (per la formattazione dei nomi, vedi Nomi dei pacchetti):

package PACKAGE-NAME;

Esempio:

package android.hardware.nfc@1.0;

Dichiarazioni di funzione

Il nome della funzione, i parametri, generates e i valori restituiti devono trovarsi sulla stessa riga se ci stanno. Esempio:

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

Se non rientrano nella stessa riga, prova a inserire i parametri e i valori restituiti nello stesso livello di rientro e a distinguere generate per aiutare il lettore a visualizzare rapidamente i parametri e i valori restituiti. Esempio:

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);
}

Ulteriori dettagli:

  • Una parentesi aperta si trova sempre sulla stessa riga del nome della funzione.
  • Nessuno spazio tra il nome della funzione e la parentesi aperta.
  • Nessuno spazio tra le parentesi e i parametri tranne quando sono presenti avanzamenti riga tra loro.
  • Se generates si trova sulla stessa riga della parentesi chiusa precedente, utilizza uno spazio precedente. Se generates si trova sulla stessa riga della parentesi aperta successiva, aggiungi uno spazio.
  • Allinea tutti i parametri e i valori restituiti (se possibile).
  • Il rientro predefinito è di 4 spazi.
  • I parametri a capo sono allineati ai primi parametri della riga precedente, altrimenti hanno un rientro di 8 spazi.

Annotazioni

Utilizza il seguente formato per le annotazioni:

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

Ordina le annotazioni in ordine alfabetico e utilizza gli spazi intorno ai segni di uguale. Esempio:

@callflow(key = value)
@entry
@exit

Assicurati che un'annotazione occupi l'intera riga. Esempi:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

Se le annotazioni non possono essere inserite sulla stessa riga, rientra di 8 spazi. Esempio:

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

Se l'intero array di valori non può essere contenuto nella stessa riga, inserisci interruzioni di riga dopo le parentesi graffe aperte { e dopo ogni virgola all'interno dell'array. Inserisci la parentesi chiusa immediatamente dopo l'ultimo valore. Non inserire le parentesi graffe se è presente un solo valore.

Se l'intero array di valori può essere contenuto nella stessa riga, non utilizzare spazi dopo le parentesi graffe aperte e prima di quelle chiuse e utilizza uno spazio dopo ogni virgola. Esempi:

/* Good */
@callflow(key = {"val", "val"})

/* Bad */
@callflow(key = { "val","val" })

NON devono esserci righe vuote tra le annotazioni e la dichiarazione della funzione. Esempi:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Dichiarazioni di enumerazione

Utilizza le seguenti regole per le dichiarazioni enum:

  • Se le dichiarazioni enum vengono condivise con un altro pacchetto, inseriscile in types.hal anziché incorporarle in un'interfaccia.
  • Usa uno spazio prima e dopo i due punti e uno spazio dopo il tipo sottostante prima della parentesi graffa aperta.
  • L'ultimo valore enum potrebbe non avere una virgola aggiuntiva.

Dichiarazioni di struct

Utilizza le seguenti regole per le dichiarazioni di struct:

  • Se le dichiarazioni di struct vengono condivise con un altro pacchetto, inseriscile in types.hal anziché incorporarle in un'interfaccia.
  • Utilizza uno spazio dopo il nome del tipo di struct prima della parentesi graffa aperta.
  • Allinea i nomi dei campi (facoltativo). Esempio:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }

Dichiarazioni di array

Non inserire spazi tra i seguenti elementi:

  • Tipo di elemento e parentesi quadra aperta.
  • Parentesi quadra aperta e dimensione dell'array.
  • Dimensione dell'array e parentesi quadra chiusa.
  • Chiudi la parentesi quadra e la successiva parentesi quadra aperta, se esiste più di una dimensione.

Esempi:

/* Good */
int32_t[5] array;

/* Good */
int32_t[5][6] multiDimArray;

/* Bad */
int32_t [ 5 ] [ 6 ] array;

Vettori

Non inserire spazi tra i seguenti elementi:

  • vec e parentesi angolare aperta.
  • Parentesi angolare aperta e tipo di elemento (eccezione: il tipo di elemento è anche un vec).
  • Tipo di elemento e parentesi angolare chiusa (Eccezione: il tipo di elemento è anche un vec).

Esempi:

/* 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;