Code-Styleguide

Der HIDL-Codestil ähnelt C++-Code im Android-Framework, mit Einzügen von 4 Speichen und Dateinamen in Groß- und Kleinschreibung. Paketdeklarationen, Importe und Docstrings ähneln denen in Java, mit geringfügigen Änderungen.

Die folgenden Beispiele für IFoo.hal und types.hal veranschaulichen HIDL-Codestyles und enthalten Schnelllinks zu Details zu den einzelnen Stilen (IFooClientCallback.hal, IBar.hal und IBaz.hal wurden weggelassen).

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

Funktions-, Variablen- und Dateinamen sollten beschreibend sein. Vermeiden Sie zu viele Abkürzungen. Behandeln Sie Akronyme als Wörter (z. B. INfc anstelle von INFC).

Verzeichnisstruktur und Dateibenennung

Die Verzeichnisstruktur sollte so aussehen:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (optional, kann mehr als eine Ebene sein)
        • 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 Kleinbuchstabenwort sein, das das Subsystem beschreibt (z. B. nfc). Wenn mehr als ein Wort erforderlich ist, verwenden Sie verschachtelte SUBMODULE. Es kann mehrere Verschachtelungsebenen geben.
  • VERSION muss genau der Version (major.minor) entsprechen, die unter Versionen beschrieben ist.
  • IINTERFACE_X sollte der Name der Schnittstelle mit UpperCamelCase/PascalCase sein (z. B. INfc), wie unter Schnittstellennamen beschrieben.

Beispiel:

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

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

Paketnamen

Paketnamen müssen das folgende Format für vollständig qualifizierte Namen (Fully Qualified Name, FQN) verwenden (PACKAGE-NAME):

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

