HIDL

Ngôn ngữ định nghĩa giao diện HAL hoặc HIDL là ngôn ngữ mô tả giao diện (IDL) để chỉ định giao diện giữa HAL và người dùng của nó. HIDL cho phép chỉ định các loại và gọi phương thức, được thu thập vào các giao diện và gói. Nói rộng hơn, HIDL là một hệ thống giao tiếp giữa các cơ sở mã có thể được biên dịch độc lập. Kể từ Android 10, HIDL không còn được dùng nữa và Android đang chuyển sang sử dụng AIDL ở mọi nơi.

HIDL được thiết kế để sử dụng cho giao tiếp giữa các quá trình (IPC). HAL được tạo bằng HDL được gọi là HAL được liên kết hóa ở chỗ chúng có thể giao tiếp với các lớp kiến ​​trúc khác bằng cách sử dụng các cuộc gọi giao tiếp liên quá trình (IPC) liên kết. Các HAL được liên kết hóa chạy trong một quy trình riêng biệt với máy khách sử dụng chúng. Đối với các thư viện phải được liên kết với một quy trình, chế độ thông qua cũng khả dụng (không được hỗ trợ trong Java).

HIDL chỉ định cấu trúc dữ liệu và chữ ký phương thức, được tổ chức trong các giao diện (tương tự như một lớp) được thu thập thành các gói. Cú pháp của HIDL có vẻ quen thuộc với các lập trình viên C++ và Java, nhưng với một bộ từ khóa khác. HIDL cũng sử dụng các chú thích kiểu Java.

Thuật ngữ

Phần này sử dụng các thuật ngữ liên quan đến HIDL sau:

kết dính Cho biết HIDL đang được sử dụng cho các cuộc gọi thủ tục từ xa giữa các quy trình, được triển khai qua cơ chế giống như Binder. Xem thêm thông qua .
gọi lại, không đồng bộ Giao diện do người dùng HAL cung cấp, được chuyển đến HAL (sử dụng phương thức HIDL) và được HAL gọi để trả về dữ liệu bất kỳ lúc nào.
gọi lại, đồng bộ Trả về dữ liệu từ việc triển khai phương thức HIDL của máy chủ cho máy khách. Không được sử dụng cho các phương thức trả về khoảng trống hoặc một giá trị nguyên thủy duy nhất.
khách hàng Quá trình gọi các phương thức của một giao diện cụ thể. Một quy trình khung HAL hoặc Android có thể là ứng dụng khách của một giao diện và máy chủ của một giao diện khác. Xem thêm thông qua .
mở rộng Cho biết một giao diện bổ sung các phương thức và/hoặc loại vào một giao diện khác. Một giao diện chỉ có thể mở rộng một giao diện khác. Có thể được sử dụng để tăng phiên bản nhỏ trong cùng tên gói hoặc cho gói mới (ví dụ: tiện ích mở rộng của nhà cung cấp) để xây dựng trên gói cũ hơn.
tạo ra Cho biết một phương thức giao diện trả về các giá trị cho máy khách. Để trả về một giá trị không nguyên thủy hoặc nhiều hơn một giá trị, hàm gọi lại đồng bộ được tạo.
giao diện Bộ sưu tập các phương pháp và các loại. Được dịch sang một lớp bằng C++ hoặc Java. Tất cả các phương thức trong một giao diện được gọi theo cùng một hướng: quy trình máy khách gọi các phương thức được thực hiện bởi quy trình máy chủ.
một chiều Khi được áp dụng cho một phương thức HIDL, cho biết phương thức này không trả về giá trị nào và không chặn.
bưu kiện Tập hợp các giao diện và kiểu dữ liệu dùng chung một phiên bản.
thông qua Chế độ HIDL trong đó máy chủ là một thư viện dùng chung, do máy khách dlopen ed. Trong chế độ chuyển tiếp, máy khách và máy chủ là cùng một quy trình nhưng các cơ sở mã riêng biệt. Chỉ được sử dụng để đưa các cơ sở mã kế thừa vào mô hình HIDL. Xem thêm Binderized .
người phục vụ Quá trình thực hiện các phương thức của một giao diện. Xem thêm thông qua .
chuyên chở Cơ sở hạ tầng HIDL di chuyển dữ liệu giữa máy chủ và máy khách.
phiên bản Phiên bản của một gói. Bao gồm hai số nguyên, lớn và nhỏ. Các phiên bản nhỏ hơn có thể thêm (nhưng không thay đổi) các loại và phương thức.

thiết kế HIDL

Mục tiêu của HIDL là khung Android có thể được thay thế mà không phải xây dựng lại HAL. HAL sẽ được các nhà cung cấp hoặc nhà sản xuất SOC xây dựng và đặt trong phân vùng /vendor trên thiết bị, cho phép khung Android, trong phân vùng riêng của nó, được thay thế bằng OTA mà không cần biên dịch lại HAL.

