Guida allo stile del codice

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

I seguenti esempi per IFoo.hal types.hal illustrano gli stili di codice HIDL e forniscono collegamenti rapidi ai dettagli su ciascuno 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, dei nomi delle variabili e dei file dovrebbero essere descrittivi; evitare eccessive abbreviazioni. Tratta gli acronimi come parole (ad esempio, usa INfc invece di INFC ).

Struttura delle directory e denominazione dei file

La struttura della directory dovrebbe apparire come segue:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (opzionale, potrebbe essere più di un livello)
        • VERSION
          • Android.mk
          • I INTERFACE_1 .hal
          • I INTERFACE_2 .hal
          • I INTERFACE_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 SoC o a un OEM/ODM.
  • MODULE dovrebbe essere una parola minuscola che descrive il sottosistema (ad esempio nfc ). Se è necessaria più di una parola, utilizzare SUBMODULE nidificato. Può esserci più di un livello di annidamento.
  • VERSION dovrebbe essere esattamente la stessa versione (major.minor) descritta in Versions .
  • I INTERFACE_X dovrebbe 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 avere autorizzazioni non eseguibili (in Git).

Nomi dei pacchetti

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 che mappa su ROOT-DIRECTORY . In particolare PACKAGE è:
    • android.hardware per i pacchetti HIDL principali (mappatura su hardware/interfaces ).
    • vendor. VENDOR .hardware per pacchetti fornitore, dove VENDOR si riferisce a un fornitore SoC o un OEM/ODM (mappatura a vendor/ VENDOR /interfaces ).
  • MODULE [. SUBMODULE [. SUBMODULE […]]]@ VERSION hanno esattamente gli stessi nomi di cartelle nella struttura descritta in Struttura delle directory .
  • I nomi dei pacchetti dovrebbero essere in minuscolo. Se sono lunghi più di una parola, le parole dovrebbero essere usate come sottomoduli o scritte in snake_case .
  • Non sono ammessi spazi.

L'FQN viene sempre utilizzato nelle dichiarazioni dei pacchi.

Versioni

Le versioni dovrebbero avere il seguente formato:

MAJOR.MINOR

Sia la versione MAJOR che quella MINOR dovrebbero essere un singolo numero intero. HIDL utilizza regole di controllo delle versioni semantiche .

Importazioni

Un'importazione ha uno dei tre formati seguenti:

  • Importazioni di pacchetti interi: import PACKAGE-NAME ;
  • Importazioni parziali: import PACKAGE-NAME :: UDT ; (o, se il tipo importato è nello stesso pacchetto, import UDT ;
  • Importazioni solo di tipi: import PACKAGE-NAME ::types;

Il PACKAGE-NAME segue il formato in Packagenames . Il types.hal del pacchetto corrente (se esiste) viene importato automaticamente (non importarlo esplicitamente).

Nomi completi (FQN)

Utilizzare nomi completi per l'importazione di tipi definiti dall'utente solo quando necessario. Omettere PACKAGE-NAME se il tipo di importazione è 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 , fare riferimento all'interfaccia precedente come INfcClientCallback . Altrimenti, utilizzare solo il nome completo.

Raggruppare e ordinare le importazioni

Utilizzare una riga vuota dopo la dichiarazione del pacco (prima delle importazioni). Ogni importazione dovrebbe occupare una singola riga e non dovrebbe essere rientrata. Raggruppare le importazioni nel seguente ordine:

  1. Altri pacchetti android.hardware (utilizzare nomi completi).
  2. Altro vendor. VENDOR Pacchetti vendor. VENDOR (utilizzare nomi completi).
    • Ogni venditore dovrebbe essere un gruppo.
    • Ordina i venditori in ordine alfabetico.
  3. Importazioni da altre interfacce nello stesso pacchetto (usa nomi semplici).

Utilizzare una riga vuota tra i gruppi. All'interno di ciascun 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 una I , seguita da un nome UpperCamelCase / PascalCase . Un'interfaccia con nome IFoo deve essere definita nel file IFoo.hal . Questo file può contenere definizioni solo per l'interfaccia IFoo (l'interfaccia I NAME dovrebbe essere in I NAME .hal ).

Funzioni

Per i nomi delle funzioni, gli argomenti e i nomi delle variabili restituite, utilizzare lowerCamelCase . Esempio:

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

Nomi dei campi di struttura/unione

Per i nomi dei campi struttura/unione, utilizzare lowerCamelCase . Esempio:

struct FooReply {
    vec<uint8_t> replyData;
}

Digita i nomi

I nomi dei tipi si riferiscono a definizioni di struttura/unione, definizioni di tipo enum e typedef . Per questi nomi, utilizzare UpperCamelCase / PascalCase . Esempi:

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

Valori di enumerazione

I valori enum dovrebbero essere UPPER_CASE_WITH_UNDERSCORES . Quando si passano valori enum come argomenti di funzione e li si restituiscono come risultati di funzione, utilizzare 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 esplicitamente dopo i due punti. Poiché non dipende dal compilatore, l'utilizzo del tipo enum effettivo è più chiaro.

Per i nomi completi per i valori enum, vengono utilizzati i 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. Utilizzare un nome completo solo quando necessario e omettere le parti non necessarie. Esempio:

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

Commenti

