Phóng điện cường độ cao

Ngôn ngữ định nghĩa giao diện HAL hay HIDL là một ngôn ngữ mô tả giao diện (IDL) dùng để chỉ định giao diện giữa HAL và người dùng. HIDL cho phép chỉ định các loại và lệnh gọi phương thức, được thu thập trong 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 một cách độc lập.

HIDL được dùng cho hoạt động giao tiếp liên quy trình (IPC). Các HAL được tạo bằng HDL được gọi là HAL liên kết, trong đó chúng có thể giao tiếp với các lớp kiến trúc khác bằng các lệnh gọi giao tiếp liên quy trình (IPC) liên kết. HAL được liên kết chạy trong một quy trình riêng biệt với ứng dụng 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, bạn cũng có thể sử dụng chế độ truyền qua (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 sắp xếp 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 trông quen thuộc với các lập trình viên C++ và Java, nhưng với một nhóm từ khoá khác. HIDL cũng sử dụng các chú giải kiểu Java.

Thuật ngữ

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

đã liên kết Cho biết HIDL đang được dùng cho các lệnh gọi quy trình từ xa giữa các quy trình, được triển khai qua một cơ chế giống như Binder. Xem thêm phần hướng dẫn chi tiết.
gọi lại, không đồng bộ Giao diện do người dùng HAL cung cấp, được truyền đến HAL (bằng phương thức HIDL) và do HAL gọi để trả về dữ liệu bất cứ lúc nào.
lệnh gọi lại, đồng bộ Trả về dữ liệu từ phương thức triển khai phương thức HIDL của máy chủ đến máy khách. Không dùng cho các phương thức trả về giá trị trống hoặc một giá trị gốc.
ứng dụng Quy trình gọi các phương thức của một giao diện cụ thể. 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. Hãy xem thêm phần thông qua.
mở rộng Cho biết một giao diện thêm phương thức và/hoặc kiểu 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ể dùng cho việc tăng phiên bản nhỏ trong cùng một tên gói hoặc cho một gói mới (ví dụ: tiện ích của nhà cung cấp) để xây dựng trên gói cũ.
tạo Cho biết phương thức giao diện trả về các giá trị cho ứng dụng. Để trả về một giá trị không nguyên gốc hoặc nhiều giá trị, hệ thống sẽ tạo một hàm callback đồng bộ.
giao diện Tập hợp các phương thức và kiểu. Được dịch sang một lớp trong 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: một quy trình ứng dụng sẽ gọi các phương thức do quy trình của máy chủ triển khai.
một chiều Khi được áp dụng cho một phương thức HIDL, phương thức này sẽ không trả về giá trị và không chặn.
gói hàng Tập hợp giao diện và kiểu dữ liệu dùng chung một phiên bản.
truyền qua Chế độ HIDL, trong đó máy chủ là một thư viện dùng chung, do ứng dụng dlopen sử dụng. Ở chế độ truyền qua, máy khách và máy chủ là cùng một quy trình nhưng có các cơ sở mã riêng biệt. Chỉ dùng để đưa cơ sở mã cũ vào mô hình HIDL. Hãy xem thêm phần Bị ràng buộc.
máy chủ Quy trình triển khai các phương thức của một giao diện. Hãy xem thêm phần thông qua.
giao thông Cơ sở hạ tầng HIDL di chuyển dữ liệu giữa máy chủ và ứng dụng.
version Phiên bản của một gói. Bao gồm 2 số nguyên là chính và nhỏ. Khi tăng một chút cho phiên bản, bạn có thể thêm (nhưng không thể thay đổi) loại và phương thức.

Thiết kế HIDL

Mục tiêu của HIDL là có thể thay thế khung Android mà không cần phải tạo lại HAL. HAL sẽ do nhà cung cấp hoặc nhà sản xuất SOC xây dựng và đặt vào phân vùng /vendor trên thiết bị, cho phép khung Android, trong phân vùng riêng của khung, đượ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 vấn đề sau:

  • Khả năng tương tác. Tạo các giao diện có khả năng tương tác đáng tin cậy giữa các quy trình mà có thể được biên dịch bằng nhiều cấu trúc, chuỗi công cụ và cấu hình bản dựng. Giao diện HIDL đã được tạo phiên bản và không thể thay đổi sau khi 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 phân phối đến mã C++ trong cấu trúc dữ liệu bố cục chuẩn C++ có thể sử dụng được mà không cần giải nén. HIDL cũng cung cấp giao diện bộ nhớ dùng chung và do RPC vốn có phần hơi chậm, nên HIDL hỗ trợ 2 cách chuyể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ác vấn đề phức tạp về quyền sở hữu bộ nhớ bằng cách chỉ sử dụng các tham số in cho RPC (xem Ngôn ngữ định nghĩa giao diện Android (AIDL)); các giá trị không thể trả về một cách hiệu quả từ các phương thức sẽ được trả về thông qua các hàm gọi lại. Việc truyền dữ liệu vào HIDL để chuyển hoặc nhận dữ liệu từ HIDL sẽ không làm thay đổi quyền sở hữu dữ liệu, quyền sở hữu vẫn luôn được duy trì trong hàm gọi. Dữ liệu chỉ cần tồn tại trong thời gian diễn ra hàm được gọi và có thể bị huỷ ngay sau khi hàm được gọi trở về.

Sử dụng chế độ truyền 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ể gói cả HAL thông thường (và cũ) trong giao diện HIDL mới phân phát HAL ở các chế độ liên kết và cùng quy trình (truyền qua). Gói này là trong suốt đối với cả HAL và khung Android.

Chế độ truyền qua chỉ dành cho ứng dụng và hoạt động triển khai C++. Các thiết bị chạy những phiên bản Android trước đó không có HAL được viết bằng Java, vì vậy, HAL Java vốn được liên kết với nhau.

Khi tệp .hal được biên dịch, hidl-gen sẽ tạo thêm một tệp tiêu đề truyền qua BsFoo.h ngoài các tiêu đề dùng để giao tiếp liên kết; tiêu đề này xác định các hàm cần được dlopen. Khi HAL truyền qua chạy trong cùng một quy trình mà chúng được gọi, 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). Các phương thức oneway chạy trong luồng riêng vì chúng không có mục đích chờ HAL xử lý chúng (có nghĩa là mọi HAL (Lớp trừu tượng phần cứng) sử dụng các phương thức oneway ở chế độ truyền qua đều phải an toàn cho luồng).

