HIDL 要求所有以 HIDL 編寫的介面都需要版本化。HAL 後 介面發布後,將進入凍結狀態,如需進一步變更, 新版介面。雖然指定的介面無法 則可由其他介面擴充
HIDL 程式碼結構
HIDL 程式碼是依使用者定義分類 類型、介面和套件:
- 使用者定義的類型 (UDT):HIDL 是用來存取一組 可用來組合較複雜類型的原始資料類型 結構、聯集和列舉項目UDT 會傳遞給 介面,而且可在套件層級定義 (所有 或本機介面傳送至介面
- 介面。是 HIDL 的基本構成要素,也就是介面 包含 UDT 和方法宣告。介面也可以繼承 另一個介面
- 套件。組織相關 HIDL 介面與資料
以及它們的運作類型套件是以名稱和版本做為識別
包含下列項目:
- 名為
types.hal
的資料類型定義檔案。 - 零或多個介面,每個介面分別位於各自的
.hal
檔案中。
- 名為
資料類型定義檔案 types.hal
僅包含 UDT (全部)
套件層級 UDT 會保存在單一檔案中)。目標中的表示法
套件中的所有介面皆可使用。
版本管理理念
之後的 HIDL 套件 (例如 android.hardware.nfc
)
發布的版本 (例如 1.0
) 無法變更。該資料來源
無法變更。修改套件中的介面或
對其 UDT 的變更只能在「其他」套件中進行。
在 HIDL 中,版本管理是套用於套件層級,而非介面層級。 且套件中的所有介面和 UDT 都會共用相同的版本。整批交易廣告 版本遵循語意 沒有修補程式等級和 build-metadata 元件的版本管理。在 特定套件的次要版本是指新版本 該套件可回溯相容於舊套件和主要套件 version 通則代表新版本的套件沒有 回溯相容於舊套件
從概念上來說,套件可透過以下其中一種方式與其他套件建立關聯:
- 完全不會。
- 套件層級回溯相容可擴充。這個
套件的新次要版本更新 (下一個遞增的修訂版本) 會發生;
新套件的名稱和主要版本與舊套件相同,但
小調版本從功能上來說,新套件是舊版 Pod 的超集合
包裝,也就是:
- 新套件中存在父項套件的頂層介面
儘管介面可能有新方法,但會使用新的介面本機 UDT (
介面層級擴充功能,以及
types.hal
。 - 新介面也可以新增至新套件。
- 父項套件的所有資料類型都存在於新套件中 可以透過舊套件的 (可能重新實作) 方法處理。
- 新的資料類型也可以透過更新過的新方法使用 或透過新介面進行預測
- 新套件中存在父項套件的頂層介面
儘管介面可能有新方法,但會使用新的介面本機 UDT (
介面層級擴充功能,以及
- 介面層級回溯相容的擴充能力。而
也可以將原始套件
只提供其他功能的介面,而非核心功能。
您可以針對這個目標執行下列動作:
- 新套件中的介面需要回溯至舊資料類型的資料類型 套件。
- 新套件中的介面可擴充一或多個舊版的介面 套件
- 擴充原始的回溯相容性。這是 套件的主要版本升級,因此 不過這兩者之間有一些主要差異在該數量上,這個數字可以結合 以及子集的子集 舊版套件介面
建構介面
為了打造結構完善的介面,建議您加入 不屬於原始設計的一部分,因此得修改 HIDL 存取 API相反地,如果預期或預期會在 介面將提供新功能,且在不變更介面的情況下 表示介面未結構化
Treble 支援另外編譯的供應商和系統元件
裝置上的 vendor.img
,system.img
可
個別編譯而成vendor.img
到 之間的所有互動
system.img
必須明確且明確定義,才能
該機構會繼續努力 多年這包含許多 API 介面
表面是 HIDL 採用的 IPC 機制
在
system.img
/vendor.img
個界線。
需求條件
透過 HIDL 傳送的所有資料都必須明確定義。為了確保 即使已編譯完成,用戶端仍可繼續合作 單獨或獨立開發的資料,必須遵守下列規定: 規定:
- 可直接在 HIDL 中說明 (使用結構體列舉等) 語意名稱和意義
- 可透過 ISO/IEC 7816 等公開標準提供。
- 可根據硬體標準或硬體實體配置來描述。
- 若有需要,可以是不透明資料 (例如公開金鑰、ID 等)。
如果使用不透明資料,則只能由 HIDL 的其中一側讀取該資料
存取 API舉例來說,如果 vendor.img
程式碼提供
system.img
字串訊息或 vec<uint8_t>
資料,system.img
本身無法剖析該資料;它可以
才會傳回 vendor.img
進行解讀時間
將值從 vendor.img
傳遞到
system.img
或其他裝置:資料格式及其方式
的解讀方式必須明確描述,且仍屬於
介面。
規範
您應該能夠僅使用 .hal 檔案 (也就是說,您不應該查看 Android 原始碼或公開檔案) 標準)。建議您指明必要的行為。這類陳述 「實作可能需 A 或 B」鼓勵採行的 與開發客戶的相對安全
HIDL 程式碼版面配置
HIDL 包含核心和供應商套件。
核心 HIDL 介面皆是由 Google 指定的介面。他們所屬的套件
開頭是 android.hardware.
,並以子系統命名
可能會使用巢狀結構命名舉例來說,NFC 套件的名稱為
android.hardware.nfc
,而相機套件為
android.hardware.camera
。一般來說,核心套件的名稱
android.hardware.
[name1
].[name2
]....
除了名稱之外,HIDL 套件還具有版本。舉例來說
android.hardware.camera
可能為 3.4
版本;這是
重要的是,因為套件版本會影響其在來源樹狀結構中的位置。
所有核心套件都位於以下項目的 hardware/interfaces/
中:
建構系統包裹
android.hardware.
[name1
]。[name2
]...
版本 $m.$n
低於
hardware/interfaces/name1/name2/
.../$m.$n/
;包裹
android.hardware.camera
版 3.4
位於目錄中
hardware/interfaces/camera/3.4/.
已存在硬式編碼對應
介於套件前置字串 android.hardware.
和路徑之間
hardware/interfaces/
。
非核心 (供應商) 套件是由 SoC 供應商或 ODM 產生的套件。
非核心套件的前置字串為 vendor.$(VENDOR).hardware.
,其中
$(VENDOR)
是指 SoC 供應商或原始設備製造商 (OEM)/ODM。這會對應至
樹狀結構中的 vendor/$(VENDOR)/interfaces
(此對應也能
硬式編碼)。
完整的使用者定義類型名稱
在 HIDL 中,每個 UDT 都有一個完整名稱,由 UDT 名稱、
定義 UDT 的套件名稱以及套件版本。
只有在執行個體已宣告且
未定義類型本身。例如,假設
android.hardware.nfc,
版本 1.0
定義了結構
名為 NfcData
。在宣告網站中 (無論
types.hal
或介面宣告中),
單純表示:
struct NfcData { vec<uint8_t> data; };
宣告此類型的例項時 (無論是在資料結構內 作為方法參數),請使用完整的類型名稱:
android.hardware.nfc@1.0::NfcData
一般語法為
PACKAGE@VERSION::UDT
,其中:
PACKAGE
是 HIDL 套件以點分隔的名稱 (例如:android.hardware.nfc
)。VERSION
是以點分隔的 main.minor-version 格式 (例如1.0
)。UDT
是 HIDL UDT 的以點分隔名稱。 由於 HIDL 支援巢狀 UDT 和 HIDL 介面,因此可以包含 UDT ( 巢狀宣告)),則使用半形句號來存取名稱。
舉例來說,如果下列巢狀宣告是由
套件 android.hardware.example
版本的類型檔案
1.0
:
// types.hal package android.hardware.example@1.0; struct Foo { struct Bar { // … }; Bar cheers; };
Bar
的完整名稱為
android.hardware.example@1.0::Foo.Bar
。如果除了
上述套件,巢狀宣告位於名為
IQuux
:
// IQuux.hal package android.hardware.example@1.0; interface IQuux { struct Foo { struct Bar { // … }; Bar cheers; }; doSomething(Foo f) generates (Foo.Bar fb); };
Bar
的完整名稱為
android.hardware.example@1.0::IQuux.Foo.Bar
。
在這兩種情況下,Bar
只能稱為 Bar
在 Foo
宣告範圍內。在套件中,
介面層級,您必須透過 Foo
參照 Bar
:
Foo.Bar
(如 doSomething
方法的宣告中所示)
。或者,您可以更詳盡地宣告方法,如下所示:
// IQuux.hal doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);
完整列舉值
如果 UDT 是列舉類型,則列舉類型的每個值都有
完整名稱,開頭為列舉類型的完整名稱,
後面加上冒號,然後是列舉值的名稱。例如:
假設套件 android.hardware.nfc,
第 1.0
版
定義了列舉類型 NfcStatus
:
enum NfcStatus { STATUS_OK, STATUS_FAILED };
參照 STATUS_OK
時,完整名稱為:
android.hardware.nfc@1.0::NfcStatus:STATUS_OK
一般語法為
PACKAGE@VERSION::UDT:VALUE
,
其中:
PACKAGE@VERSION::UDT
是 與列舉類型的完整名稱完全相同。VALUE
是值的名稱。
自動推論規則
您不需要指定完整的 UDT 名稱。UDT 名稱可以 安全地省略下列程式碼:
- 包裹,例如
@1.0::IFoo.Type
- 套件和版本,例如:
IFoo.Type
HIDL 會嘗試使用自動幹擾規則完成名稱 (較低規則) 數字表示優先順序較高)。
規則 1
如未提供套件和版本,系統會嘗試查詢本機名稱。 例子:
interface Nfc { typedef string NfcErrorMessage; send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m); };
已在本機查詢NfcErrorMessage
,typedef
或顯示在其他元件上方也在當地查詢了NfcData
,但內容不變
未在本機定義,使用的是規則 2 和 3。@1.0::NfcStatus
因此不會套用規則 1
規則 2
如果規則 1 失敗,且缺少完整名稱的組成部分
(套件、版本,或套件和版本),系統會透過
最新資訊接著,HIDL 編譯器會檢查
目前的檔案 (及所有匯入內容),找出自動填入的完整完整名稱。
使用上述範例假設 ExtendedNfcData
的宣告
已在同一套件 (android.hardware.nfc
) 中製作
版本 (1.0
) 取代為 NfcData
,如下所示:
struct ExtendedNfcData { NfcData base; // … additional members };
HIDL 編譯器會填寫
,產生完整的 UDT 名稱
android.hardware.nfc@1.0::NfcData
。這個 ID 存在於
目前的套件 (假設已正確匯入),則會用於
宣告內容
只有在下列其中一個項目出現時,系統才會匯入目前套件中的名稱 是:
- 會以
import
陳述式明確匯入。 - 這是在目前套件中的
types.hal
定義
如果 NfcData
僅由
版本號碼:
struct ExtendedNfcData { // autofill the current package name (android.hardware.nfc) @1.0::NfcData base; // … additional members };
規則 3
如果規則 2 無法產生相符項目 (目前資源中並未定義 UDT)
HIDL 編譯器),會掃描所有匯入的套件中是否有相符項目。
使用上述範例假設 ExtendedNfcData
android.hardware.nfc
套件的 1.1
版本,
1.1
會正確匯入 1.0
(請參閱
套件層級的擴充功能) 和定義
只會指定 UDT 名稱:
struct ExtendedNfcData { NfcData base; // … additional members };
編譯器會尋找任何名為 NfcData
的 UDT,並會在
於 1.0
版本為 android.hardware.nfc
,導致
完全合格的 UDT,共 android.hardware.nfc@1.0::NfcData
。如果顯示
在指定的部分 UDT (HIDL 編譯器) 找到一個以上的相符項目
擲回錯誤。
範例
使用規則 2 時,目前套件中定義的匯入類型會取代 從其他套件匯入的類型:
// hardware/interfaces/foo/1.0/types.hal package android.hardware.foo@1.0; struct S {}; // hardware/interfaces/foo/1.0/IFooCallback.hal package android.hardware.foo@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/types.hal package android.hardware.bar@1.0; typedef string S; // hardware/interfaces/bar/1.0/IFooCallback.hal package android.hardware.bar@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/IBar.hal package android.hardware.bar@1.0; import android.hardware.foo@1.0; interface IBar { baz1(S s); // android.hardware.bar@1.0::S baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback };
S
是內插類型,android.hardware.bar@1.0::S
,位置:bar/1.0/types.hal
(因為types.hal
會自動 已匯入)。IFooCallback
是內插類型,android.hardware.bar@1.0::IFooCallback
使用規則 2,但規則 2 未匯入bar/1.0/IFooCallback.hal
,因此找不到。 自動 (如types.hal
所示)。因此,規則 3 會解析為 改為android.hardware.foo@1.0::IFooCallback
,這是已匯入的資料 透過import android.hardware.foo@1.0;
)。
types.hal
每個 HIDL 套件都包含一個 types.hal
檔案,其中包含 UDT
該套件參與的所有介面共用HIDL 類型
一律為公開狀態;無論您是否在
types.hal
或介面宣告中,這些類型為
位於定義範圍外存取。types.hal
並非用來說明套件的公用 API,而是代管 UDT
套件中的所有介面使用基於 HIDL 的本質,所有 UDT
是介面的一部分
types.hal
包含 UDT 和 import
陳述式。
由於 types.hal
提供給
套件 (為隱式匯入),這些 import
陳述式會是
套件層級定義types.hal
中的 UDT 也可以納入
進而匯入 UDT 和介面。
以 IFoo.hal
為例:
package android.hardware.foo@1.0; // whole package import import android.hardware.bar@1.0; // types only import import android.hardware.baz@1.0::types; // partial imports import android.hardware.qux@1.0::IQux.Quux; // partial imports import android.hardware.quuz@1.0::Quuz;
系統會匯入以下項目:
android.hidl.base@1.0::IBase
(隱含)android.hardware.foo@1.0::types
(隱含)- 「
android.hardware.bar@1.0
」中的所有項目 (包括全部 介面及其types.hal
) - 距離
android.hardware.baz@1.0::types
types.hal
(android.hardware.baz@1.0
中的介面不會匯入) IQux.hal
和types.hal
:android.hardware.qux@1.0
- 來自
android.hardware.quuz@1.0
的Quuz
(假設Quuz
是在types.hal
中定義,也就是 已剖析types.hal
檔案,但Quuz
以外的類型 不會匯入)。
介面層級版本管理
套件中的每個介面都位於專屬的檔案中。套件
是透過
package
陳述式。在套件宣告之後 (零或多個)
系統可能會列出介面層級的匯入作業 (部分或完整套件)。例如:
package android.hardware.nfc@1.0;
在 HIDL 中,介面可以透過
extends
關鍵字。至於要擴充其他介面的介面
必須透過 import
陳述式存取。清單的名稱
擴充的介面 (基礎介面) 會遵循類型名稱的規則
評估。一個介面只能繼承一個介面;
HIDL 不支援多重繼承。
以下較新版本的版本管理範例使用下列套件:
// types.hal package android.hardware.example@1.0 struct Foo { struct Bar { vec<uint32_t> val; }; }; // IQuux.hal package android.hardware.example@1.0 interface IQuux { fromFooToBar(Foo f) generates (Foo.Bar b); }
更新規則
如要定義套件 package@major.minor
,可以採用 A 或整個 B
須為 true:
規則 A | 「是起始子版本」:所有先前的子版本,
package@major.0 、package@major.1 、...、
無法定義 package@major.(minor-1) 。
|
---|
規則 B | 符合下列所有條件:
|
---|
根據規則 A 的做法:
- 套件的開頭可以是任何次要版本號碼 (例如
android.hardware.biometrics.fingerprint
開始時間@2.1
)。 - 未定義要求「
android.hardware.foo@1.0
」意思是 目錄hardware/interfaces/foo/1.0
甚至不應存在。
不過,規則 A 不會對套件名稱相同的套件造成影響,
不同的主要版本 (例如
android.hardware.camera.device
同時包含@1.0
和
@3.2
已定義;@3.2
不需要互動
@1.0
)。因此,@3.2::IExtFoo
可以延長
@1.0::IFoo
。
如果套件名稱不同,
package@major.minor::IBar
可從具有
不同的名稱 (例如,android.hardware.bar@1.0::IBar
可以
擴充 android.hardware.baz@2.2::IBaz
)。如果介面沒有
使用 extend
關鍵字明確宣告超級類型
擴充 android.hidl.base@1.0::IBase
(IBase
除外)
其本身)。
請務必同時遵循 B.2 和 B.3。舉例來說
android.hardware.foo@1.1::IFoo
延伸
android.hardware.foo@1.0::IFoo
傳遞規則 B.2
android.hardware.foo@1.1::IExtBar
延長
android.hardware.foo@1.0::IBar
,這仍然無效。
升級介面
提升 android.hardware.example@1.0
(如上定義) 至
@1.1
:
// types.hal package android.hardware.example@1.1; import android.hardware.example@1.0; // IQuux.hal package android.hardware.example@1.1 interface IQuux extends @1.0::IQuux { fromBarToFoo(Foo.Bar b) generates (Foo f); }
這是套件層級 import
(第 1.0
版)
types.hal
中有 android.hardware.example
。但沒有新觀眾
系統會在套件的 1.1
版本中新增 UDT,在
仍需要 1.0
版,因此套件層級匯入作業
位置:types.hal
。(如果使用
IQuux.hal
中的介面層級匯入)。
在以下宣告的 extends @1.0::IQuux
中:
IQuux
,我們指定的 IQuux
版本
繼承 (由於 IQuux
是用於
宣告介面並繼承自某個介面)。如宣告內容所示
也就是沿用網站網站上所有套件和版本屬性的名稱
宣告時,釐清歧義必須位於基礎介面的名稱;我們
可能也使用了完整的 UDT
多餘的功能。
新介面 IQuux
不會重新宣告方法
fromFooToBar()
繼承自 @1.0::IQuux
;它只是
列出其新增 fromBarToFoo()
的新方法。在 HIDL 中沿用
方法「無法」在子項介面中再次宣告,因此
IQuux
介面無法宣告 fromFooToBar()
方法。
更新慣例
有時介面名稱必須重新命名擴充介面。建議做法 列舉擴充套件、結構體和聯集的名稱,與擴充項目的名稱相同 除非它們與新名稱之間有極大差異例如:
// in parent hal file enum Brightness : uint32_t { NONE, WHITE }; // in child hal file extending the existing set with additional similar values enum Brightness : @1.0::Brightness { AUTOMATIC }; // extending the existing set with values that require a new, more descriptive name: enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };
如果方法可擁有新的語意名稱 (例如
fooWithLocation
),然後設定為偏好選項。否則,
類似其延伸名稱舉例來說
@1.1::IFoo
中的 foo_1_1
可能會取代功能
就會使用 @1.0::IFoo
中的 foo
方法 (如果沒有更好的話)
替代名稱。
套件層級版本管理
HIDL 版本管理是在套件層級進行。套件發布後 是不可變動的 (介面集和 UDT 無法變更)。套件可以 都以多種方式傳達給彼此 是介面層級的繼承和建構 UDT 的組合。
不過,我們嚴格定義了其中一種關係,因此必須強制執行: 套件層級回溯相容繼承。在這種情況下 parent 套件是沿用自 child 套件是擴充父項的套件。套件層級 回溯相容的繼承規則如下:
- 父項套件的所有頂層介面,都是由 子套件。
- 您也可以增加新介面,以新增介面 (沒有 與其他套件中其他介面的關係)。
- 新的資料類型也可以透過更新過的新方法使用 或透過新介面進行預測
如要導入這些規則,可以使用 HIDL 介面層級繼承和 UDT 構成,但需要元級知識才能瞭解這些關係 則會視為回溯相容的套件擴充功能。這項知識是系統推論出來的 如下所示:
如果套件符合這項規定,hidl-gen
會強制執行
回溯相容規則