Per un commento a riga singola, // , /* */ e /** */ vanno bene.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Utilizzare /* */ per i commenti. Anche se HIDL supporta // i commenti, questi sono sconsigliati perché non vengono visualizzati nell'output generato.
  • Utilizzare /** */ per la documentazione generata. Questi possono essere applicati solo alle 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. Utilizzare * all'inizio di ogni riga. Termina il commento con */ su una riga separata, allineando gli asterischi. Esempio:
    /**
     * My multi-line
     * comment
     */
    
  • Gli avvisi di licenza e i log delle modifiche dovrebbero iniziare una nuova riga con /* (un singolo asterisco), utilizzare * all'inizio di ogni riga e posizionare */ sull'ultima riga da solo (gli asterischi dovrebbero allinearsi). Esempio:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */
    

Archiviare i commenti

Inizia ogni file con l'avviso di licenza appropriato. Per gli HAL core, dovrebbe essere la licenza AOSP Apache in development/docs/copyright-templates/c.txt . Ricordarsi di aggiornare l'anno e utilizzare lo stile /* */ per i commenti su più righe come spiegato sopra.

Facoltativamente è possibile inserire una riga vuota dopo l'avviso di licenza, seguita da informazioni sul registro delle modifiche/versione. Utilizzare i commenti su più righe in stile /* */ come spiegato sopra, posizionare la riga vuota dopo il registro delle modifiche, quindi seguire con la dichiarazione del pacchetto.

TODO commenti

I TODO dovrebbero includere la stringa TODO tutta maiuscola seguita da due punti. Esempio:

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

I commenti TODO sono consentiti solo durante lo sviluppo; non devono esistere nelle interfacce pubblicate.

Commenti interfaccia/funzione (docstring)

Utilizzare /** */ per stringhe di documenti su più righe e su righe singole. Non utilizzare // per le docstring.

Le docstring per le interfacce dovrebbero descrivere i meccanismi generali dell'interfaccia, la logica di progettazione, lo scopo, ecc. Le docstring per le funzioni dovrebbero 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();
};

È necessario aggiungere @param s e @return s per ciascun parametro/valore restituito:

  • @param deve essere aggiunto per ogni parametro. Dovrebbe essere seguito dal nome del parametro e poi dalla docstring.
  • @return deve essere aggiunto per ogni valore restituito. Dovrebbe essere seguito dal nome del valore restituito, quindi 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);

Formattazione

Le regole generali di formattazione includono:

  • Lunghezza della linea . Ogni riga di testo deve essere lunga al massimo 100 colonne.
  • Spazi bianchi . Nessuno spazio bianco finale sulle righe; le righe vuote non devono contenere spazi bianchi.
  • Spazi e tabulazioni . Utilizza solo gli spazi.
  • Dimensione del rientro . Utilizza 4 spazi per i blocchi e 8 spazi per gli avvolgimenti di riga
  • Rinforzo . Ad eccezione dei valori di annotazione , una parentesi aperta va sulla stessa riga del codice precedente ma una parentesi graffa chiusa e il punto e virgola successivo occupano l'intera riga. Esempio:
    interface INfc {
        close();
    };
    

Dichiarazione del pacco

La dichiarazione del pacchetto dovrebbe trovarsi all'inizio del file dopo l'avviso di licenza, dovrebbe occupare l'intera riga e non dovrebbe essere rientrata. I pacchetti vengono dichiarati utilizzando il seguente formato (per la formattazione dei nomi, vedere Nomi dei pacchetti ):

package PACKAGE-NAME;

Esempio:

package android.hardware.nfc@1.0;

Dichiarazioni di funzioni

Il nome della funzione, i parametri, generates e i valori restituiti dovrebbero essere sulla stessa riga se si adattano. Esempio:

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

Se non rientrano nella stessa riga, provare a inserire parametri e valori restituiti nello stesso livello di rientro e distinguere generate per aiutare il lettore a vedere 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);
}

Dettagli aggiuntivi:

  • 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 parentesi e parametri , tranne quando sono presenti avanzamenti di riga tra di essi.
  • Se generates è sulla stessa riga della parentesi di chiusura precedente, utilizzare uno spazio precedente. Se generates è sulla stessa riga della successiva parentesi aperta, seguire con uno spazio.
  • Allinea tutti i parametri e restituisci i valori (se possibile).
  • Il rientro predefinito è di 4 spazi.
  • I parametri racchiusi 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 attorno 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 stare sulla stessa riga, rientra con 8 spazi. Esempio:

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

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

Se l'intero array di valori può stare nella stessa riga, non utilizzare spazi dopo le parentesi graffe aperte e prima di chiudere le parentesi graffe e utilizzare 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

Utilizzare le seguenti regole per le dichiarazioni di enumerazione:

  • Se le dichiarazioni enum sono condivise con un altro pacchetto, inserisci le dichiarazioni in types.hal anziché incorporarle all'interno di un'interfaccia.
  • Utilizzare uno spazio prima e dopo i due punti e uno spazio dopo il tipo sottostante prima della parentesi graffa aperta.
  • L'ultimo valore enum può o meno contenere una virgola aggiuntiva.

Dichiarazioni di struttura

Utilizzare le seguenti regole per le dichiarazioni struct:

  • Se le dichiarazioni struct sono condivise con un altro pacchetto, inserisci le dichiarazioni in types.hal invece di incorporarle all'interno di un'interfaccia.
  • Utilizzare uno spazio dopo il nome del tipo di struttura 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 quanto segue:

  • Tipo di elemento e parentesi quadra aperta.
  • Parentesi quadra aperta e dimensione dell'array.
  • Dimensioni dell'array e parentesi quadra chiusa.
  • Chiudere 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 quanto segue:

  • vec e parentesi angolare aperta.
  • Parentesi angolare aperta e tipo di elemento ( eccezione: anche il tipo di elemento è un vec ).
  • Tipo di elemento e parentesi uncinata chiusa ( eccezione: anche il tipo di elemento è 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;