HIDL Java

Trong Android 8.0, hệ điều hành Android đã được kiến ​​trúc lại để xác định các giao diện rõ ràng 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ưới dạng giao diện HAL, được định nghĩa 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 ổn định, được phiên bản, có thể bằng Java (được mô tả bên dưới) hoặc là giao diện HIDL phía máy khách và máy chủ trong C++ .

Giao diện HIDL được thiết kế để sử dụng chủ yếu từ mã gốc và 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 Java HIDL.

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

Là khách hàng

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

Thêm thư viện

Bạn cần thêm các phụ thuộc vào thư viện sơ khai HIDL tương ứng nếu muốn sử dụng nó. Thông thường, đây là 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 bạn biết rằng mình đã sử dụng các phần phụ thuộc vào các thư viện này, bạn cũng có thể sử dụng liên kết chia sẻ:

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

Những cân nhắc bổ sung khi thêm thư viện trong Android 10

Nếu bạn có ứng dụng hệ thống hoặc nhà cung cấp nhắm mục tiêu Android 10 trở lên, bạn có thể bao gồm tĩnh các thư viện này. Bạn cũng có thể sử dụng (chỉ) các lớp HIDL từ các JAR tùy chỉnh được cài đặt trên thiết bị với cá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. Cách tiếp cận thứ hai giúp tiết kiệm không gian trên thiết bị. Để biết thêm chi tiết, hãy xem Triển khai Thư viện SDK Java . Đối với các ứng dụng cũ hơn, hành vi cũ vẫn được giữ nguyên.

Bắt đầu từ Android 10, các phiên bản "nông" của các thư viện này cũng có sẵn. Chúng bao gồm lớp được đề cập 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 gói android.hidl.base-V1.0-java , chứa lớp cơ sở của tất cả HIDL giao diện. Nếu bạn đang tạo một thư viện đã có sẵn các lớp cơ sở của giao diện ưa thích dưới dạng phụ thuộc, bạn có thể sử dụng như 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 trình quản lý và cơ sở HIDL cũng không còn khả dụng trên đường dẫn lớp khởi động cho ứng dụng (trước đây, đôi khi chúng được sử dụng làm API ẩn do trình nạp lớp đại biểu đầu tiên của Android). Thay vào đó, chúng đã được chuyển sang một không gian tên mới với jarjar và các ứng dụng sử dụng những ứng dụng này (nhất thiết phải là ứ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 của chúng để sử dụng phiên bản của các thư viện này tồn tại trong đường dẫn lớp khởi động.

Sửa đổi nguồn Java của bạn

Chỉ có một phiên bản ( @1.0 ) của dịch vụ này nên mã này chỉ truy xuất phiên bản đó. Xem phần mở rộng giao diện để biết cách xử lý nhiều phiên bản dịch vụ khác nhau.

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 một dịch vụ

Mã khung trong Java có thể cần phân phát các giao diện để nhận các 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 của bạn trong HIDL.
  2. Mở /tmp/android/hardware/foo/IFooCallback.java làm tài liệu tham khảo.
  3. Tạo một mô-đun mới để triển khai Java của bạn.
  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 nó và triển khai các phương thức trừu tượng.

Xem các tập tin đượ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 , điều này tạo ra tệp /tmp/android/hardware/foo/1.0/IFooCallback.java , đóng gói giao diện Java, mã proxy và sơ khai (cả proxy và sơ khai phù hợp với giao diện).

-Lmakefile tạo các quy tắc chạy lệnh này tại thời điểm xây dựng và cho phép bạn bao gồm android.hardware.foo-V1.0-java và liên kết với các tệp thích hợp. Bạn có thể tìm thấy tập lệnh tự động thực hiện việc này cho một dự án có đầ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ã của bạn để cho phép bạn phát triển HAL trước khi xuất bản nó.

Chạy một dịch vụ

HAL cung cấp giao diện IFoo , giao diện này phải thực hiện các cuộc gọi lại không đồng bộ tới 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 mở rộng 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ả cá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 khả năng bổ sung được triển khai trong tiện ích mở rộng giao diện IBetterFoo , như sau:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

Gọi mã nhận biết giao diện mở rộng có thể sử dụng phương thức Java castFrom() để chuyển giao diện cơ sở sang 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.
}