Code-Styleguide

Der HIDL-Codestil ähnelt C++-Code im Android-Framework, mit Einzügen aus 4 Leerzeichen und Dateinamen mit gemischten 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-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 zu starke Abkürzungen. Akronyme als Wörter behandeln (z. B. 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 ist:
    • hardware/interfaces für Kern-HIDL-Pakete.
    • vendor/ VENDOR /interfaces für Anbieterpakete, wobei sich VENDOR 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 benötigt wird, verwenden Sie verschachteltes SUBMODULE . Es kann mehr als eine Verschachtelungsebene geben.
  • VERSION sollte genau die gleiche Version (major.minor) sein, wie in 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 (in Git) verfügen.

Paketnamen

Paketnamen müssen das folgende vollqualifizierte Namensformat (FQN) verwenden (als PACKAGE-NAME bezeichnet):

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

Wo:

  • PACKAGE ist das Paket, das dem ROOT-DIRECTORY zugeordnet ist. PACKAGE ist insbesondere:
    • android.hardware für Kern-HIDL-Pakete (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 unter 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.
  • Leerzeichen sind nicht erlaubt.

Die 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:

  • Import von ganzen Paketen: import PACKAGE-NAME ;
  • Teilimporte: import PACKAGE-NAME :: UDT ; (oder, wenn sich der importierte Typ im selben Paket befindet, import UDT ;
  • Nur-Typen-Importe: import PACKAGE-NAME ::types;

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

Vollqualifizierte Namen (FQNs)

Verwenden Sie nur bei Bedarf vollständig qualifizierte Namen für einen benutzerdefinierten Typimport. Lassen Sie PACKAGE-NAME wenn sich der Importtyp im selben Paket befindet. Eine FQN darf keine Leerzeichen enthalten. Beispiel für einen vollqualifizierten Namen:

android.hardware.nfc@1.0::INfcClientCallback

Verweisen Sie in einer anderen Datei unter android.hardware.nfc@1.0 auf die obige Schnittstelle als INfcClientCallback . Verwenden Sie andernfalls nur den vollständig qualifizierten Namen.

Gruppieren und Bestellen von Importen

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

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

Verwenden Sie eine Leerzeile zwischen 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 werden. Diese Datei kann nur Definitionen für die IFoo -Schnittstelle enthalten (die Schnittstelle I NAME sollte sich in I NAME .hal ).

Funktionen

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

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

Struktur-/Union-Feldnamen

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

struct FooReply {
    vec<uint8_t> replyData;
}

Geben Sie Namen ein

Typnamen beziehen sich auf Struct/Union-Definitionen, Enum-Typdefinitionen und typedef s. 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 Integer-Typ). 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 Enum-Typs übersichtlicher.

Bei vollqualifizierten Namen für Enum-Werte wird ein Doppelpunkt zwischen dem Namen des Enum-Typs und dem Namen des Enum-Werts verwendet:

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

Innerhalb eines vollständig qualifizierten Namens dürfen keine Leerzeichen enthalten sein. 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. Obwohl HIDL // für Kommentare unterstützt, wird davon abgeraten, da sie nicht in der generierten Ausgabe erscheinen.
  • Verwenden Sie /** */ für generierte Dokumentation. Diese können nur auf Typ-, Methoden-, Feld- und Aufzählungswertdeklarationen 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:
     * ...
     */
    

Kommentare ablegen

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 . Denken Sie daran, das Jahr zu aktualisieren und mehrzeilige Kommentare im Stil von /* */ wie oben beschrieben zu verwenden.

Optional können Sie nach dem Lizenzhinweis eine Leerzeile einfügen, gefolgt von einer Änderungsprotokoll-/Versionsinformation. Verwenden Sie mehrzeilige Kommentare im Stil von /* */ wie oben erläutert, platzieren Sie die leere Zeile nach dem Änderungsprotokoll und folgen Sie dann 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 erlaubt; 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.

Docstrings für Schnittstellen sollten allgemeine Mechanismen der Schnittstelle, Designgründe, Zweck usw. beschreiben. Docstrings für Funktionen sollten funktionsspezifisch 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 der Docstring folgen.
  • @return muss für jeden Rückgabewert hinzugefügt werden. Darauf sollte der Name des Rückgabewerts und dann 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);

Formatierung

Zu den allgemeinen Formatierungsregeln gehören:

  • Zeilenlänge . Jede Textzeile sollte höchstens 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
  • Verspannung . Mit Ausnahme von Anmerkungswerten steht eine öffnende geschweifte Klammer in derselben Zeile wie der vorhergehende Code, aber eine schließende geschweifte Klammer und das folgende Semikolon nehmen die gesamte Zeile ein. Beispiel:
    interface INfc {
        close();
    };
    

Paketerklärung

Die Paketdeklaration sollte am Anfang der Datei nach dem Lizenzhinweis stehen, die gesamte Zeile einnehmen und nicht eingerückt sein. Pakete werden unter Verwendung des folgenden Formats deklariert (zur Namensformatierung siehe 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 die gleiche Zeile passen, versuchen Sie, Parameter und Rückgabewerte auf die gleiche Einrückungsebene zu setzen 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 der gleichen Zeile wie der Funktionsname.
  • Keine Leerzeichen zwischen dem Funktionsnamen und der öffnenden Klammer.
  • Keine Leerzeichen zwischen den Klammern und Parametern, außer wenn Zeilenumbrüche dazwischen stehen.
  • Befindet sich generates in derselben Zeile wie die vorherige schließende Klammer, verwenden Sie ein vorangestelltes Leerzeichen. Befindet sich generates “ in derselben Zeile wie die nächste offene Klammer, folgt ein Leerzeichen.
  • Gleichen Sie alle Parameter und Rückgabewerte (wenn möglich) an.
  • Der Standardeinzug beträgt 4 Leerzeichen.
  • Umbrochene Parameter werden an den ersten Parametern in der vorherigen Zeile ausgerichtet, andernfalls haben sie einen Einzug von 8 Leerzeichen.

Anmerkungen

Verwenden Sie das folgende Format für Anmerkungen:

@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 um 8 Leerzeichen ein. Beispiel:

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

Wenn das gesamte Wertarray nicht in dieselbe Zeile passt, setzen Sie Zeilenumbrüche nach offenen Klammern { und nach jedem Komma innerhalb des Arrays. Platzieren Sie die schließende Klammer unmittelbar nach dem letzten Wert. Setzen Sie die geschweiften Klammern nicht, wenn es nur einen Wert gibt.

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

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

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

Zwischen Annotationen 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 Aufzählungswert kann ein zusätzliches Komma haben oder nicht.

Strukturdeklarationen

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

  • 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 vor der öffnenden geschweiften Klammer ein Leerzeichen.
  • Feldnamen ausrichten (optional). Beispiel:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }
    

Array-Deklarationen

Setzen Sie keine Leerzeichen zwischen:

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

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