HIDL Java

Trong Android 8.0, hệ điều hành Android được thiết kế lại để xác định rõ giao diện giữa nền tảng Android độc lập với thiết bị và mã dành riêng cho thiết bị và nhà cung cấp. Android đã xác định nhiều giao diện như vậy ở dạng giao diện HAL, được xác định là tiêu đề C trong hardware/libhardware. HIDL đã thay thế các giao diện HAL này bằng các giao diện có phiên bản ổn định. Các giao diện này có thể ở dạng Java (như mô tả bên dưới) hoặc là giao diện HIDL phía máy khách và phía máy chủ trong C++.

Giao diện HIDL chủ yếu được dùng từ mã gốc, do đó, HIDL tập trung vào việc tự động tạo mã hiệu quả trong C++. Tuy nhiên, giao diện HIDL cũng phải có sẵn để sử dụng trực tiếp từ Java, vì một số hệ thống con Android (chẳng hạn như Điện thoại) có giao diện HIDL Java.

Các trang trong phần này mô tả giao diện người dùng Java cho giao diện HIDL, chi tiết cách tạo, đăng ký và sử dụng dịch vụ, đồng thời giải thích cách HAL và ứng dụng HAL được viết bằng Java tương tác với hệ thống RPC HIDL.

Ví dụ về ứng dụng

Đây là ví dụ về ứng dụng cho giao diện IFoo trong gói android.hardware.foo@1.0 được đăng ký dưới dạng tên dịch vụ default và một dịch vụ bổ sung có tên dịch vụ tuỳ chỉnh second_impl.

Thêm thư viện

Bạn cần thêm các phần phụ thuộc trên thư viện giả lập HIDL tương ứng nếu muốn sử dụng thư viện đó. Thông thường, đây là một thư viện tĩnh:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Nếu biết rằng mình đang lấy các phần phụ thuộc trên các thư viện này, bạn cũng có thể sử dụng đường liên kết dùng chung:

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Những điểm cần cân nhắc khác khi thêm thư viện trong Android 10

Nếu có một ứng dụng hệ thống hoặc ứng dụng của nhà cung cấp nhắm đến Android 10 trở lên, bạn có thể đưa các thư viện này vào một cách tĩnh. Bạn cũng có thể (chỉ) sử dụng các lớp HIDL từ các tệp JAR tuỳ chỉnh được cài đặt trên thiết bị có API Java ổn định được cung cấp bằng cách sử dụng cơ chế uses-library hiện có cho các ứng dụng hệ thống. Phương pháp sau giúp tiết kiệm dung lượng trên thiết bị. Để biết thêm thông tin chi tiết, hãy xem phần Triển khai Thư viện SDK Java. Đối với các ứng dụng cũ, hành vi cũ sẽ được giữ nguyên.

Kể từ Android 10, các phiên bản "shallow" (nhẹ) của các thư viện này cũng có sẵn. Các lớp này bao gồm lớp có liên quan nhưng không bao gồm bất kỳ lớp phụ thuộc nào. Ví dụ: android.hardware.foo-V1.0-java-shallow bao gồm các lớp trong gói foo, nhưng không bao gồm các lớp trong android.hidl.base-V1.0-java, chứa lớp cơ sở của tất cả giao diện HIDL. Nếu đang tạo một thư viện đã có các lớp cơ sở của giao diện ưu tiên dưới dạng phần phụ thuộc, bạn có thể sử dụng các phần sau:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java-shallow", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow

Thư viện cơ sở và thư viện trình quản lý HIDL cũng không còn có trên đường dẫn lớp khởi động cho ứng dụng (trước đây, các thư viện này đôi khi được dùng làm API ẩn, do trình tải lớp uỷ quyền trước của Android). Thay vào đó, các ứng dụng này đã được chuyển vào một không gian tên mới bằng jarjar và các ứng dụng sử dụng các ứng dụng này (cần thiết là các ứng dụng riêng tư) phải có bản sao riêng. Các mô-đun trên đường dẫn lớp khởi động sử dụng HIDL phải sử dụng các biến thể nông của các thư viện Java này và thêm jarjar_rules: ":framework-jarjar-rules" vào Android.bp để sử dụng phiên bản của các thư viện này có trong đường dẫn lớp khởi động.