Dabei gilt:

  • PACKAGE ist das Paket, das der ROOT-DIRECTORY zugeordnet ist. Insbesondere gilt für PACKAGE:
    • android.hardware für Kern-HIDL-Pakete (Zuordnung zu hardware/interfaces).
    • vendor.VENDOR.hardware für Anbieterpakete, wobei VENDOR sich auf einen SoC-Anbieter oder einen OEM/ODM bezieht (Zuordnung zu vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION sind genau dieselben Ordnernamen wie in der Struktur, die im Abschnitt 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 vollständige Pfadname wird immer in Paketdeklarationen verwendet.

Versionen

Versionen müssen das folgende Format haben:

MAJOR.MINOR

Sowohl die MAJOR- als auch die MINOR-Version sollten ein einzelnes Ganzzahlformat haben. HIDL verwendet Regeln für die semantische Versionierung.

Importe

Ein Import kann eines der folgenden drei Formate haben:

  • Importe ganzer Pakete: import PACKAGE-NAME;
  • Teilimporte: import PACKAGE-NAME::UDT; (oder, wenn sich der importierte Typ im selben Paket befindet,import UDT;
  • Nur Typen importiert: import PACKAGE-NAME::types;

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

Voll 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 vollständiger qualifizierter Name darf keine Leerzeichen enthalten. Beispiel für einen voll qualifizierten Namen:

android.hardware.nfc@1.0::INfcClientCallback

Beziehen Sie sich in einer anderen Datei unter android.hardware.nfc@1.0 auf die oben genannte Benutzeroberfläche als INfcClientCallback. Andernfalls verwenden Sie nur den voll qualifizierten Namen.

Importe gruppieren und sortieren

Fügen Sie nach der Paketdeklaration (vor den Imports) eine leere Zeile ein. Jeder Import sollte eine einzelne Zeile umfassen und nicht eingerückt sein. Gruppieren Sie Importe in der folgenden Reihenfolge:

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

Fügen Sie zwischen den Gruppen eine leere Zeile ein. 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

Interfacenamen 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 darf nur Definitionen für die IFoo-Benutzeroberfläche enthalten. Die Benutzeroberfläche 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);

Namen von Struct- und Union-Feldern

Verwenden Sie für Feldnamen von Strukturen oder Unions lowerCamelCase. Beispiel:

struct FooReply {
    vec<uint8_t> replyData;
}

Typnamen

Typennamen beziehen sich auf Struktur- oder Union-Definitionen, Enumerationstypdefinitionen 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ückgabe 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 nach dem Doppelpunkt explizit deklariert. Da er nicht compilerabhängig ist, ist die Verwendung des tatsächlichen Enum-Typs übersichtlicher.

Bei vollständig qualifizierten Namen für Enum-Werte wird zwischen dem Namen des Enum-Typs und dem Namen des Enum-Werts ein Doppelpunkt 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 erforderlich 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 eignen sich //, /* */ und /** */.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Verwenden Sie /* */ für Kommentare. HIDL unterstützt // für Kommentare, sie werden jedoch nicht empfohlen, da sie nicht in der generierten Ausgabe erscheinen.
  • Verwenden Sie /** */ für generierte Dokumentation. Sie können nur auf Deklarationen von Typ, Methode, Feld und Enum-Wert 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 /** auf 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
     */
  • Der Lizenzhinweis und die Änderungsliste sollten in einer neuen Zeile mit /* (ein Sternchen) beginnen, * am Anfang jeder Zeile verwenden und */ in der letzten Zeile ganz alleine platzieren. Die Sternchen sollten ausgerichtet sein. Beispiel:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */

Dateikommentare

Jede Datei muss mit der entsprechenden Lizenzhinweis beginnen. Für Kern-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 von /* */ zu verwenden, wie oben beschrieben.

Optional können Sie nach der Lizenzmitteilung eine leere Zeile und dann einen Änderungslog/Versionshinweise einfügen. Verwenden Sie wie oben beschrieben mehrzeilige Kommentare im /* */-Format, platzieren Sie die leere Zeile nach dem Changelog und fahren Sie dann mit der Paketdeklaration fort.

TODO-Kommentare

TODOs müssen 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 Benutzeroberflächen vorhanden sein.

Benutzeroberflächen- und Funktionskommentare (Docstrings)

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

Docstrings für Oberflächen sollten allgemeine Mechanismen der Oberfläche, Designgrundlagen, Zweck usw. beschreiben. Docstrings für Funktionen sollten spezifisch für die Funktion sein. Die Dokumentation auf Paketebene wird in einer README-Datei im Paketverzeichnis abgelegt.

/**
 * 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. Darauf folgen der Name des Parameters und dann die Docstring.
  • Für jeden Rückgabewert muss @return hinzugefügt werden. Es sollte vom Namen des Rückgabewerts und dann von der Docstring gefolgt werden.

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

Zu den allgemeinen Formatierungsregeln gehören:

  • Zeilenlänge Jede Textzeile darf maximal 100 Spalten lang sein.
  • Leerzeichen: Zeilen dürfen keine nachgestellte Leerzeichen enthalten. Leere Zeilen dürfen keine Leerzeichen enthalten.
  • Liegt der Unterschied zwischen Leerzeichen und Tabulatoren? Verwenden Sie nur Leerzeichen.
  • Einzugsgröße: Verwenden Sie 4 Leerzeichen für Blöcke und 8 Leerzeichen für Zeilenumbrüche.
  • Verstärkung Mit Ausnahme von Anmerkungswerten steht ein öffnendes Quadratglied in derselben Zeile wie der vorherige Code, während ein schließendes Quadratglied und das folgende Semikolon die gesamte Zeile einnehmen. Beispiel:
    interface INfc {
        close();
    };

Verpackungserklärung

Die Paketdeklaration sollte sich oben in der Datei nach der Lizenzhinweis befinden, die gesamte Zeile einnehmen und nicht eingerückt sein. Pakete werden im folgenden Format deklariert. Informationen zur Formatierung von Namen finden Sie unter Paketnamen.

package PACKAGE-NAME;

Beispiel:

package android.hardware.nfc@1.0;

Funktionsdeklarationen

Funktionsname, Parameter, generates und Rückgabewerte sollten sich in derselben Zeile befinden, sofern dies möglich ist. 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 Einzugstufe zu platzieren und generate zu unterscheiden, damit die Leser die Parameter und Rückgabewerte schnell sehen können. 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:

  • Ein eckiger Klammern öffnender Bindestrich steht immer in derselben Zeile wie der Funktionsname.
  • Zwischen dem Funktionsnamen und der öffnenden Klammer darf kein Leerzeichen stehen.
  • Zwischen den Klammern und den Parametern dürfen keine Leerzeichen stehen, es sei denn, dazwischen sind Zeilenumbrüche.
  • Wenn sich generates in derselben Zeile wie die vorherige schließende Klammer befindet, setzen Sie davor ein Leerzeichen. Wenn sich generates in derselben Zeile wie die nächste öffnende Klammer befindet, setzen Sie ein Leerzeichen ein.
  • Richten Sie alle Parameter und Rückgabewerte (falls möglich) aus.
  • Die Standardeinzugweite beträgt vier Leerzeichen.
  • Umgebrochene Parameter werden an den ersten Parametern in der vorherigen Zeile ausgerichtet. Andernfalls haben sie einen Einzug von 8 Leerzeichen.

Anmerkungen

Verwenden Sie für Anmerkungen das folgende Format:

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

Sortieren Sie 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 auf dieselbe Zeile passen, setzen Sie einen Einzug von 8 Spalten. Beispiel:

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

Wenn das gesamte Wertearray nicht in eine Zeile passt, setzen Sie nach den öffnenden Klammern { und nach jedem Komma im Array einen Zeilenumbruch. Setzen Sie die schließende Klammer direkt nach dem letzten Wert. Setzen Sie die Klammern nicht, wenn nur ein Wert vorhanden ist.

Wenn das gesamte Wertearray in eine Zeile passt, dürfen nach öffnenden und vor schließenden Klammern keine Leerzeichen stehen. Setzen Sie nach jedem Komma ein Leerzeichen. Beispiele:

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

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

Zwischen Anmerkungen und der Funktionsdeklaration dürfen KEINE Leerzeilen vorhanden sein. Beispiele:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Enum-Deklarationen

Beachten Sie bei Enumerationsdeklarationen die folgenden Regeln:

  • Wenn Enum-Deklarationen für ein anderes Paket freigegeben werden, platzieren Sie die Deklarationen in types.hal, anstatt sie in eine Schnittstelle einzubetten.
  • Setzen Sie ein Leerzeichen vor und nach dem Doppelpunkt sowie ein Leerzeichen nach dem zugrunde liegenden Typ vor der öffnenden Klammer.
  • Der letzte Aufzählungswert enthält möglicherweise kein zusätzliches Komma.

Struct-Deklarationen

Beachten Sie bei Strukturdeklarationen die folgenden Regeln:

  • Wenn Strukturdeklarationen für ein anderes Paket freigegeben werden, platzieren Sie die Deklarationen in types.hal, anstatt sie in eine Schnittstelle einzubetten.
  • Setzen Sie nach dem Namen des Strukturtyps vor der öffnenden Klammer ein Leerzeichen.
  • Feldnamen ausrichten (optional) Beispiel:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }

Arraydeklarationen

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

  • Elementtyp und öffnender eckiger Klammernstrich
  • Öffnende eckige Klammer und Arraygröße.
  • Arraygröße und schließender eckiger Klammernstrich.
  • Geschlossene eckige Klammer und die nächste geöffnete eckige Klammer, falls 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 zwischen den folgenden Elementen keine Leerzeichen ein:

  • vec und eine öffnende spitze Klammer.
  • Öffnende spitze Klammer und Elementtyp (Ausnahme: Elementtyp ist auch ein vec).
  • Elementtyp und schließende spitze Klammer (Ausnahme: 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;