HIDL yêu cầu mọi giao diện được viết bằng HIDL phải được tạo phiên bản. Sau khi giao diện HAL được phát hành, giao diện đó sẽ bị khoá và mọi thay đổi tiếp theo phải được thực hiện đối với phiên bản mới của giao diện đó. Mặc dù không thể sửa đổi một giao diện đã xuất bản nhất định, nhưng giao diện đó có thể được mở rộng bằng một giao diện khác.
Cấu trúc mã HIDL
Mã HIDL được sắp xếp theo các loại, giao diện và gói do người dùng xác định:
- Loại do người dùng xác định (UDT). HIDL cung cấp quyền truy cập vào một tập hợp các loại dữ liệu gốc có thể dùng để tạo các loại phức tạp hơn thông qua các cấu trúc, liên kết và liệt kê. UDT được truyền đến các phương thức của giao diện và có thể được xác định ở cấp gói (phổ biến cho tất cả giao diện) hoặc cục bộ cho một giao diện.
- Giao diện. Là một thành phần cơ bản của HIDL, giao diện bao gồm UDT và các nội dung khai báo phương thức. Giao diện cũng có thể kế thừa từ giao diện khác.
- Gói. Sắp xếp các giao diện HIDL liên quan và các loại dữ liệu mà các giao diện đó hoạt động. Gói được xác định bằng tên và phiên bản, đồng thời bao gồm các thông tin sau:
- Tệp định nghĩa kiểu dữ liệu có tên là
types.hal
. - Không có hoặc có giao diện, mỗi giao diện trong một tệp
.hal
riêng.
- Tệp định nghĩa kiểu dữ liệu có tên là
Tệp định nghĩa loại dữ liệu types.hal
chỉ chứa các UDT (tất cả UDT cấp gói được lưu giữ trong một tệp duy nhất). Tất cả giao diện trong gói đều có thể sử dụng bản trình bày bằng ngôn ngữ mục tiêu.
Triết lý lập phiên bản
Gói HIDL (chẳng hạn như android.hardware.nfc
), sau khi được phát hành cho một phiên bản nhất định (chẳng hạn như 1.0
), là không thể thay đổi; bạn không thể thay đổi gói này. Bạn chỉ có thể sửa đổi giao diện trong gói hoặc thực hiện mọi thay đổi đối với UDT trong gói đó trong một gói khác.
Trong HIDL, việc tạo phiên bản áp dụng ở cấp gói chứ không phải ở cấp giao diện, đồng thời tất cả giao diện và UDT trong một gói đều dùng chung một phiên bản. Các phiên bản gói tuân theo quy trình tạo phiên bản ngữ nghĩa mà không có cấp độ bản vá và các thành phần siêu dữ liệu bản dựng. Trong một gói nhất định, một bản phát hành phiên bản nhỏ có nghĩa là phiên bản mới của gói tương thích ngược với gói cũ và một bản phát hành phiên bản lớn có nghĩa là phiên bản mới của gói không tương thích ngược với gói cũ.
Về mặt khái niệm, một gói có thể liên quan đến một gói khác theo một trong những cách sau:
- Không hề.
- Khả năng mở rộng tương thích ngược ở cấp gói. Điều này xảy ra với các bản cập nhật phiên bản nhỏ mới (bản sửa đổi tăng dần tiếp theo) của một gói; gói mới có cùng tên và phiên bản lớn với gói cũ, nhưng có phiên bản nhỏ cao hơn. Về mặt chức năng, gói mới là tập hợp con của gói cũ, nghĩa là:
- Các giao diện cấp cao nhất của gói mẹ có trong gói mới, mặc dù các giao diện này có thể có các phương thức mới, UDT cục bộ giao diện mới (tiện ích cấp giao diện được mô tả bên dưới) và UDT mới trong
types.hal
. - Bạn cũng có thể thêm giao diện mới vào gói mới.
- Tất cả các loại dữ liệu của gói mẹ đều có trong gói mới và có thể được xử lý bằng các phương thức (có thể được triển khai lại) từ gói cũ.
- Bạn cũng có thể thêm các loại dữ liệu mới để sử dụng bằng các phương thức mới của giao diện hiện có đã được cập nhật hoặc bằng các giao diện mới.
- Các giao diện cấp cao nhất của gói mẹ có trong gói mới, mặc dù các giao diện này có thể có các phương thức mới, UDT cục bộ giao diện mới (tiện ích cấp giao diện được mô tả bên dưới) và UDT mới trong
- Khả năng mở rộng tương thích ngược ở cấp giao diện. Gói mới cũng có thể mở rộng gói ban đầu bằng cách bao gồm các giao diện riêng biệt về mặt logic chỉ cung cấp chức năng bổ sung chứ không phải chức năng cốt lõi.
Để đạt được mục đích này, bạn nên làm như sau:
- Các giao diện trong gói mới cần sử dụng lại các loại dữ liệu của gói cũ.
- Các giao diện trong gói mới có thể mở rộng giao diện của một hoặc nhiều gói cũ.
- Mở rộng khả năng không tương thích ngược ban đầu. Đây là bản cập nhật phiên bản chính của gói và không cần có mối tương quan nào giữa hai phiên bản này. Trong phạm vi có thể, bạn có thể biểu thị lớp này bằng cách kết hợp các loại từ phiên bản cũ của gói và kế thừa một tập hợp con của giao diện gói cũ.
Sắp xếp giao diện
Để có giao diện được cấu trúc tốt, bạn cần sửa đổi giao diện HIDL khi thêm các loại chức năng mới không có trong thiết kế ban đầu. Ngược lại, nếu bạn có thể hoặc dự kiến sẽ thực hiện thay đổi ở cả hai bên giao diện để giới thiệu chức năng mới mà không thay đổi chính giao diện, thì giao diện đó không có cấu trúc.
Treble hỗ trợ các thành phần hệ thống và nhà cung cấp được biên dịch riêng biệt, trong đó vendor.img
trên một thiết bị và system.img
có thể được biên dịch riêng biệt. Tất cả các hoạt động tương tác giữa vendor.img
và system.img
phải được xác định rõ ràng và kỹ lưỡng để có thể tiếp tục hoạt động trong nhiều năm. API này bao gồm nhiều nền tảng API, nhưng nền tảng chính là cơ chế IPC mà HIDL sử dụng để giao tiếp liên quy trình trên ranh giới system.img
/vendor.img
.
Yêu cầu
Tất cả dữ liệu được truyền qua HIDL phải được xác định rõ ràng. Để đảm bảo việc triển khai và ứng dụng có thể tiếp tục hoạt động cùng nhau ngay cả khi được biên dịch riêng biệt hoặc phát triển độc lập, dữ liệu phải tuân thủ các yêu cầu sau:
- Có thể được mô tả trực tiếp trong HIDL (sử dụng enum cấu trúc, v.v.) với tên và ý nghĩa ngữ nghĩa.
- Có thể được mô tả theo một tiêu chuẩn công khai như ISO/IEC 7816.
- Có thể được mô tả bằng tiêu chuẩn phần cứng hoặc bố cục phần cứng thực tế.
- Có thể là dữ liệu mờ (chẳng hạn như khoá công khai, mã nhận dạng, v.v.) nếu cần.
Nếu sử dụng dữ liệu mờ, thì chỉ một bên của giao diện HIDL mới được đọc dữ liệu đó. Ví dụ: nếu mã vendor.img
cung cấp cho một thành phần trên system.img
một thông báo chuỗi hoặc dữ liệu vec<uint8_t>
, thì chính system.img
không thể phân tích cú pháp dữ liệu đó; dữ liệu đó chỉ có thể được chuyển lại cho vendor.img
để diễn giải. Khi truyền một giá trị từ vendor.img
đến mã nhà cung cấp trên system.img
hoặc đến một thiết bị khác, định dạng của dữ liệu và cách diễn giải dữ liệu đó phải được mô tả chính xác và vẫn là một phần của giao diện.
Nguyên tắc
Bạn có thể viết một phương thức triển khai hoặc ứng dụng của HAL chỉ bằng cách sử dụng các tệp .hal (tức là bạn không cần phải xem nguồn Android hoặc các tiêu chuẩn công khai). Bạn nên chỉ định hành vi bắt buộc chính xác. Các câu lệnh như "một phương thức triển khai có thể thực hiện A hoặc B" khuyến khích các phương thức triển khai được liên kết với các ứng dụng mà chúng được phát triển.
Bố cục mã HIDL
HIDL bao gồm các gói lõi và gói nhà cung cấp.
Các giao diện HIDL cốt lõi là những giao diện do Google chỉ định. Các gói thuộc về lớp này bắt đầu bằng android.hardware.
và được đặt tên theo hệ thống con, có thể có các cấp độ đặt tên lồng nhau. Ví dụ: gói NFC có tên là android.hardware.nfc
và gói máy ảnh là android.hardware.camera
. Nhìn chung, gói cốt lõi có tên là android.hardware.
[name1
].[name2
]…. Ngoài tên, các gói HIDL còn có phiên bản. Ví dụ: gói android.hardware.camera
có thể ở phiên bản 3.4
; điều này rất quan trọng vì phiên bản của gói ảnh hưởng đến vị trí của gói trong cây nguồn.
Tất cả gói cốt lõi đều được đặt trong hardware/interfaces/
trong hệ thống xây dựng. Gói android.hardware.
[name1
].[name2
]… ở phiên bản $m.$n
nằm trong hardware/interfaces/name1/name2/
…/$m.$n/
; gói android.hardware.camera
phiên bản 3.4
nằm trong thư mục hardware/interfaces/camera/3.4/.
. Có một ánh xạ được mã hoá cứng giữa tiền tố gói android.hardware.
và đường dẫn hardware/interfaces/
.
Gói không phải lõi (nhà cung cấp) là những gói do nhà cung cấp SoC hoặc ODM sản xuất. Tiền tố cho các gói không phải lõi là vendor.$(VENDOR).hardware.
, trong đó $(VENDOR)
tham chiếu đến một nhà cung cấp SoC hoặc OEM/ODM. Thao tác này liên kết với đường dẫn vendor/$(VENDOR)/interfaces
trong cây (lệnh liên kết này cũng được mã hoá cứng).
Tên loại do người dùng xác định đủ điều kiện
Trong HIDL, mỗi UDT đều có một tên đủ điều kiện bao gồm tên UDT, tên gói nơi UDT được xác định và phiên bản gói. Tên đủ điều kiện chỉ được dùng khi khai báo các thực thể của loại này chứ không phải nơi xác định chính loại đó. Ví dụ: giả sử gói android.hardware.nfc,
phiên bản 1.0
xác định một cấu trúc có tên là NfcData
. Tại vị trí khai báo (dù trong types.hal
hay trong phần khai báo của giao diện), nội dung khai báo chỉ nêu:
struct NfcData { vec<uint8_t> data; };
Khi khai báo một thực thể của loại này (dù là trong cấu trúc dữ liệu hay làm tham số phương thức), hãy sử dụng tên loại đủ điều kiện:
android.hardware.nfc@1.0::NfcData
Cú pháp chung là PACKAGE@VERSION::UDT
, trong đó:
PACKAGE
là tên được phân tách bằng dấu chấm của một gói HIDL (ví dụ:android.hardware.nfc
).VERSION
là định dạng major.minor-version được phân tách bằng dấu chấm của gói (ví dụ:1.0
).UDT
là tên được phân tách bằng dấu chấm của một UDT HIDL. Vì HIDL hỗ trợ các UDT lồng nhau và giao diện HIDL có thể chứa các UDT (một loại khai báo lồng nhau), nên các dấu chấm được dùng để truy cập vào các tên.
Ví dụ: nếu nội dung khai báo lồng nhau sau đây được xác định trong tệp loại phổ biến trong gói android.hardware.example
phiên bản 1.0
:
// types.hal package android.hardware.example@1.0; struct Foo { struct Bar { // … }; Bar cheers; };
Tên đủ điều kiện của Bar
là android.hardware.example@1.0::Foo.Bar
. Nếu ngoài việc nằm trong gói trên, nội dung khai báo lồng nhau nằm trong một giao diện có tên là IQuux
:
// IQuux.hal package android.hardware.example@1.0; interface IQuux { struct Foo { struct Bar { // … }; Bar cheers; }; doSomething(Foo f) generates (Foo.Bar fb); };
Tên đủ điều kiện của Bar
là android.hardware.example@1.0::IQuux.Foo.Bar
.
Trong cả hai trường hợp, bạn chỉ có thể gọi Bar
là Bar
trong phạm vi khai báo của Foo
. Ở cấp gói hoặc giao diện, bạn phải tham chiếu đến Bar
thông qua Foo
: Foo.Bar
, như trong phần khai báo phương thức doSomething
ở trên. Ngoài ra, bạn có thể khai báo phương thức chi tiết hơn như sau:
// IQuux.hal doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);
Giá trị liệt kê đủ điều kiện
Nếu UDT là loại enum, thì mỗi giá trị của loại enum sẽ có tên đủ điều kiện bắt đầu bằng tên đủ điều kiện của loại enum, theo sau là dấu hai chấm, rồi theo sau là tên của giá trị enum. Ví dụ: giả sử gói android.hardware.nfc,
phiên bản 1.0
xác định một loại enum NfcStatus
:
enum NfcStatus { STATUS_OK, STATUS_FAILED };
Khi tham chiếu đến STATUS_OK
, tên đủ điều kiện là:
android.hardware.nfc@1.0::NfcStatus:STATUS_OK
Cú pháp chung là PACKAGE@VERSION::UDT:VALUE
, trong đó:
PACKAGE@VERSION::UDT
là tên đủ điều kiện giống hệt nhau cho loại enum.VALUE
là tên của giá trị.
Quy tắc tự động suy luận
Không cần chỉ định tên UDT đủ điều kiện. Tên UDT có thể bỏ qua những thông tin sau một cách an toàn:
- Gói, ví dụ:
@1.0::IFoo.Type
- Cả gói và phiên bản, ví dụ:
IFoo.Type
HIDL sẽ cố gắng hoàn tất tên bằng cách sử dụng các quy tắc tự động can thiệp (số quy tắc thấp hơn có nghĩa là mức độ ưu tiên cao hơn).
Quy tắc 1
Nếu bạn không cung cấp gói và phiên bản, hệ thống sẽ tìm tên cục bộ. Ví dụ:
interface Nfc { typedef string NfcErrorMessage; send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m); };
NfcErrorMessage
được tra cứu cục bộ và typedef
ở phía trên được tìm thấy. NfcData
cũng được tra cứu cục bộ, nhưng vì không được xác định cục bộ nên quy tắc 2 và 3 được sử dụng. @1.0::NfcStatus
cung cấp một phiên bản, vì vậy quy tắc 1 không áp dụng.
Quy tắc 2
Nếu quy tắc 1 không thành công và thiếu một thành phần của tên đủ điều kiện (gói, phiên bản hoặc gói và phiên bản), thì thành phần đó sẽ được tự động điền thông tin từ gói hiện tại. Sau đó, trình biên dịch HIDL sẽ tìm trong tệp hiện tại (và tất cả các tệp nhập) để tìm tên đủ điều kiện được tự động điền.
Sử dụng ví dụ trên, giả sử nội dung khai báo ExtendedNfcData
được thực hiện trong cùng một gói (android.hardware.nfc
) ở cùng một phiên bản (1.0
) với NfcData
, như sau:
struct ExtendedNfcData { NfcData base; // … additional members };
Trình biên dịch HIDL điền tên gói và tên phiên bản từ gói hiện tại để tạo tên UDT đủ điều kiện android.hardware.nfc@1.0::NfcData
. Vì tên này tồn tại trong gói hiện tại (giả sử tên này được nhập đúng cách), nên tên này được dùng để khai báo.
Tên trong gói hiện tại chỉ được nhập nếu một trong các điều kiện sau là đúng:
- Tệp này được nhập rõ ràng bằng câu lệnh
import
. - Phương thức này được xác định trong
types.hal
trong gói hiện tại
Quy trình tương tự sẽ được thực hiện nếu NfcData
chỉ được xác định bằng số phiên bản:
struct ExtendedNfcData { // autofill the current package name (android.hardware.nfc) @1.0::NfcData base; // … additional members };
Quy tắc 3
Nếu quy tắc 2 không tạo ra kết quả trùng khớp (UDT không được xác định trong gói hiện tại), thì trình biên dịch HIDL sẽ quét để tìm kết quả trùng khớp trong tất cả các gói đã nhập.
Sử dụng ví dụ trên, giả sử ExtendedNfcData
được khai báo trong phiên bản 1.1
của gói android.hardware.nfc
, 1.1
nhập 1.0
như dự kiến (xem Tiện ích cấp gói) và định nghĩa chỉ chỉ định tên UDT:
struct ExtendedNfcData { NfcData base; // … additional members };
Trình biên dịch tìm kiếm mọi UDT có tên NfcData
và tìm thấy một UDT trong android.hardware.nfc
ở phiên bản 1.0
, dẫn đến một UDT đủ điều kiện của android.hardware.nfc@1.0::NfcData
. Nếu tìm thấy nhiều kết quả trùng khớp cho một UDT đủ điều kiện một phần nhất định, trình biên dịch HIDL sẽ gửi một lỗi.
Ví dụ
Khi sử dụng quy tắc 2, một loại được nhập được xác định trong gói hiện tại sẽ được ưu tiên hơn một loại được nhập từ một gói khác:
// 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
được nội suy dưới dạngandroid.hardware.bar@1.0::S
và có trongbar/1.0/types.hal
(vìtypes.hal
được nhập tự động).IFooCallback
được nội suy dưới dạngandroid.hardware.bar@1.0::IFooCallback
bằng quy tắc 2, nhưng không thể tìm thấy vìbar/1.0/IFooCallback.hal
không được nhập tự động (nhưtypes.hal
). Do đó, quy tắc 3 sẽ phân giải thànhandroid.hardware.foo@1.0::IFooCallback
(được nhập thông quaimport android.hardware.foo@1.0;
).
types.hal
Mỗi gói HIDL đều chứa một tệp types.hal
chứa các UDT được chia sẻ giữa tất cả các giao diện tham gia gói đó. Các loại HIDL luôn công khai; bất kể UDT được khai báo trong types.hal
hay trong nội dung khai báo giao diện, bạn đều có thể truy cập các loại này bên ngoài phạm vi xác định. types.hal
không dùng để mô tả API công khai của một gói, mà là để lưu trữ các UDT mà tất cả giao diện trong gói sử dụng. Do bản chất của HIDL, tất cả UDT đều là một phần của giao diện.
types.hal
bao gồm các UDT và câu lệnh import
.
Vì types.hal
được cung cấp cho mọi giao diện của gói (đây là một lệnh nhập ngầm), nên theo định nghĩa, các câu lệnh import
này thuộc cấp gói. UDT trong types.hal
cũng có thể kết hợp các UDT và giao diện được nhập theo cách này.
Ví dụ: đối với 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;
Các phần sau đây được nhập:
android.hidl.base@1.0::IBase
(ngầm ẩn)android.hardware.foo@1.0::types
(ngầm ẩn)- Mọi thứ trong
android.hardware.bar@1.0
(bao gồm cả tất cả giao diện vàtypes.hal
của giao diện đó) types.hal
từandroid.hardware.baz@1.0::types
(không nhập giao diện trongandroid.hardware.baz@1.0
)IQux.hal
vàtypes.hal
từandroid.hardware.qux@1.0
Quuz
từandroid.hardware.quuz@1.0
(giả sửQuuz
được xác định trongtypes.hal
, toàn bộ tệptypes.hal
sẽ được phân tích cú pháp, nhưng các loại khác ngoàiQuuz
sẽ không được nhập).
Tạo phiên bản ở cấp giao diện
Mỗi giao diện trong một gói nằm trong tệp riêng. Gói giao diện thuộc về được khai báo ở đầu giao diện bằng câu lệnh package
. Sau phần khai báo gói, có thể không có hoặc có nhiều lệnh nhập cấp giao diện (một phần hoặc toàn bộ gói) được liệt kê. Ví dụ:
package android.hardware.nfc@1.0;
Trong HIDL, các giao diện có thể kế thừa từ các giao diện khác bằng cách sử dụng từ khoá extends
. Để mở rộng một giao diện khác, giao diện đó phải có quyền truy cập vào giao diện đó thông qua câu lệnh import
. Tên của giao diện được mở rộng (giao diện cơ sở) tuân theo các quy tắc về việc xác định tên loại được giải thích ở trên. Một giao diện chỉ có thể kế thừa từ một giao diện; HIDL không hỗ trợ nhiều kiểu kế thừa.
Các ví dụ về việc tạo phiên bản uprev dưới đây sử dụng gói sau:
// 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); }
Quy tắc Uprev
Để xác định một gói package@major.minor
, A hoặc tất cả B phải đúng:
Quy tắc A | "Is a start minor version" (Có phải là phiên bản nhỏ bắt đầu không): Không được xác định tất cả các phiên bản nhỏ trước đó, package@major.0 , package@major.1 , …, package@major.(minor-1) .
|
---|
Quy tắc B | Tất cả các điều kiện sau đều đúng:
|
---|
Do quy tắc A:
- Gói có thể bắt đầu bằng bất kỳ số phiên bản phụ nào (ví dụ:
android.hardware.biometrics.fingerprint
bắt đầu từ@2.1
). - Yêu cầu "
android.hardware.foo@1.0
không được xác định" có nghĩa là thư mụchardware/interfaces/foo/1.0
thậm chí không được tồn tại.
Tuy nhiên, quy tắc A không ảnh hưởng đến một gói có cùng tên gói nhưng có phiên bản chính khác (ví dụ: android.hardware.camera.device
đã xác định cả @1.0
và @3.2
; @3.2
không cần tương tác với @1.0
). Do đó, @3.2::IExtFoo
có thể mở rộng @1.0::IFoo
.
Miễn là tên gói khác nhau, package@major.minor::IBar
có thể mở rộng từ một giao diện có tên khác (ví dụ: android.hardware.bar@1.0::IBar
có thể mở rộng android.hardware.baz@2.2::IBaz
). Nếu một giao diện không khai báo rõ ràng một loại siêu dữ liệu bằng từ khoá extend
, thì giao diện đó sẽ mở rộng android.hidl.base@1.0::IBase
(ngoại trừ chính IBase
).
Bạn phải tuân thủ cả B.2 và B.3 cùng một lúc. Ví dụ: ngay cả khi android.hardware.foo@1.1::IFoo
mở rộng android.hardware.foo@1.0::IFoo
để vượt qua quy tắc B.2, nếu android.hardware.foo@1.1::IExtBar
mở rộng android.hardware.foo@1.0::IBar
, thì đây vẫn không phải là bản cập nhật hợp lệ.
Giao diện Uprev
Cách nâng cấp android.hardware.example@1.0
(được xác định ở trên) lên @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); }
Đây là import
cấp gói của phiên bản 1.0
của
android.hardware.example
trong types.hal
. Mặc dù không có UDT mới nào được thêm vào phiên bản 1.1
của gói, nhưng bạn vẫn cần tham chiếu đến UDT trong phiên bản 1.0
, do đó, việc nhập ở cấp gói trong types.hal
là cần thiết. (Bạn cũng có thể đạt được hiệu quả tương tự bằng cách nhập ở cấp giao diện trong IQuux.hal
.)
Trong extends @1.0::IQuux
trong phần khai báo IQuux
, chúng ta đã chỉ định phiên bản IQuux
đang được kế thừa (bạn phải phân biệt vì IQuux
được dùng để khai báo giao diện và kế thừa từ giao diện). Vì nội dung khai báo chỉ là tên kế thừa tất cả các thuộc tính gói và phiên bản tại vị trí khai báo, nên nội dung phân định phải nằm trong tên của giao diện cơ sở; chúng ta cũng có thể sử dụng UDT đủ điều kiện, nhưng điều đó sẽ là thừa.
Giao diện mới IQuux
không khai báo lại phương thức fromFooToBar()
kế thừa từ @1.0::IQuux
; giao diện này chỉ liệt kê phương thức mới mà nó thêm vào fromBarToFoo()
. Trong HIDL, bạn không thể khai báo lại các phương thức kế thừa trong giao diện con, vì vậy, giao diện IQuux
không thể khai báo rõ ràng phương thức fromFooToBar()
.
Quy ước Uprev
Đôi khi, tên giao diện phải đổi tên giao diện mở rộng. Bạn nên đặt tên cho các tiện ích enum, cấu trúc và liên kết giống với tên của phần mở rộng, trừ phi các phần mở rộng đó khác biệt đủ để đảm bảo tên mới. Ví dụ:
// 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 };
Nếu một phương thức có thể có tên ngữ nghĩa mới (ví dụ: fooWithLocation
), thì đó là phương thức ưu tiên. Nếu không, lớp này phải được đặt tên tương tự như lớp mà nó đang mở rộng. Ví dụ: phương thức foo_1_1
trong @1.1::IFoo
có thể thay thế chức năng của phương thức foo
trong @1.0::IFoo
nếu không có tên thay thế nào tốt hơn.
Tạo phiên bản ở cấp gói
Việc tạo phiên bản HIDL diễn ra ở cấp gói; sau khi một gói được phát hành, gói đó sẽ không thể thay đổi (không thể thay đổi tập hợp giao diện và UDT). Các gói có thể liên quan với nhau theo một số cách, tất cả đều có thể biểu thị thông qua việc kết hợp kế thừa cấp giao diện và tạo UDT theo cấu trúc.
Tuy nhiên, một loại mối quan hệ được xác định nghiêm ngặt và phải được thực thi: Khả năng kế thừa tương thích ngược ở cấp gói. Trong trường hợp này, gói mẹ là gói được kế thừa và gói con là gói mở rộng gói mẹ. Sau đây là các quy tắc kế thừa tương thích ngược ở cấp gói:
- Tất cả giao diện cấp cao nhất của gói mẹ đều được kế thừa từ các giao diện trong gói con.
- Bạn cũng có thể thêm giao diện mới vào gói mới (không có quy định hạn chế về mối quan hệ với các giao diện khác trong các gói khác).
- Bạn cũng có thể thêm các loại dữ liệu mới để sử dụng bằng các phương thức mới của giao diện hiện có đã được cập nhật hoặc bằng các giao diện mới.
Bạn có thể triển khai các quy tắc này bằng cách sử dụng tính năng kế thừa cấp giao diện HIDL và thành phần UDT, nhưng cần có kiến thức cấp meta để biết các mối quan hệ này tạo thành một tiện ích gói tương thích ngược. Kiến thức này được suy luận như sau:
Nếu một gói đáp ứng yêu cầu này, hidl-gen
sẽ thực thi các quy tắc tương thích ngược.