Giao diện và gói

HIDL được xây dựng dựa trên các giao diện, một loại trừu tượng được dùng trong các ngôn ngữ hướng đối tượng để xác định hành vi. Mỗi giao diện là một phần của một gói.

Gói

Tên gói có thể có các cấp phụ như package.subpackage. Thư mục gốc cho các gói HIDL đã xuất bản là hardware/interfaces hoặc vendor/vendorName (ví dụ: vendor/google cho các thiết bị Pixel). Tên gói tạo thành một hoặc nhiều thư mục con trong thư mục gốc; tất cả các tệp xác định một gói đều nằm trong cùng một thư mục. Ví dụ: bạn có thể tìm thấy package android.hardware.example.extension.light@2.0 trong hardware/interfaces/example/extension/light/2.0.

Bảng sau đây liệt kê các tiền tố và vị trí gói:

Tiền tố gói Vị trí Loại giao diện
android.hardware.* hardware/interfaces/* HAL
android.frameworks.* frameworks/hardware/interfaces/* frameworks/ related
android.system.* system/hardware/interfaces/* hệ thống/ có liên quan
android.hidl.* system/libhidl/transport/* core

Thư mục gói chứa các tệp có đuôi .hal. Mọi tệp phải chứa một câu lệnh package đặt tên cho gói và phiên bản mà tệp đó thuộc về. Tệp types.hal (nếu có) không xác định giao diện mà xác định các loại dữ liệu mà mọi giao diện trong gói đều có thể truy cập.

Định nghĩa giao diện

Ngoài types.hal, mọi tệp .hal khác đều xác định một giao diện. Giao diện thường được xác định như sau:

interface IBar extends IFoo { // IFoo is another interface
    // embedded types
    struct MyStruct {/*...*/};

    // interface methods
    create(int32_t id) generates (MyStruct s);
    close();
};

Một giao diện không có phần khai báo extends rõ ràng sẽ mở rộng ngầm ẩn từ android.hidl.base@1.0::IBase (tương tự như java.lang.Object trong Java). Giao diện IBase, được nhập ngầm, khai báo một số phương thức được đặt trước mà không được và không thể khai báo lại trong các giao diện do người dùng xác định hoặc sử dụng theo cách khác. Các phương thức này bao gồm:

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

Quy trình nhập

Câu lệnh import là cơ chế HIDL để truy cập vào các giao diện và loại gói trong một gói khác. Câu lệnh import liên quan đến hai thực thể:

  • Thực thể nhập, có thể là một gói hoặc một giao diện
  • Thực thể nhập, có thể là một gói hoặc một giao diện

Thực thể nhập được xác định theo vị trí của câu lệnh import. Khi câu lệnh nằm bên trong types.hal của gói, toàn bộ gói sẽ thấy nội dung đang được nhập; đây là lệnh nhập cấp gói. Khi câu lệnh nằm bên trong tệp giao diện, thực thể nhập là chính giao diện đó; đây là một lệnh nhập cấp giao diện.

Thực thể được nhập được xác định bằng giá trị sau từ khoá import. Giá trị này không cần phải là tên đủ điều kiện; nếu một thành phần bị bỏ qua, thành phần đó sẽ tự động được điền thông tin từ gói hiện tại. Đối với các giá trị đủ điều kiện, các trường hợp nhập sau đây được hỗ trợ:

  • Nhập toàn bộ gói. Nếu giá trị là tên gói và phiên bản (cú pháp được mô tả bên dưới), thì toàn bộ gói sẽ được nhập vào thực thể nhập.
  • Nhập một phần. Nếu giá trị là:
    • Một giao diện, types.hal của gói và giao diện đó được nhập vào thực thể nhập.
    • Một UDT được xác định trong types.hal, sau đó chỉ UDT đó được nhập vào thực thể nhập (các loại khác trong types.hal sẽ không được nhập).
  • Chỉ nhập loại. Nếu giá trị sử dụng cú pháp của một lệnh nhập một phần được mô tả ở trên, nhưng với từ khoá types thay vì tên Giao diện, thì chỉ các UDT trong types.hal của gói được chỉ định mới được nhập.

Thực thể nhập có quyền truy cập vào tổ hợp:

  • Các UDT phổ biến của gói đã nhập được xác định trong types.hal;
  • Giao diện của gói đã nhập (đối với quá trình nhập toàn bộ gói) hoặc giao diện được chỉ định (đối với quá trình nhập một phần) cho mục đích gọi các giao diện đó, truyền tay cầm đến các giao diện đó và/hoặc kế thừa từ các giao diện đó.

Câu lệnh nhập sử dụng cú pháp tên-loại đủ điều kiện để cung cấp tên và phiên bản của gói hoặc giao diện đang được nhập:

import android.hardware.nfc@1.0;            // import a whole package
import android.hardware.example@1.0::IQuux; // import an interface and types.hal
import android.hardware.example@1.0::types; // import just types.hal

Tính kế thừa giao diện

Giao diện có thể là phần mở rộng của giao diện đã xác định trước đó. Tiện ích có thể là một trong ba loại sau:

  • Giao diện có thể thêm chức năng vào một giao diện khác, kết hợp API của giao diện đó mà không thay đổi.
  • Gói có thể thêm chức năng vào một gói khác, tích hợp API của gói đó mà không thay đổi.
  • Giao diện có thể nhập các loại từ một gói hoặc từ một giao diện cụ thể.

