Lập phiên bản

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 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.
  • 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.imgsystem.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 Barandroid.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 Barandroid.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 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 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ành android.hardware.foo@1.0::IFooCallback , được nhập qua import 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 trong android.hardware.baz@1.0 không được nhập)
  • 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 được phân tích cú pháp, nhưng các loại khác ngoài Quuz 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.
HOẶC
Quy tắc B

Tất cả những điều sau đây là đúng:

  1. "Phiên bản phụ trước đó 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 trong số package@major.0 đến package@major.(minor-2) được xác định) hoặc quy tắc B (nếu đó là 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": Tồn tại 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 với tên khác": Không được tồn tại 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.

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

Đô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:

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