Thiết kế HIDL cân bằng các mối quan tâm sau:

  • Khả năng tương tác . Tạo các giao diện có thể tương tác đáng tin cậy giữa các quy trình có thể được biên dịch với nhiều kiến ​​trúc, chuỗi công cụ và cấu hình bản dựng khác nhau. Giao diện HIDL được phiên bản và không thể thay đổi sau khi chúng được xuất bản.
  • Hiệu quả . HIDL cố gắng giảm thiểu số lượng thao tác sao chép. Dữ liệu do HIDL xác định được gửi tới mã C++ trong cấu trúc dữ liệu bố cục tiêu chuẩn C++ có thể được sử dụng mà không cần giải nén. HIDL cũng cung cấp các giao diện bộ nhớ dùng chung và vì RPC vốn đã hơi chậm nên HIDL hỗ trợ hai cách để truyền dữ liệu mà không cần sử dụng lệnh gọi RPC: bộ nhớ dùng chung và Hàng đợi tin nhắn nhanh (FMQ).
  • trực quan . HIDL tránh các vấn đề hóc búa về quyền sở hữu bộ nhớ bằng cách chỉ sử dụng in các tham số cho RPC (xem Ngôn ngữ định nghĩa giao diện Android (AIDL) ); các giá trị không thể được trả về một cách hiệu quả từ các phương thức được trả về thông qua các hàm gọi lại. Việc truyền dữ liệu vào HIDL để truyền cũng như nhận dữ liệu từ HIDL đều không làm thay đổi quyền sở hữu dữ liệu—quyền sở hữu luôn thuộc về chức năng gọi. Dữ liệu chỉ cần tồn tại trong khoảng thời gian của hàm được gọi và có thể bị hủy ngay sau khi hàm được gọi trả về.

Sử dụng chế độ thông qua

Để cập nhật các thiết bị chạy phiên bản Android cũ hơn lên Android O, bạn có thể bọc cả HAL thông thường (và cũ) trong giao diện HIDL mới phục vụ HAL ở chế độ liên kết và cùng quy trình (chuyển qua). Gói này trong suốt đối với cả HAL và khung Android.

Chế độ truyền qua chỉ khả dụng cho các ứng dụng khách và triển khai C++. Các thiết bị chạy các phiên bản Android cũ hơn không có HAL được viết bằng Java, do đó, HAL của Java vốn đã được kết dính.

Khi một tệp .hal được biên dịch, hidl-gen tạo ra một tệp tiêu đề thông qua bổ sung BsFoo.h ngoài các tiêu đề được sử dụng để liên lạc với chất kết dính; tiêu đề này xác định chức năng được dlopen ed. Vì các HAL truyền qua chạy trong cùng một quy trình mà chúng được gọi, nên trong hầu hết các trường hợp, các phương thức truyền qua được gọi bằng lệnh gọi hàm trực tiếp (cùng một luồng). oneway phương thức một chiều chạy trong luồng riêng của chúng vì chúng không có ý định đợi HAL xử lý chúng (điều này có nghĩa là bất kỳ oneway nào sử dụng các phương thức một chiều trong chế độ truyền qua phải an toàn cho luồng).

Đưa ra một IFoo.hal , BsFoo.h bao bọc các phương thức do oneway tạo ra để cung cấp các tính năng bổ sung (chẳng hạn như thực hiện các giao dịch một chiều chạy trong một chuỗi khác). Tệp này tương tự như BpFoo.h , tuy nhiên thay vì chuyển các cuộc gọi IPC bằng cách sử dụng chất kết dính, các chức năng mong muốn được gọi trực tiếp. Việc triển khai HAL trong tương lai có thể cung cấp nhiều triển khai, chẳng hạn như FooFast HAL và FooAccurate HAL. Trong những trường hợp như vậy, một tệp cho mỗi lần triển khai bổ sung sẽ được tạo (ví dụ: PTFooFast.cppPTFooAccurate.cpp ).

HAL thông qua liên kết

Bạn có thể ràng buộc các triển khai HAL hỗ trợ chế độ truyền qua. Với giao diện HAL abcd@MN::IFoo , hai gói được tạo:

  • abcd@MN::IFoo-impl . Chứa việc triển khai HAL và hiển thị hàm IFoo* HIDL_FETCH_IFoo(const char* name) . Trên các thiết bị cũ, gói này được dlopen ed và việc triển khai được khởi tạo bằng cách sử dụng HIDL_FETCH_IFoo . Bạn có thể tạo mã cơ sở bằng cách sử dụng hidl-gen-Lc++-impl-Landroidbp-impl .
  • abcd@MN::IFoo-service . Mở HAL thông qua và tự đăng ký dưới dạng dịch vụ liên kết, cho phép sử dụng cùng một triển khai HAL làm cả thông qua và liên kết.

