Phiên bản giao diện

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 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.
  • 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.imgsystem.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 Barandroid.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 Barandroid.hardware.example@1.0::IQuux.Foo.Bar.

Trong cả hai trường hợp, bạn chỉ có thể gọi BarBar 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ạng android.hardware.bar@1.0::S và có trong bar/1.0/types.hal (vì types.hal được nhập tự động).
  • IFooCallback được nội suy dưới dạng android.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ành android.hardware.foo@1.0::IFooCallback (được nhập thông qua import 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 trong android.hardware.baz@1.0)
  • IQux.haltypes.hal từ android.hardware.qux@1.0
  • Quuz từ android.hardware.quuz@1.0 (giả sử Quuz được xác định trong types.hal, toàn bộ tệp types.hal sẽ được phân tích cú pháp, nhưng các loại khác ngoài Quuz 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).
HOẶC
Quy tắc B

Tất cả các điều kiện sau đều đúng:

  1. "Phiên bản phụ trước đó là hợp lệ": package@major.(minor-1) phải được xác định và tuân theo cùng một quy tắc A (không có quy tắc nào từ package@major.0 đến package@major.(minor-2) được xác định) hoặc quy tắc B (nếu đó là phiên bản nâng cấp từ @major.(minor-2));



  2. "Kế thừa ít nhất một giao diện có cùng tên": Có một giao diện package@major.minor::IFoo mở rộng package@major.(minor-1)::IFoo (nếu gói trước có giao diện);



  3. "Không có giao diện kế thừa có tên khác": Không được có package@major.minor::IBar mở rộng package@major.(minor-1)::IBaz, trong đó IBarIBaz là hai tên khác nhau. Nếu có một giao diện có cùng tên, package@major.minor::IBar phải mở rộng package@major.(minor-k)::IBar sao cho không có IBar nào tồn tại với k nhỏ hơn.

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ục hardware/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@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:

  1. 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.
  2. 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).
  3. 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.