Code-Styleguide

Der HIDL-Codestil ähnelt dem C++-Code im Android-Framework mit Einrückungen von 4 Leerzeichen und Dateinamen mit gemischter Groß-/Kleinschreibung. Paketdeklarationen, Importe und Docstrings ähneln denen in Java, mit leichten Änderungen.

Die folgenden Beispiele für IFoo.hal und types.hal veranschaulichen die HIDL-Codestile und bieten Schnelllinks zu Details zu den einzelnen Stilen (IFooClientCallback.hal, IBar.hal und IBaz.hal wurden ausgelassen).

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

Namenskonventionen

Funktionsnamen, Variablennamen und Dateinamen sollten aussagekräftig sein. Vermeiden Sie Abkürzungen. Behandeln Sie Akronyme als Wörter (verwenden Sie z. B. INfc statt INFC).

Verzeichnisstruktur und Dateibenennung

Die Verzeichnisstruktur sollte so aussehen:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (optional, kann mehr als eine Ebene umfassen)
        • VERSION
          • Android.mk
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal (optional)

Dabei gilt:

  • ROOT-DIRECTORY ist:
    • hardware/interfaces für HIDL-Kernpakete.
    • vendor/VENDOR/interfaces für Anbieterpakete, wobei VENDOR sich auf einen SoC-Anbieter oder einen OEM/ODM bezieht.
  • MODULE sollte ein Wort in Kleinbuchstaben sein, das das Subsystem beschreibt (z. B. nfc). Wenn mehr als ein Wort erforderlich ist, verwenden Sie verschachtelte SUBMODULE. Es kann mehr als eine Ebene der Verschachtelung geben.
  • VERSION sollte genau die Version (major.minor) sein, die unter Versionen beschrieben wird.
  • IINTERFACE_X sollte der Schnittstellenname mit UpperCamelCase/PascalCase (z. B. INfc) sein, wie unter Schnittstellennamen beschrieben.

Beispiel:

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

Hinweis:Alle Dateien müssen nicht ausführbare Berechtigungen (in Git) haben.

Paketnamen

Paketnamen müssen das folgende vollständig qualifizierte Namensformat (Fully Qualified Name, FQN) (als PACKAGE-NAME bezeichnet) verwenden:

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