Một giao diện chỉ có thể mở rộng một giao diện khác (không có nhiều tính năng kế thừa). Mỗi giao diện trong một gói có số phiên bản phụ khác 0 phải mở rộng một giao diện trong phiên bản trước của gói. Ví dụ: nếu giao diện IBar trong phiên bản 4.0 của gói derivative dựa trên (mở rộng) giao diện IFoo trong phiên bản 1.2 của gói original và phiên bản 1.3 của gói original được tạo, thì IBar phiên bản 4.1 không thể mở rộng phiên bản 1.3 của IFoo. Thay vào đó, IBar phiên bản 4.1 phải mở rộng IBar phiên bản 4.0, được liên kết với IFoo phiên bản 1.2. IBar phiên bản 5.0 có thể mở rộng IFoo phiên bản 1.3, nếu bạn muốn.

Tiện ích giao diện không ngụ ý phần phụ thuộc thư viện hoặc đưa vào nhiều HAL trong mã được tạo – chúng chỉ nhập cấu trúc dữ liệu và định nghĩa phương thức ở cấp HIDL. Mọi phương thức trong HAL đều phải được triển khai trong HAL đó.

Tiện ích của nhà cung cấp

Trong một số trường hợp, tiện ích của nhà cung cấp được triển khai dưới dạng một lớp con của đối tượng cơ sở đại diện cho giao diện cốt lõi mà các tiện ích đó mở rộng. Cùng một đối tượng được đăng ký theo tên và phiên bản HAL cơ sở, cũng như theo tên và phiên bản HAL (nhà cung cấp) của tiện ích.

Lập phiên bản

Các gói được tạo phiên bản và giao diện có phiên bản của gói. Phiên bản được biểu thị bằng hai số nguyên, chính.phụ.

  • Các phiên bản chính không có khả năng tương thích ngược. Việc tăng số phiên bản chính sẽ đặt lại số phiên bản phụ về 0.
  • Các phiên bản nhỏ có khả năng tương thích ngược. Việc tăng số phiên bản phụ cho biết phiên bản mới hơn hoàn toàn tương thích ngược với phiên bản trước. Bạn có thể thêm các cấu trúc và phương thức dữ liệu mới, nhưng không được thay đổi cấu trúc dữ liệu hoặc chữ ký phương thức hiện có.

Nhiều phiên bản chính hoặc phụ của HAL có thể xuất hiện đồng thời trên một thiết bị. Tuy nhiên, bạn nên ưu tiên phiên bản nhỏ hơn phiên bản lớn vì mã ứng dụng hoạt động với giao diện phiên bản nhỏ trước đó cũng hoạt động với các phiên bản nhỏ sau này của cùng giao diện đó. Để biết thêm thông tin chi tiết về việc tạo phiên bản và tiện ích của nhà cung cấp, hãy xem phần Tạo phiên bản HIDL.

Tóm tắt bố cục giao diện

Phần này tóm tắt cách quản lý gói giao diện HIDL (chẳng hạn như hardware/interfaces) và hợp nhất thông tin được trình bày trong toàn bộ phần HIDL. Trước khi đọc, hãy đảm bảo bạn đã nắm rõ các khái niệm về phiên bản HIDL, hàm băm bằng hidl-gen, thông tin chi tiết về cách làm việc chung với HIDL và các định nghĩa sau:

Thuật ngữ Định nghĩa
Giao diện nhị phân của ứng dụng (ABI) Giao diện lập trình ứng dụng cùng với mọi đường liên kết nhị phân bắt buộc.
tên đủ điều kiện (fqName) Tên để phân biệt loại hidl. Ví dụ: android.hardware.foo@1.0::IFoo.
gói hàng Gói chứa giao diện và các loại HIDL. Ví dụ: android.hardware.foo@1.0.
thư mục gốc của gói Gói gốc chứa các giao diện HIDL. Ví dụ: giao diện HIDL android.hardware nằm trong thư mục gốc của gói android.hardware.foo@1.0.
đường dẫn gốc của gói Vị trí trong cây nguồn Android mà thư mục gốc của gói ánh xạ đến.

Để biết thêm định nghĩa, hãy xem Từ vựng HIDL.

Bạn có thể tìm thấy mọi tệp từ mối liên kết gốc của gói và tên đủ điều kiện của tệp

Gốc gói được chỉ định cho hidl-gen dưới dạng đối số -r android.hardware:hardware/interfaces. Ví dụ: nếu gói là vendor.awesome.foo@1.0::IFoohidl-gen được gửi -r vendor.awesome:some/device/independent/path/interfaces, thì tệp giao diện phải nằm trong $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal.

Trong thực tế, nhà cung cấp hoặc OEM có tên awesome nên đặt giao diện chuẩn của họ trong vendor.awesome. Sau khi chọn đường dẫn gói, bạn không được thay đổi đường dẫn này vì đường dẫn này được đưa vào ABI của giao diện.

Bản đồ ánh xạ đường dẫn gói phải là duy nhất

Ví dụ: nếu bạn có -rsome.package:$PATH_A-rsome.package:$PATH_B, thì $PATH_A phải bằng $PATH_B để có thư mục giao diện nhất quán (điều này cũng giúp phiên bản giao diện dễ dàng hơn nhiều).

Thư mục gốc của gói phải có tệp phiên bản

Nếu tạo đường dẫn gói như -r vendor.awesome:vendor/awesome/interfaces, bạn cũng nên tạo tệp $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt. Tệp này sẽ chứa hàm băm của các giao diện được tạo bằng tuỳ chọn -Lhash trong hidl-gen (vấn đề này được thảo luận rộng rãi trong phần Hàm băm bằng hidl-gen).

Giao diện nằm ở các vị trí độc lập với thiết bị

Trong thực tế, bạn nên chia sẻ giao diện giữa các nhánh. Điều này cho phép sử dụng lại mã tối đa và kiểm thử mã tối đa trên nhiều thiết bị và trường hợp sử dụng.