Code-Styleguide

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

Die folgenden Beispiele für IFoo.hal types.hal veranschaulichen HIDL-Codestile und bieten schnelle Links zu Details zu jedem Stil ( 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;
};

Regeln der Namensgebung

Funktionsnamen, Variablennamen und Dateinamen sollten beschreibend sein; Vermeiden Sie übermäßige Abkürzungen. Behandeln Sie Akronyme wie Wörter (verwenden Sie beispielsweise INfc anstelle von INFC ).

Verzeichnisstruktur und Dateibenennung

Die Verzeichnisstruktur sollte wie folgt aussehen:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (optional, kann mehr als eine Ebene umfassen)
        • VERSION
          • Android.mk
          • I INTERFACE_1 .hal
          • I INTERFACE_2 .hal
          • I INTERFACE_N .hal
          • types.hal (optional)

Wo:

  • ROOT-DIRECTORY lautet:
    • hardware/interfaces für Kern-HIDL-Pakete.
    • vendor/ VENDOR /interfaces für Anbieterpakete, wobei VENDOR sich auf einen SoC-Anbieter oder einen OEM/ODM bezieht.
  • MODULE sollte ein kleingeschriebenes Wort sein, das das Subsystem beschreibt (z. B. nfc ). Wenn mehr als ein Wort benötigt wird, verwenden Sie verschachteltes SUBMODULE . Es kann mehr als eine Verschachtelungsebene geben.
  • VERSION sollte genau dieselbe Version (major.minor) sein, wie unter Versionen beschrieben.
  • I INTERFACE_X sollte der Schnittstellenname mit UpperCamelCase / PascalCase (z. B. INfc ) sein, wie in Schnittstellennamen beschrieben.

Beispiel:

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

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

Paketnamen

Paketnamen müssen das folgende FQN-Format (Fully Qualified Name) verwenden (bezeichnet als PACKAGE-NAME ):

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

Wo:

  • PACKAGE ist das Paket, das dem ROOT-DIRECTORY zugeordnet ist. PACKAGE ist insbesondere:
    • android.hardware für HIDL-Kernpakete (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 die gleichen Ordnernamen in der in Verzeichnisstruktur beschriebenen Struktur.
  • Paketnamen sollten in Kleinbuchstaben geschrieben werden. Wenn sie mehr als ein Wort lang sind, sollten die Wörter entweder als Submodule verwendet oder in snake_case geschrieben werden.
  • Es sind keine Leerzeichen erlaubt.

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 Versionierungsregeln .

Importe

Ein Import hat eines der folgenden drei Formate:

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

Der PACKAGE-NAME folgt dem Format in Paketnamen . Die types.hal des aktuellen Pakets (falls vorhanden) wird automatisch importiert (nicht explizit importieren).

Vollqualifizierte Namen (FQNs)

Verwenden Sie für einen benutzerdefinierten Typimport nur dann vollständig qualifizierte Namen, wenn dies erforderlich ist. Lassen Sie PACKAGE-NAME weg, wenn sich der Importtyp im selben Paket befindet. Ein FQN darf keine Leerzeichen enthalten. Beispiel für einen vollqualifizierten Namen:

android.hardware.nfc@1.0::INfcClientCallback

Beziehen Sie sich in einer anderen Datei unter android.hardware.nfc@1.0 auf die obige Schnittstelle als INfcClientCallback . Andernfalls verwenden Sie nur den vollständig qualifizierten Namen.

Importe gruppieren und ordnen

Verwenden Sie nach der Paketdeklaration (vor den Importen) eine leere Zeile. Jeder Import sollte eine einzelne Zeile einnehmen und nicht eingerückt sein. Gruppieren Sie Importe in der folgenden Reihenfolge:

  1. Andere android.hardware Pakete (vollständig qualifizierte Namen verwenden).
  2. Anderer vendor. VENDOR Pakete (vollständig qualifizierte Namen verwenden).
    • Jeder Anbieter sollte eine Gruppe sein.
    • Bestellen Sie die Anbieter alphabetisch.
  3. Importiert von anderen Schnittstellen im selben Paket (einfache Namen verwenden).

Verwenden Sie eine Leerzeile zwischen den Gruppen. 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 einem 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 I NAME sollte in I NAME .hal enthalten sein).

Funktionen

Verwenden Sie für Funktionsnamen, Argumente und Rückgabevariablennamen lowerCamelCase . Beispiel:

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

Struktur-/Vereinigungsfeldnamen

Verwenden Sie für Struktur-/Union-Feldnamen lowerCamelCase . Beispiel:

struct FooReply {
    vec<uint8_t> replyData;
}

Geben Sie Namen ein

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

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

Enum-Werte

Enum-Werte sollten UPPER_CASE_WITH_UNDERSCORES sein. Wenn Sie Aufzählungswerte als Funktionsargumente übergeben und als Funktionsrückgaben zurückgeben, verwenden Sie den tatsächlichen Aufzählungstyp (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 Aufzählungstyps wird explizit nach dem Doppelpunkt deklariert. Da es nicht vom Compiler abhängig ist, ist die Verwendung des tatsächlichen Aufzählungstyps klarer.

Bei vollständig qualifizierten Namen für Aufzählungswerte wird ein Doppelpunkt zwischen dem Namen des Aufzählungstyps und dem Namen des Aufzählungswerts verwendet:

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

Ein vollständig qualifizierter Name darf keine Leerzeichen enthalten. Verwenden Sie nur bei Bedarf einen vollständig qualifizierten Namen 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. Während HIDL // für Kommentare unterstützt, wird davon abgeraten, da sie nicht in der generierten Ausgabe erscheinen.
  • Verwenden Sie /** */ für die generierte Dokumentation. Diese 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,
        ...
    }
    
  • Beginnen Sie mehrzeilige Kommentare 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 Änderungsprotokolle sollten eine neue Zeile mit /* (einem einzelnen Sternchen) beginnen, * am Anfang jeder Zeile verwenden und */ allein in der letzten Zeile 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 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 /* */ zu verwenden, wie oben erläutert.

