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, wobeiVENDOR
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 verschachtelteSUBMODULE
. 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 mitUpperCamelCase
/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 derROOT-DIRECTORY
zugeordnet ist. Insbesondere gilt fürPACKAGE
:android.hardware
für Kern-HIDL-Pakete (Zuordnung zuhardware/interfaces
).vendor.VENDOR.hardware
für Anbieterpakete, wobeiVENDOR
sich auf einen SoC-Anbieter oder einen OEM/ODM bezieht (Zuordnung zuvendor/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:
- Andere
android.hardware
-Pakete (verwenden Sie voll qualifizierte Namen). - Andere
vendor.VENDOR
-Pakete (verwenden Sie voll qualifizierte Namen).- Jeder Anbieter sollte eine Gruppe sein.
- Sortieren Sie die Anbieter alphabetisch.
- 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 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ü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 @param
s und @return
s 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 sichgenerates
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;