HIDL yêu cầu mọi giao diện được viết bằng HIDL phải được phiên bản. Sau khi giao diện HAL được xuất bản, giao diện đó sẽ bị đóng băng 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ù 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ằng giao diện khác.
Cấu trúc mã HIDL
Mã HIDL được tổ chức theo các loại, 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ác cấu trúc, kết hợp và liệt kê. UDT được chuyển đến các phương thức của giao diện và có thể được xác định ở cấp độ gói (chung cho tất cả các giao diện) hoặc cục bộ cho một giao diện.
- 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 UDT và phương thức. Giao diện cũng có thể kế thừa từ giao diện khá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 trên đó. Một gói được xác định bằng tên và phiên bản và bao gồm những thông tin sau:
- Tệp định nghĩa kiểu dữ liệu có tên
types.hal
. - Không có hoặc nhiều giao diện, mỗi giao diện nằm trong tệp
.hal
riêng.
- Tệp định nghĩa kiểu dữ liệu có tên
Tệp định nghĩa kiểu dữ liệu types.hal
chỉ chứa UDT (tất cả các UDT cấp gói được lưu 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
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. Việc sửa đổi giao diện trong gói hoặc bất kỳ thay đổi nào đối với UDT của nó chỉ có thể diễn ra trong gói khác .
Trong HIDL, việc lập phiên bản áp dụng ở cấp gói chứ không phải ở cấp giao diện và tất cả các giao diện cũng như 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 phiên bản ngữ nghĩa mà không có các thành phần cấp bản vá và siêu dữ liệu xây dựng. Trong một gói nhất định, phần nâng cấp phiên bản phụ ngụ ý phiên bản mới của gói tương thích ngược với gói cũ và phần nâng cấp phiên bản chính ngụ ý 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 nhiều cách:
- Không có gì .
- 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 phụ mới (bản sửa đổi tăng dần tiếp theo) của gói; gói mới có cùng tên và phiên bản chính với gói cũ nhưng có phiên bản phụ cao hơn. Về mặt chức năng, gói mới là siêu bộ của gói cũ, nghĩa là:
- Các giao diện cấp cao nhất của gói gốc 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 giao diện cục bộ mới (phần mở rộng cấp giao diện được mô tả bên dưới) và UDT mới trong
types.hal
. - 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 gốc đề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ũ.
- Các loại dữ liệu mới cũng có thể được thêm vào để sử dụng theo các phương pháp mới của giao diện hiện có được nâng cấp 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 gốc 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 giao diện cục bộ mới (phần mở rộng 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. 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 các kiểu dữ liệu của gói 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 hai phiên bản này. Trong phạm vi có thể, nó có thể được thể hiện bằng sự kết hợp của các loại từ phiên bản cũ hơn của gói và kế thừa một tập hợp con các giao diện gói cũ.
Cấu trúc giao diện
Để có 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 muốn thực hiện thay đổi ở cả hai mặt của giao diện nhằm 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 đó vendor.img
trên một thiết bị và system.img
có thể được biên dịch riêng. 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 để liên lạc 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 truyề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 hoạt động cùng nhau ngay cả khi được biên dịch riêng 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 structs 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 chung, id, v.v.) nếu cần.
Nếu sử dụng dữ liệu mờ, nó chỉ được đọc bởi một bên của giao diện HIDL. Ví dụ: nếu mã nhà 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 bản thân system.img
không thể phân tích cú pháp dữ liệu đó; nó chỉ có thể được chuyển trở lại vendor.img
để giải thích. Khi chuyển một giá trị từ vendor.img
sang mã nhà cung cấp trên system.img
hoặc sang 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 .
Hướng dẫn
Bạn có thể viết bản 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 cộng). 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ư "việc 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 cùng phát triển với chúng.
Bố cục mã HIDL
HIDL bao gồm các gói cốt lõi và nhà cung cấp.
Giao diện HIDL cốt lõi là những giao diện được chỉ định bởi Google. Các gói thuộc về chúng 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 android.hardware.nfc
và gói camera là android.hardware.camera
. Nói chung, 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 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ư mục 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à đường dẫn hardware/interfaces/
.
Các gói không cốt lõi (nhà cung cấp) là những gói được sản xuất bởi nhà cung cấp SoC hoặc ODM. 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. Ánh xạ này tới 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 thể hiện của kiểu đó được khai báo chứ không phải khi kiểu đó được xác định. 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 NfcData
. Tại trang khai báo (dù ở types.hal
hay trong khai báo của giao diện), khai báo chỉ đơn giản nêu rõ:
struct NfcData { vec<uint8_t> data; };
Khi khai báo một thể hiện của loại 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 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 gói HIDL (ví dụ:android.hardware.nfc
). -
VERSION
là định dạng phiên bản chính.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à giao diện HIDL có thể chứa UDT (một loại khai báo lồng nhau), nên các dấu chấm được sử dụng để truy cập tên.
Ví dụ: nếu 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 cho Bar
là android.hardware.example@1.0::Foo.Bar
. Nếu, ngoài việc nằm trong gói trên, phần khai báo lồng nhau còn 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 khảo Bar
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 này một cách 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 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, 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 loại 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
chính xác là tên đủ điều kiện cho loại enum. -
VALUE
là tên của giá trị.
Quy tắc tự động suy luận
Không cần phải 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 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. 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à tìm thấy typedef
ở trên. NfcData
cũng được tra cứu cục bộ nhưng vì nó không được xác định cục bộ nên quy tắc 2 và 3 sẽ đượ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ử việc 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 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ên nó được sử dụng để khai báo.
Tên trong gói hiện tại chỉ được nhập nếu một trong những điều sau đây là đúng:
- Nó được nhập một cách rõ ràng bằng một câu lệnh
import
. - Nó được định nghĩa trong
types.hal
trong gói hiện tại
Quy trình tương tự được thực hiện nếu NfcData
chỉ đủ điều kiện theo 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ả khớ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 kết quả 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ư bình thường (xem Package-Level Extensions ) 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 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 là android.hardware.nfc@1.0::NfcData
. Nếu tìm thấy nhiều hơn một kết quả 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ẽ báo lỗi.
Ví dụ
Sử dụng quy tắc 2, loại đã nhập được xác định trong gói hiện tại sẽ được ưu tiên hơn loại được 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 làandroid.hardware.bar@1.0::S
và được tìm thấy 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
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 (nhưtypes.hal
). Do đó, quy tắc 3 sẽ giải quyết nó thànhandroid.hardware.foo@1.0::IFooCallback
, được nhập quaimport android.hardware.foo@1.0;
).
các loại.hal
Mỗi gói HIDL chứa một tệp types.hal
chứa 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 công khai; bất kể UDT được khai báo trong types.hal
hay trong khai báo giao diện, những loại này đều có thể truy cập được bên ngoài phạm vi mà chúng được xác định. 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 tính 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 UDT và câu lệnh import
. Bởi vì types.hal
được cung cấp cho mọi giao diện của gói (nó là một lệnh nhập ngầm), các câu lệnh import
này theo định nghĩa ở cấp độ gói. Các UDT trong types.hal
cũng có thể kết hợp các UDT và giao diện được nhập vào.
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;
Sau đây được nhập khẩu:
-
android.hidl.base@1.0::IBase
(ngầm) -
android.hardware.foo@1.0::types
(ngầm) - Mọi thứ trong
android.hardware.bar@1.0
(bao gồm tất cả các giao diện vàtypes.hal
của nó) -
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 xác định trongtypes.hal
, toàn bộ tệptypes.hal
được phân tích cú pháp, nhưng các loại khác ngoà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 chứa giao diện đượ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ể liệt kê 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). 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 từ khóa extends
. Để một giao diện mở rộng giao diện khác, 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 đ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 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ợ đa kế thừa.
Các ví dụ về phiên bản nâng cấp 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 nâng cấp
Để xác định gói package@major.minor
, A hoặc tất cả B phải đúng:
Quy tắc A | "Là phiên bản phụ bắt đầu": Tất cả các phiên bản phụ trước đó, package@major.0 , package@major.1 , …, package@major.(minor-1) không được xác định. |
---|
Quy tắc B | Tất cả những điều sau đây là đúng:
|
---|
Vì quy tắc A:
- Gói có thể bắt đầu bằng bất kỳ số phiên bản nhỏ nào (ví dụ:
android.hardware.biometrics.fingerprint
bắt đầu tại@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 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
được 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 là tên gói khác, 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 siêu loại 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 thực hiện đồ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à bản cập nhật hợp lệ.
Nâng cấp giao diện
Để nâng cấp android.hardware.example@1.0
(được xác định ở 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à bản 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, nhưng 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
. (Có thể đạt được hiệu ứng tương tự khi 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 (cần phải phân biệt 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í khai báo nên việc định hướ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 đủ tiêu chuẩn, nhưng điều đó sẽ dư 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 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 giao diện con, do đó giao diện IQuux
không thể khai báo rõ ràng phương thức fromFooToBar()
.
quy ước nâng cấp
Đôi khi tên giao diện phải đổi tên giao diện mở rộng. Chúng tôi khuyên rằng các phần mở rộng, cấu trúc và liên kết enum nên có cùng tên với tên mà chúng mở rộng trừ khi chúng đủ khác biệt để đảm bảo có 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ì phương thức đó được ưu tiên. Nếu không, nó phải được đặt tên tương tự như tên 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.
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ó không thể thay đổi được (bộ giao diện và UDT của nó không thể thay đổi). Các gói có thể liên quan với nhau theo nhiều cách, tất cả đều có thể biểu diễn được thông qua sự kết hợp giữa kế thừa cấp độ giao diện và xây dựng UDT theo thành phần.
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: Kế thừa tương thích ngược ở cấp độ gói . Trong trường hợp này, gói cha là gói được kế thừa từ đó và gói con là gói mở rộng gói 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 cha đều đượ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 có hạn chế nào về mối quan hệ với các giao diện khác trong các gói khác).
- Các loại dữ liệu mới cũng có thể được thêm vào để sử dụng theo các phương pháp mới của giao diện hiện có được nâng cấp hoặc bằng các giao diện mới.
Các quy tắc này có thể được triển khai bằng cách sử dụng tính kế thừa cấp giao diện HIDL và thành phần UDT, nhưng yêu cầu kiến thức ở cấp độ siêu dữ liệ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
sẽ thực thi các quy tắc tương thích ngược.