HIDL 程式碼樣式與 Android 架構中的 C++ 程式碼相似,有 4 個空格的縮排和大小寫混合檔案名稱。套件宣告、匯入和 docstring 與 Java 中的類似,但有輕微修改。
以下 IFoo.hal
和 types.hal
範例說明 HIDL 程式碼樣式,並提供各樣式的詳細資料快速連結 (IFooClientCallback.hal
、IBar.hal
和 IBaz.hal
已省略)。
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; }; |
命名慣例
函式名稱、變數名稱和檔案名稱應具描述性,避免過度縮寫。將縮寫詞視為單字 (例如,使用 INfc
而非 INFC
)。
目錄結構和檔案命名
目錄結構應如下所示:
ROOT-DIRECTORY
MODULE
SUBMODULE
(選用,可有多個層級)VERSION
Android.mk
IINTERFACE_1.hal
IINTERFACE_2.hal
…
IINTERFACE_N.hal
types.hal
(非必要)
地點:
ROOT-DIRECTORY
是:hardware/interfaces
:適用於核心 HIDL 套件。vendor/VENDOR/interfaces
適用於供應商套件,其中VENDOR
是指 SoC 供應商或 OEM/ODM。
MODULE
應為一個小寫字詞,用來描述子系統 (例如nfc
)。如果需要多個字詞,請使用巢狀SUBMODULE
。巢狀結構可以有一個以上的層級。VERSION
應與「版本」一節所述的版本 (major.minor) 完全相同。IINTERFACE_X
應為介面名稱,並包含UpperCamelCase
/PascalCase
(例如INfc
),如「介面名稱」一節所述。
例子:
hardware/interfaces
nfc
1.0
Android.mk
INfc.hal
INfcClientCallback.hal
types.hal
注意:所有檔案都必須具有非執行權限 (在 Git 中)。
套件名稱
套件名稱必須使用以下完整名稱 (FQN) 格式 (稱為 PACKAGE-NAME
):
PACKAGE .MODULE [.SUBMODULE [.SUBMODULE […]]]@VERSION
地點:
PACKAGE
是對應至ROOT-DIRECTORY
的套件。具體來說,PACKAGE
是:android.hardware
:核心 HIDL 套件 (對應至hardware/interfaces
)。vendor.VENDOR.hardware
適用於供應商套件,其中VENDOR
是指 SoC 供應商或 OEM/ODM (對應至vendor/VENDOR/interfaces
)。
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
是目錄結構中所述結構中的確切資料夾名稱。- 套件名稱應使用小寫字母。如果名稱超過一個字,則應將字詞用作子模組,或以
snake_case
編寫。 - 但不可包含空格。
FQN 一律會用於套件宣告。
版本
版本應採用下列格式:
MAJOR .MINOR
MAJOR 和 MINOR 版本都應為單一整數。HIDL 會使用語意版本化規則。
匯入
匯入檔案格式有下列三種:
- 整個套件匯入作業:
import PACKAGE-NAME;
- 部分匯入:
import PACKAGE-NAME::UDT;
(如果匯入的類型位於相同套件中,則為import UDT;
- 僅匯入類型:
import PACKAGE-NAME::types;
PACKAGE-NAME
會採用套件名稱的格式。系統會自動匯入目前套件的 types.hal
(如果存在) (請勿明確匯入)。
完整名稱 (FQDN)
只有在必要時,才使用完整的名稱匯入使用者定義的類型。如果匯入類型位於相同套件中,請省略 PACKAGE-NAME
。FQN 不得包含空格。完整名稱範例:
android.hardware.nfc@1.0::INfcClientCallback
在 android.hardware.nfc@1.0
下的另一個檔案中,將上述介面稱為 INfcClientCallback
。否則,請只使用完整的名稱。
匯入項目的分組和排序
在套件宣告後 (匯入項目之前) 使用空白行。每個匯入項目應占用單一行,且不應縮排。按照下列順序匯入群組:
- 其他
android.hardware
套件 (使用完整名稱)。 - 其他
vendor.VENDOR
套件 (使用完整名稱)。- 每個供應商都應是單一群組。
- 依字母順序排列供應商。
- 從相同套件中的其他介面匯入 (使用簡單名稱)。
請在各組之間使用空白行。在每個群組內,依字母順序排序匯入項目。例子:
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;
介面名稱
介面名稱的開頭必須是 I
,後方接著 UpperCamelCase
/PascalCase
名稱。檔案 IFoo.hal
中必須定義名稱為 IFoo
的介面。這個檔案只能包含 IFoo
介面的定義 (介面 INAME
應位於 INAME.hal
)。
函式
如要使用函式名稱、引數和傳回變數名稱,請使用 lowerCamelCase
。例子:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Struct 和 Union 欄位名稱
如要使用 struct 或 union 欄位名稱,請使用 lowerCamelCase
。例子:
struct FooReply { vec<uint8_t> replyData; }
類型名稱
型別名稱是指結構體或聯集定義、列舉型別定義和 typedef
。請使用 UpperCamelCase
/PascalCase
命名這些項目。例如:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
列舉值
列舉值應為 UPPER_CASE_WITH_UNDERSCORES
。將列舉值做為函式引數傳遞,並將其做為函式傳回時,請使用實際的列舉型別 (而非基礎的整數型別)。例子:
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 };
注意:列舉類型的基礎類型會在冒號後方明確宣告。由於不依賴編譯器,因此使用實際的列舉型別會更清楚。
列舉值的完整名稱會在列舉類型名稱和列舉值名稱之間使用冒號:
PACKAGE-NAME ::UDT [.UDT [.UDT […]]:ENUM_VALUE_NAME
完整名稱中不得有空格。請只在必要時使用完整的名稱,並省略不必要的部分。例子:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
留言
對於單行註解,//
、/* */
和 /** */
都沒問題。
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
-
使用
/* */
註解。雖然 HIDL 支援用於註解的//
,但不建議使用,因為這些註解不會出現在產生的輸出內容中。 - 使用
/** */
產生文件。這些標記只能套用至型別、方法、欄位和列舉值宣告。範例如下:/** 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, ... }
- 在另一行使用
/**
開始多行註解。在每行開頭使用*
。在另一行結束註解,並在結尾加上*/
,以便對齊星號。範例如下:/** * My multi-line * comment */
- 授權通知和變更記錄應以
/*
(單一星號) 開頭,並在每行開頭使用*
,最後在最後一行放置*/
(星號應對齊)。範例如下:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
檔案中的註解
請在每個檔案開頭加上適當的授權通知。對於核心 HAL,這應為 development/docs/copyright-templates/c.txt
中的 AOSP Apache 授權。請記得更新年份,並使用 /* */
樣式的多行註解,如上文所述。
您可以選擇在授權聲明後方加上空白行,接著是變更記錄/版本資訊。請按照上述說明使用 /* */
樣式的多行註解,並在變更記錄後方放置空白行,然後接著是套件宣告。
TODO 註解
TODO 應包含全大寫的字串 TODO
,後面接一個冒號。例子:
// TODO: remove this code before foo is checked in.
TODO 註解僅可在開發期間使用,不得出現在已發布的介面中。
介面和函式註解 (docstring)
使用 /** */
處理多行和單行 docstring。請勿將 //
用於 docstring。
介面說明文字應描述介面的一般機制、設計原理、用途等。函式說明文字應專屬於函式 (套件層級說明文件會放在套件目錄中的 README 檔案中)。
/** * 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(); };
您必須為每個參數/傳回值新增 @param
和 @return
:
- 每個參數都必須加入
@param
。後面應接著參數名稱和字串說明。 - 必須為每個傳回值新增
@return
。後面應接著傳回值的名稱,然後是 docstring。
例子:
/** * 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);
格式設定規則
一般格式設定規則包括:
- 行長度。每行文字長度上限為 100 個欄。
- 空白字元。行中不得有結尾空格;空白行不得包含空格。
- 聊天室與分頁。請只使用空格。
- 縮排尺寸:使用 4 個空格分隔區塊,並使用 8 個空格分隔行
- 支撐。除了註解值,開啟大括號會與前面的程式碼位於同一行,但關閉大括號和後面的分號會佔用整行。範例如下:
interface INfc { close(); };
套件宣告
套件宣告應位於檔案頂端的授權通知後方,應佔用整行,且不應縮排。套件會使用下列格式宣告 (如需名稱格式,請參閱「套件名稱」):
packagePACKAGE-NAME ;
例子:
package android.hardware.nfc@1.0;
函式宣告
如果函式名稱、參數、generates
和傳回值可放入同一行,則應將這些項目放在同一行。例子:
interface IFoo { /** ... */ easyMethod(int32_t data) generates (int32_t result); };
如果無法在同一行中顯示,請嘗試將參數和傳回值放在相同的縮排層級,並使用 generate
區分,方便讀者快速查看參數和傳回值。例子:
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); }
其他詳細資料:
- 開方括號一律與函式名稱位於同一行。
- 函式名稱與開方括號之間不得有空格。
- 在括號和參數之間不留空格,但如果兩者之間有換行符號,則可放置空格。
- 如果
generates
與前一個閉圓括號位於同一行,請使用前置空格。如果generates
與下一個開方括號位於同一行,請在後面加上空格。 - 對齊所有參數和傳回值 (如果可以的話)。
- 預設縮排為 4 個空格。
- 已包裝的參數會與前一行的第一個參數對齊,否則會以 8 個空格的縮排方式顯示。
註解
請使用下列格式提供註解:
@annotate(keyword = value, keyword = {value, value, value})
依照字母順序排序註解,並在等號兩側使用空格。例子:
@callflow(key = value) @entry @exit
確保註解佔據整個行。例如:
/* Good */ @entry @exit /* Bad */ @entry @exit
如果註解無法只占一行,請以 8 個空格縮排。例子:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
如果整個值陣列無法放在同一行,請在陣列內的開方括號 {
和每個半形逗號後面換行。將結束括號放在最後一個值的後方。如果只有一個值,請勿放置大括號。
如果整個值陣列可在同一行中顯示,請勿在開方括號後和結束方括號前使用空格,並在每個逗號後使用一個空格。例如:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
註解和函式宣告之間不得有空白行。例如:
/* Good */ @entry foo(); /* Bad */ @entry foo();
列舉宣告
請遵守下列規則宣告列舉:
- 如果列舉宣告與其他套件共用,請將宣告放在
types.hal
中,而不是嵌入介面中。 - 在冒號前後使用空格,並在基礎類型後方和開啟大括號前方使用空格。
- 最後一個列舉值可能不會有額外的半形逗號。
結構體宣告
請使用下列規則宣告結構體:
- 如果結構定義會與其他套件共用,請將定義放在
types.hal
中,而不是嵌入介面中。 - 在結構體類型名稱後方和左大括號前方使用空格。
- 對齊欄位名稱 (選用)。範例如下:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
陣列宣告
請勿在以下項目間加入空格:
- 元素類型和開方括號。
- 開啟方括號和陣列大小。
- 陣列大小和右方括號。
- 右方括號和下一個左方括號 (如果有多個維度)。
例如:
/* Good */ int32_t[5] array; /* Good */ int32_t[5][6] multiDimArray; /* Bad */ int32_t [ 5 ] [ 6 ] array;
向量
請勿在以下項目間加入空格:
vec
和左角括號。- 左角括號和元素類型 (例外狀況:元素類型也是
vec
)。 - 元素類型和閉合角度括號 (例外狀況:元素類型也是
vec
)。
例如:
/* 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;