Dabei gilt:

  • PACKAGE ist das Paket, das ROOT-DIRECTORY zugeordnet ist. Insbesondere: PACKAGE ist:
    • android.hardware für HIDL-Kernpakete (Zuordnung zu hardware/interfaces).
    • vendor.VENDOR.hardware für Anbieterpakete, wobei sich VENDOR auf einen SoC-Anbieter oder einen OEM/ODM bezieht (Zuordnung zu vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION sind die exakt gleichen Ordnernamen in der Struktur, die unter Verzeichnisstruktur beschrieben wird.
  • Paketnamen sollten in Kleinbuchstaben geschrieben werden. Wenn sie aus mehr als einem Wort bestehen, sollten die Wörter entweder als Untermodule verwendet oder in snake_case geschrieben werden.
  • Leerzeichen sind nicht zulässig.

Der FQN wird immer in Paketdeklarationen verwendet.

Versionen

Versionen sollten das folgende Format haben:

MAJOR.MINOR

Sowohl die MAJOR- als auch die MINOR-Version sollten eine einzelne Ganzzahl sein. HIDL verwendet semantische Versionsregeln.

Importe

Ein Import kann eines der folgenden drei Formate haben:

  • Importe des gesamten Pakets: import PACKAGE-NAME;
  • Teilimporte: import PACKAGE-NAME::UDT; (oder, wenn der importierte Typ im selben Paket ist,import UDT;
  • Nur Typen importieren: import PACKAGE-NAME::types;

Der PACKAGE-NAME entspricht dem Format unter Paketnamen. Die types.hal des aktuellen Pakets (falls vorhanden) wird automatisch importiert. Sie müssen sie nicht explizit importieren.

Vollständig qualifizierte Namen (Fully Qualified Names, FQNs)

Verwenden Sie nur bei Bedarf voll qualifizierte Namen für den Import eines benutzerdefinierten Typs. Lassen Sie PACKAGE-NAME weg, wenn sich der Importtyp im selben Paket befindet. Ein FQN darf keine Leerzeichen enthalten. Beispiel für einen vollständig qualifizierten Namen:

android.hardware.nfc@1.0::INfcClientCallback

Verweisen Sie in einer anderen Datei unter android.hardware.nfc@1.0 auf die oben genannte Schnittstelle als INfcClientCallback. Andernfalls verwenden Sie nur den voll qualifizierten Namen.

Importe gruppieren und sortieren

Verwenden Sie nach der Paketdeklaration (vor den Importen) eine leere Zeile. Jeder Import sollte in einer einzelnen Zeile stehen und nicht eingerückt sein. Importieren Sie Gruppen in der folgenden Reihenfolge:

  1. Andere android.hardware-Pakete (voll qualifizierte Namen verwenden).
  2. Andere vendor.VENDOR-Pakete (voll qualifizierte Namen verwenden).
    • Jeder Anbieter sollte eine Gruppe sein.
    • Anbieter alphabetisch sortieren.
  3. Importe aus anderen Schnittstellen im selben Paket (einfache Namen verwenden).

Lassen Sie zwischen den Gruppen eine leere Zeile. Sortieren Sie die Importe innerhalb jeder Gruppe alphabetisch. Beispiel:

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;

Schnittstellennamen

Schnittstellennamen müssen mit I beginnen, gefolgt von einem UpperCamelCase-/PascalCase-Namen. In der Datei IFoo.hal muss eine Schnittstelle mit dem Namen IFoo definiert sein. Diese Datei kann nur Definitionen für die IFoo-Schnittstelle enthalten (die Schnittstelle INAME sollte sich in INAME.hal befinden).

Funktionen

Verwenden Sie lowerCamelCase für Funktionsnamen, Argumente und Namen von Rückgabevariablen. Beispiel:

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

Struct- und Union-Feldnamen

Verwenden Sie für Struct- oder Union-Feldnamen lowerCamelCase. Beispiel:

struct FooReply {
    vec<uint8_t> replyData;
}

Typnamen

Typnamen beziehen sich auf Struktur- oder Union-Definitionen, Enum-Typdefinitionen und typedefs. Verwenden Sie für diese Namen UpperCamelCase/PascalCase. Beispiele:

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

Enum-Werte

Enum-Werte sollten UPPER_CASE_WITH_UNDERSCORES sein. Wenn Sie Enum-Werte als Funktionsargumente übergeben und als Funktionsrückgaben zurückgeben, verwenden Sie den tatsächlichen Enum-Typ (nicht den zugrunde liegenden Ganzzahltyp). Beispiel:

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

Hinweis:Der zugrunde liegende Typ eines Enum-Typs wird explizit nach dem Doppelpunkt deklariert. Da es nicht compilerabhängig ist, ist die Verwendung des tatsächlichen Enum-Typs eindeutiger.

Für vollständig qualifizierte Namen für Enum-Werte wird ein Doppelpunkt zwischen dem Enum-Typnamen und dem Enum-Wertnamen verwendet:

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

Ein vollständig qualifizierter Name darf keine Leerzeichen enthalten. Verwenden Sie einen vollständig qualifizierten Namen nur, wenn es notwendig ist, und lassen Sie unnötige Teile weg. Beispiel:

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

Kommentare

Für einen einzeiligen Kommentar sind //, /* */ und /** */ in Ordnung.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Verwenden Sie /* */ für Kommentare. HIDL unterstützt zwar // für Kommentare, aber davon wird abgeraten, da sie nicht in der generierten Ausgabe erscheinen.
  • Verwenden Sie /** */ für die generierte Dokumentation. Sie können nur auf Typ-, Methoden-, Feld- und Enum-Wertdeklarationen angewendet werden. Beispiel:
    /** 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,
        ...
    }
  • Mehrzeilige Kommentare beginnen mit /** in einer separaten Zeile. Verwenden Sie * am Anfang jeder Zeile. Beenden Sie den Kommentar mit */ in einer separaten Zeile und richten Sie die Sternchen aus. Beispiel:
    /**
     * My multi-line
     * comment
     */
  • Lizenzhinweise und Changelogs sollten mit einer neuen Zeile mit /* (ein einzelnes Sternchen) beginnen, * am Anfang jeder Zeile verwenden und */ in der letzten Zeile allein platzieren (Sternchen sollten ausgerichtet sein). Beispiel:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */

Dateikommentare

Beginnen Sie jede Datei mit dem entsprechenden Lizenzhinweis. Für Core-HALs sollte dies die AOSP-Apache-Lizenz in development/docs/copyright-templates/c.txt sein. Denken Sie daran, das Jahr zu aktualisieren und mehrzeilige Kommentare im /* */-Stil zu verwenden, wie oben beschrieben.

Optional können Sie nach dem Lizenzhinweis eine leere Zeile einfügen, gefolgt von einem Changelog oder Versionsinformationen. Verwenden Sie /* */-formatierte mehrzeilige Kommentare wie oben beschrieben, fügen Sie die leere Zeile nach dem Changelog ein und fahren Sie dann mit der Paketdeklaration fort.

TODO-Kommentare

TODOs sollten den String TODO in Großbuchstaben gefolgt von einem Doppelpunkt enthalten. Beispiel:

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

TODO-Kommentare sind nur während der Entwicklung zulässig. Sie dürfen nicht in veröffentlichten Schnittstellen vorhanden sein.

Kommentare für Schnittstellen und Funktionen (Docstrings)

Verwenden Sie /** */ für mehrzeilige und einzeilige Docstrings. Verwenden Sie // nicht für Docstrings.

Docstrings für Schnittstellen sollten allgemeine Mechanismen der Schnittstelle, die Designbegründung, den Zweck usw. beschreiben. Docstrings für Funktionen sollten spezifisch für die Funktion sein (Dokumentation auf Paketebene gehört in eine README-Datei im Paketverzeichnis).

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

Sie müssen für jeden Parameter/Rückgabewert @params und @returns hinzufügen:

  • @param muss für jeden Parameter hinzugefügt werden. Danach sollten der Name des Parameters und der Docstring folgen.
  • @return muss für jeden Rückgabewert hinzugefügt werden. Danach sollten der Name des Rückgabewerts und der Docstring folgen.

Beispiel:

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

Formatierungsregeln

Allgemeine Formatierungsregeln:

  • Zeilenlänge: Jede Textzeile darf maximal 100 Spalten lang sein.
  • Leerzeichen: Keine nachgestellten Leerzeichen in Zeilen; leere Zeilen dürfen keine Leerzeichen enthalten.
  • Leerzeichen im Vergleich zu Tabulatoren Verwenden Sie nur Leerzeichen.
  • Einzugsgröße: Verwenden Sie 4 Leerzeichen für Blöcke und 8 Leerzeichen für Zeilenumbrüche.
  • Abstützen Mit Ausnahme von annotation-Werten steht eine öffnende geschweifte Klammer in derselben Zeile wie der vorherige Code, eine schließende geschweifte Klammer und das folgende Semikolon belegen jedoch die gesamte Zeile. Beispiel:
    interface INfc {
        close();
    };

Paketerklärung

Die Paketdeklaration sollte nach dem Lizenzhinweis am Anfang der Datei stehen, die gesamte Zeile einnehmen und nicht eingerückt werden. Pakete werden im folgenden Format deklariert (Informationen zur Namensformatierung finden Sie unter Paketnamen):

package PACKAGE-NAME;

Beispiel:

package android.hardware.nfc@1.0;

Funktionsdeklarationen

Funktionsname, Parameter, generates und Rückgabewerte sollten in derselben Zeile stehen, sofern sie dort Platz haben. Beispiel:

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

Wenn sie nicht in dieselbe Zeile passen, versuchen Sie, Parameter und Rückgabewerte auf derselben Einzugsebene zu platzieren und generate zu verwenden, damit der Leser die Parameter und Rückgabewerte schnell erkennen kann. Beispiel:

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

Weitere Informationen:

  • Eine öffnende Klammer steht immer in derselben Zeile wie der Funktionsname.
  • Zwischen dem Funktionsnamen und der öffnenden Klammer dürfen keine Leerzeichen stehen.
  • Zwischen den Klammern und Parametern dürfen keine Leerzeichen stehen, außer, wenn Zeilenumbrüche zwischen ihnen vorhanden sind.
  • Wenn generates in derselben Zeile wie die schließende Klammer steht, verwenden Sie ein vorangestelltes Leerzeichen. Wenn generates in derselben Zeile wie die nächste offene Klammer steht, fügen Sie ein Leerzeichen ein.
  • Richten Sie alle Parameter und Rückgabewerte (falls möglich) aus.
  • Die Standardeinrückung beträgt 4 Leerzeichen.
  • Umgebrochene Parameter werden am ersten Parameter in der vorherigen Zeile ausgerichtet. Andernfalls haben sie einen Einzug von acht Leerzeichen.

Anmerkungen

Verwenden Sie das folgende Format für Anmerkungen:

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

Sortieren Sie die Anmerkungen in alphabetischer Reihenfolge und verwenden Sie Leerzeichen um Gleichheitszeichen. Beispiel:

@callflow(key = value)
@entry
@exit

Achten Sie darauf, dass eine Anmerkung die gesamte Zeile einnimmt. Beispiele:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

Wenn Anmerkungen nicht in dieselbe Zeile passen, rücken Sie sie um 8 Leerzeichen ein. Beispiel:

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

Wenn das gesamte Werte-Array nicht in dieselbe Zeile passt, fügen Sie Zeilenumbrüche nach den geschweiften Klammern { und nach jedem Komma im Array ein. Setzen Sie die schließende Klammer direkt nach dem letzten Wert. Lassen Sie die geschweiften Klammern weg, wenn nur ein Wert vorhanden ist.

Wenn das gesamte Werte-Array in dieselbe Zeile passt, verwenden Sie keine Leerzeichen nach öffnenden und vor schließenden geschweiften Klammern und ein Leerzeichen nach jedem Komma. Beispiele:

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

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

Zwischen den Anmerkungen und der Funktionsdeklaration dürfen KEINE leeren Zeilen sein. Beispiele:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Enum-Deklarationen

Beachten Sie die folgenden Regeln für Enum-Deklarationen:

  • Wenn Enum-Deklarationen mit einem anderen Paket geteilt werden, sollten Sie die Deklarationen in types.hal einfügen, anstatt sie in eine Schnittstelle einzubetten.
  • Verwenden Sie ein Leerzeichen vor und nach dem Doppelpunkt und ein Leerzeichen nach dem zugrunde liegenden Typ vor der öffnenden geschweiften Klammer.
  • Der letzte Aufzählungswert hat möglicherweise kein zusätzliches Komma.

Struct-Deklarationen

Beachten Sie die folgenden Regeln für die Deklaration von Strukturen:

  • Wenn Strukturdeklarationen mit einem anderen Paket geteilt werden, platzieren Sie die Deklarationen in types.hal, anstatt sie in eine Schnittstelle einzubetten.
  • Fügen Sie nach dem Namen des Strukturtyps vor der öffnenden geschweiften Klammer ein Leerzeichen ein.
  • Feldnamen anpassen (optional). Beispiel:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }

Arraydeklarationen

Fügen Sie keine Leerzeichen zwischen den folgenden Elementen ein:

  • Elementtyp und öffnende eckige Klammer.
  • Öffnende eckige Klammer und Arraygröße.
  • Arraygröße und schließende eckige Klammer.
  • Schließende eckige Klammer und die nächste öffnende eckige Klammer, wenn mehr als eine Dimension vorhanden ist.

Beispiele:

/* Good */
int32_t[5] array;

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

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

Vektoren

Fügen Sie keine Leerzeichen zwischen den folgenden Elementen ein:

  • vec und öffnende spitze Klammer.
  • Öffnende spitze Klammer und Elementtyp (Ausnahme: Der Elementtyp ist auch ein vec).
  • Elementtyp und schließende spitze Klammer (Ausnahme: Der Elementtyp ist auch ein vec).

Beispiele:

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