Sửa đổi nguồn Java

Dịch vụ này chỉ có một phiên bản (@1.0), vì vậy, mã này chỉ truy xuất phiên bản đó. Hãy xem phần tiện ích giao diện để biết cách xử lý nhiều phiên bản dịch vụ.

import android.hardware.foo.V1_0.IFoo;
...
// retry to wait until the service starts up if it is in the manifest
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IFoo anotherServer = IFoo.getService("second_impl", true /* retry */);
server.doSomething(…);

Cung cấp dịch vụ

Mã khung trong Java có thể cần phân phát giao diện để nhận lệnh gọi lại không đồng bộ từ HAL.

Đối với giao diện IFooCallback trong phiên bản 1.0 của gói android.hardware.foo, bạn có thể triển khai giao diện của mình trong Java bằng các bước sau:

  1. Xác định giao diện trong HIDL.
  2. Mở /tmp/android/hardware/foo/IFooCallback.java dưới dạng tài liệu tham khảo.
  3. Tạo một mô-đun mới để triển khai Java.
  4. Kiểm tra lớp trừu tượng android.hardware.foo.V1_0.IFooCallback.Stub, sau đó viết một lớp mới để mở rộng lớp đó và triển khai các phương thức trừu tượng.

Xem tệp được tạo tự động

Để xem các tệp được tạo tự động, hãy chạy:

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

Các lệnh này tạo thư mục /tmp/android/hardware/foo/1.0. Đối với tệp hardware/interfaces/foo/1.0/IFooCallback.hal, thao tác này sẽ tạo tệp /tmp/android/hardware/foo/1.0/IFooCallback.java, đóng gói giao diện Java, mã proxy và mã giả lập (cả proxy và mã giả lập đều tuân thủ giao diện).

-Lmakefile tạo các quy tắc chạy lệnh này tại thời điểm tạo bản dựng và cho phép bạn đưa android.hardware.foo-V1.0-java vào và liên kết với các tệp thích hợp. Bạn có thể tìm thấy một tập lệnh tự động thực hiện việc này cho một dự án đầy giao diện tại hardware/interfaces/update-makefiles.sh. Các đường dẫn trong ví dụ này là tương đối; phần cứng/giao diện có thể là một thư mục tạm thời trong cây mã để cho phép bạn phát triển HAL trước khi phát hành.

Chạy dịch vụ

HAL cung cấp giao diện IFoo. Giao diện này phải thực hiện lệnh gọi lại không đồng bộ đến khung qua giao diện IFooCallback. Giao diện IFooCallback không được đăng ký theo tên dưới dạng dịch vụ có thể khám phá; thay vào đó, IFoo phải chứa một phương thức như setFooCallback(IFooCallback x).

Để thiết lập IFooCallback từ phiên bản 1.0 của gói android.hardware.foo, hãy thêm android.hardware.foo-V1.0-java vào Android.mk. Mã để chạy dịch vụ là:

import android.hardware.foo.V1_0.IFoo;
import android.hardware.foo.V1_0.IFooCallback.Stub;
....
class FooCallback extends IFooCallback.Stub {
    // implement methods
}
....
// Get the service from which you will be receiving callbacks.
// This also starts the threadpool for your callback service.
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
....
// This must be a persistent instance variable, not local,
//   to avoid premature garbage collection.
FooCallback mFooCallback = new FooCallback();
....
// Do this once to create the callback service and tell the "foo-bar" service
server.setFooCallback(mFooCallback);

Tiện ích giao diện

Giả sử một dịch vụ nhất định triển khai giao diện IFoo trên tất cả thiết bị, thì có thể trên một thiết bị cụ thể, dịch vụ đó có thể cung cấp các chức năng bổ sung được triển khai trong tiện ích giao diện IBetterFoo, như sau:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

Mã gọi nhận biết giao diện mở rộng có thể sử dụng phương thức Java castFrom() để truyền giao diện cơ sở đến giao diện mở rộng một cách an toàn:

IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IBetterFoo extendedService = IBetterFoo.castFrom(baseService);
if (extendedService != null) {
  // The service implements the extended interface.
} else {
  // The service implements only the base interface.
}