Với loại IFoo , bạn có thể gọi sp<IFoo> IFoo::getService(string name, bool getStub) để có quyền truy cập vào một phiên bản IFoo . Nếu getStub là đúng, thì getService chỉ cố gắng mở HAL ở chế độ thông qua. Nếu getStub là sai, getService sẽ cố gắng tìm một dịch vụ được liên kết; nếu thất bại, nó sẽ cố gắng tìm dịch vụ thông qua. Tham số getStub không bao giờ được sử dụng ngoại trừ trong defaultPassthroughServiceImplementation . (Các thiết bị khởi chạy với Android O là các thiết bị được liên kết hoàn toàn, do đó, việc mở một dịch vụ ở chế độ thông qua không được phép.)

ngữ pháp HIDL

Theo thiết kế, ngôn ngữ HIDL tương tự như C (nhưng không sử dụng bộ tiền xử lý C). Tất cả dấu chấm câu không được mô tả bên dưới (ngoài việc sử dụng rõ ràng =| ) là một phần của ngữ pháp.

Lưu ý: Để biết chi tiết về kiểu mã HIDL, hãy xem Hướng dẫn về kiểu mã .

  • /** */ biểu thị nhận xét về tài liệu. Chúng chỉ có thể được áp dụng cho các khai báo kiểu, phương thức, trường và giá trị enum.
  • /* */ biểu thị chú thích nhiều dòng.
  • // chỉ ra một bình luận ở cuối dòng. Ngoài // , các dòng mới cũng giống như bất kỳ khoảng trắng nào khác.
  • Trong ví dụ ngữ pháp bên dưới, văn bản từ // đến cuối dòng không phải là một phần của ngữ pháp mà thay vào đó là một nhận xét về ngữ pháp.
  • [empty] có nghĩa là thuật ngữ có thể trống.
  • ? theo sau một nghĩa đen hoặc thuật ngữ có nghĩa là nó là tùy chọn.
  • ... biểu thị chuỗi chứa 0 hoặc nhiều mục có dấu chấm câu phân cách như đã chỉ ra. Không có đối số biến đổi trong HIDL.
  • Dấu phẩy ngăn cách các phần tử của dãy.
  • Dấu chấm phẩy chấm dứt từng phần tử, kể cả phần tử cuối cùng.
  • UPPERCASE là một ký tự không đầu cuối.
  • italics là họ mã thông báo, chẳng hạn như integer hoặc số identifier (quy tắc phân tích cú pháp C tiêu chuẩn).
  • constexpr là một biểu thức hằng số kiểu C (chẳng hạn như 1 + 11L << 3 ).
  • import_name là một gói hoặc tên giao diện, đủ điều kiện như được mô tả trong HIDL Versioning .
  • Các words thường là các mã thông báo theo nghĩa đen.

Thí dụ:

ROOT =
    PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... }  // not for types.hal
  | PACKAGE IMPORTS ITEM ITEM...  // only for types.hal; no method definitions

ITEM =
    ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
  |  safe_union identifier { UFIELD; UFIELD; ...};
  |  struct identifier { SFIELD; SFIELD; ...};  // Note - no forward declarations
  |  union identifier { UFIELD; UFIELD; ...};
  |  enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
  |  typedef TYPE identifier;

VERSION = integer.integer;

PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;

PREAMBLE = interface identifier EXTENDS

EXTENDS = <empty> | extends import_name  // must be interface, not package

GENERATES = generates (FIELD, FIELD ...)

// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
   [empty]
  |  IMPORTS import import_name;

TYPE =
  uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
 float | double | bool | string
|  identifier  // must be defined as a typedef, struct, union, enum or import
               // including those defined later in the file
|  memory
|  pointer
|  vec<TYPE>
|  bitfield<TYPE>  // TYPE is user-defined enum
|  fmq_sync<TYPE>
|  fmq_unsync<TYPE>
|  TYPE[SIZE]

FIELD =
   TYPE identifier

UFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SIZE =  // Must be greater than zero
     constexpr

ANNOTATIONS =
     [empty]
  |  ANNOTATIONS ANNOTATION

ANNOTATION =
  |  @identifier
  |  @identifier(VALUE)
  |  @identifier(ANNO_ENTRY, ANNO_ENTRY  ...)

ANNO_ENTRY =
     identifier=VALUE

VALUE =
     "any text including \" and other escapes"
  |  constexpr
  |  {VALUE, VALUE ...}  // only in annotations

ENUM_ENTRY =
     identifier
  |  identifier = constexpr