Optional können Sie nach dem Lizenzhinweis eine Leerzeile einfügen, gefolgt von einem Änderungsprotokoll/Versionierungsinformationen. Verwenden Sie mehrzeilige Kommentare im Stil /* */ wie oben beschrieben, platzieren Sie die leere Zeile nach dem Änderungsprotokoll und folgen Sie dann mit der Paketdeklaration.

TODO-Kommentare

TODOs sollten die Zeichenfolge 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.

Schnittstellen-/Funktionskommentare (Docstrings)

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

Dokumentzeichenfolgen für Schnittstellen sollten allgemeine Mechanismen der Schnittstelle, Designprinzipien, Zweck usw. beschreiben. Dokumentzeichenfolgen für Funktionen sollten spezifisch für die Funktion sein (Dokumentation auf Paketebene befindet sich in einer 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 @param s und @return s für jeden Parameter/Rückgabewert hinzufügen:

  • @param muss für jeden Parameter hinzugefügt werden. Darauf sollte der Name des Parameters und dann die Dokumentzeichenfolge folgen.
  • @return muss für jeden Rückgabewert hinzugefügt werden. Darauf sollte der Name des Rückgabewerts und dann die Dokumentzeichenfolge 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);

Formatierung

Zu den allgemeinen Formatierungsregeln gehören:

  • Linienlänge . Jede Textzeile sollte maximal 100 Spalten lang sein.
  • Leerzeichen . Keine abschließenden Leerzeichen in Zeilen; Leerzeilen dürfen keine Leerzeichen enthalten.
  • Leerzeichen vs. Tabulatoren . Verwenden Sie nur Leerzeichen.
  • Einzugsgröße . Verwenden Sie 4 Leerzeichen für Blöcke und 8 Leerzeichen für Zeilenumbrüche
  • Versteifung . Mit Ausnahme von Anmerkungswerten steht eine öffnende geschweifte Klammer in derselben Zeile wie der vorangehende Code, aber eine schließende geschweifte Klammer und das folgende Semikolon nehmen die gesamte Zeile ein. Beispiel:
    interface INfc {
        close();
    };
    

Paketdeklaration

Die Paketdeklaration sollte am Anfang der Datei nach dem Lizenzhinweis stehen, die gesamte Zeile einnehmen und nicht eingerückt sein. 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, wenn sie passen. 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 Einrückungsebene zu platzieren und generate zu unterscheiden, 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 Details:

  • Eine offene Klammer steht immer in derselben Zeile wie der Funktionsname.
  • Zwischen dem Funktionsnamen und der offenen Klammer dürfen keine Leerzeichen stehen.
  • Zwischen den Klammern und Parametern dürfen keine Leerzeichen stehen , es sei denn , dazwischen befinden sich Zeilenumbrüche.
  • Wenn sich generates in derselben Zeile wie die vorherige schließende Klammer befindet, verwenden Sie ein vorangehendes Leerzeichen. Wenn sich generates in derselben Zeile wie die nächste offene Klammer befindet, fügen Sie danach ein Leerzeichen ein.
  • Richten Sie alle Parameter und Rückgabewerte aus (falls möglich).
  • Der Standardeinzug beträgt 4 Leerzeichen.
  • Umschlossene 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

Stellen Sie sicher, 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 mit 8 Leerzeichen ein. Beispiel:

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

Wenn das gesamte Wertearray nicht in dieselbe Zeile passt, setzen Sie Zeilenumbrüche nach den offenen Klammern { und nach jedem Komma innerhalb des Arrays. Platzieren Sie die schließende Klammer unmittelbar nach dem letzten Wert. Setzen Sie die Klammern nicht, wenn nur ein Wert vorhanden ist.

Wenn das gesamte Wertearray in dieselbe Zeile passt, verwenden Sie nach den offenen und vor den schließenden Klammern keine Leerzeichen und verwenden 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 stehen. Beispiele:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Enum-Deklarationen

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

  • Wenn Enum-Deklarationen mit einem anderen Paket geteilt werden, fügen Sie die Deklarationen in types.hal ein, 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 offenen Klammer.
  • Der letzte Enumerationswert kann ein zusätzliches Komma enthalten oder auch nicht.

Strukturdeklarationen

Verwenden Sie die folgenden Regeln für Strukturdeklarationen:

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

Array-Deklarationen

Setzen Sie zwischen Folgendem keine Leerzeichen:

  • Elementtyp und offene eckige Klammer.
  • Öffnen Sie die eckige Klammer und die Array-Größe.
  • Arraygröße und schließende eckige Klammer.
  • Schließen Sie die eckige Klammer und öffnen Sie die nächste 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

Setzen Sie zwischen Folgendem keine Leerzeichen:

  • vec und offene Winkelhalterung.
  • Öffnen Sie die spitze Klammer und den 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;