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