Với một IFoo.hal, BsFoo.h sẽ gói các phương thức do HIDL tạo để cung cấp các tính năng bổ sung (chẳng hạn như khiến giao dịch oneway chạy trong một luồng khác). Tệp này tương tự như BpFoo.h, tuy nhiên, thay vì truyền các lệnh gọi IPC bằng liên kết, các hàm mong muốn sẽ được gọi trực tiếp. Các phương thức triển khai HAL trong tương lai có thể cung cấp nhiều phương thức triển khai, chẳng hạn như FooFast HAL và FooPrecise HAL. Trong những trường hợp như vậy, hệ thống sẽ tạo một tệp cho mỗi cách triển khai bổ sung (ví dụ: PTFooFast.cppPTFooAccurate.cpp).

Cải thiện lớp trừu tượng phần cứng (HAL)

Bạn có thể liên kết các phương thức triển khai HAL (Lớp trừu tượng phần cứng) có hỗ trợ chế độ truyền qua. Với giao diện HAL a.b.c.d@M.N::IFoo, 2 gói sẽ được tạo:

  • a.b.c.d@M.N::IFoo-impl. Chứa phương thứ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 dlopened và quá trình triển khai được tạo thực thể bằ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.
  • a.b.c.d@M.N::IFoo-service. Mở HAL truyền qua và tự đăng ký làm dịch vụ liên kết, cho phép sử dụng cùng một phương thức triển khai HAL dưới dạng 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) để lấy quyền truy cập vào một thực thể của IFoo. Nếu getStub là true, thì getService sẽ chỉ cố mở HAL ở chế độ truyền qua. Nếu getStub là false, getService sẽ cố gắng tìm một dịch vụ liên kết; nếu không thành công thì dịch vụ đó sẽ cố gắng tìm dịch vụ truyền qua. Tuyệt đối không sử dụng tham số getStub, ngoại trừ trong defaultPassthroughServiceImplementation. (Các thiết bị chạy với Android O là các thiết bị liên kết hoàn toàn, vì vậy, việc mở một dịch vụ ở chế độ truyền 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 câu không được mô tả bên dưới (ngoài việc sử dụng rõ ràng =|) đều thuộc ngữ pháp.

Lưu ý: Để biết thông tin 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 trong tài liệu. Bạn chỉ có thể áp dụng các quy tắc này cho các nội dung khai báo giá trị loại, phương thức, trường và giá trị enum.
  • /* */ biểu thị ghi chú nhiều dòng.
  • // biểu thị chú thích ở cuối dòng. Ngoài //, các dòng mới cũng giống như mọi khoảng trắng khác.
  • Trong ví dụ ngữ pháp dưới đây, 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à từ khoá đó có thể trống.
  • ? đứng sau một giá trị cố định hoặc một thuật ngữ có nghĩa là giá trị này không bắt buộc.
  • ... cho biết trình tự có chứa số không hoặc nhiều mục có dấu câu riêng biệt như đã chỉ định. Không có đối số biến thiên trong HIDL.
  • Dấu phẩy phân tách các phần tử trình tự.
  • Dấu chấm phẩy chấm dứt từng phần tử, kể cả phần tử cuối cùng.
  • CHỮ HOA không phải là chữ cái đầu cuối.
  • italics là một nhóm mã thông báo, chẳng hạn như integer hoặc identifier (các 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 tên gói hoặc giao diện, đủ điều kiện như mô tả trong Phiên bản HIDL.
  • words viết thường là mã thông báo cố định.

Ví 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