Mỗi giao diện được xác định trong gói HIDL đều có lớp C++ được tạo tự động riêng bên trong không gian tên của gói. Máy khách và máy chủ xử lý giao diện theo nhiều cách:
- Máy chủ triển khai giao diện.
- Ứng dụng gọi các phương thức trên giao diện.
Máy chủ có thể đăng ký giao diện theo tên hoặc truyền dưới dạng tham số đến các phương thức do HIDL xác định. Ví dụ: mã khung có thể phân phát một giao diện để nhận thông báo không đồng bộ từ HAL và chuyển trực tiếp giao diện đó đến HAL mà không cần đăng ký.
Triển khai máy chủ
Máy chủ triển khai giao diện IFoo
phải bao gồm tệp tiêu đề IFoo
được tạo tự động:
#include <android/hardware/samples/1.0/IFoo.h>
Tiêu đề được thư viện chia sẻ của giao diện IFoo
tự động xuất để liên kết. Ví dụ IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
Ví dụ về khung triển khai máy chủ của giao diện IFoo:
// From the IFoo.h header using android::hardware::samples::V1_0::IFoo; class FooImpl : public IFoo { Return<void> someMethod(foo my_foo, someMethod_cb _cb) { vec<uint32_t> return_data; // Compute return_data _cb(return_data); return Void(); } ... };
Để cung cấp giao diện máy chủ cho ứng dụng, bạn có thể:
- Đăng ký việc triển khai giao diện bằng
hwservicemanager
(xem thông tin chi tiết bên dưới),
HOẶC
- Truyền phương thức triển khai giao diện dưới dạng đối số của một phương thức giao diện (để biết thông tin chi tiết, hãy xem phần Lệnh gọi lại không đồng bộ).
Khi đăng ký triển khai giao diện, quá trình hwservicemanager
sẽ theo dõi các giao diện HIDL đã đăng ký chạy trên thiết bị theo tên và phiên bản. Máy chủ có thể đăng ký triển khai giao diện HIDL theo tên và ứng dụng có thể yêu cầu triển khai dịch vụ theo tên và phiên bản. Quá trình này phân phát giao diện HIDL android.hidl.manager@1.0::IServiceManager
.
Mỗi tệp tiêu đề giao diện HIDL được tạo tự động (chẳng hạn như IFoo.h
) có một phương thức registerAsService()
có thể dùng để đăng ký triển khai giao diện bằng hwservicemanager
. Đối số bắt buộc duy nhất là tên của các hoạt động triển khai giao diện vì ứng dụng sẽ sử dụng tên này để truy xuất giao diện từ hwservicemanager
sau này:
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
hwservicemanager
coi tổ hợp [package@version::interface, instance_name]
là duy nhất để cho phép các giao diện khác nhau (hoặc các phiên bản khác nhau của cùng một giao diện) đăng ký bằng tên thực thể giống nhau mà không gây xung đột. Nếu bạn gọi registerAsService()
bằng chính phiên bản gói, giao diện và tên thực thể, thì hwservicemanager
sẽ loại bỏ tham chiếu đến dịch vụ đã đăng ký trước đó và sử dụng dịch vụ mới.
Triển khai ứng dụng
Giống như máy chủ, ứng dụng phải #include
mọi giao diện mà ứng dụng tham chiếu đến:
#include <android/hardware/samples/1.0/IFoo.h>
Ứng dụng có thể lấy giao diện theo hai cách:
- Thông qua
I<InterfaceName>::getService
(thông quahwservicemanager
) - Thông qua phương thức giao diện
Mỗi tệp tiêu đề giao diện được tạo tự động đều có một phương thức getService
tĩnh có thể dùng để truy xuất một thực thể dịch vụ từ hwservicemanager
:
// getService returns nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
Bây giờ, ứng dụng có một giao diện IFoo
và có thể gọi các phương thức cho giao diện đó như thể đó là một phương thức triển khai lớp cục bộ. Trong thực tế, quá trình triển khai có thể chạy trong cùng một quy trình, một quy trình khác hoặc thậm chí trên một thiết bị khác (với tính năng điều khiển từ xa HAL). Vì ứng dụng gọi getService
trên đối tượng IFoo
có trong phiên bản 1.0
của gói, nên hwservicemanager
chỉ trả về một phương thức triển khai máy chủ nếu phương thức triển khai đó tương thích với ứng dụng 1.0
. Trong thực tế, điều này có nghĩa là chỉ triển khai máy chủ có phiên bản 1.n
(phiên bản x.(y+1)
của giao diện phải mở rộng (kế thừa từ) x.y
).
Ngoài ra, phương thức castFrom
được cung cấp để truyền giữa các giao diện khác nhau. Phương thức này hoạt động bằng cách thực hiện lệnh gọi IPC đến giao diện từ xa để đảm bảo loại cơ bản giống với loại đang được yêu cầu. Nếu không có loại được yêu cầu, thì nullptr
sẽ được trả về.
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
Lệnh gọi lại không đồng bộ
Nhiều cách triển khai HAL hiện có giao tiếp với phần cứng không đồng bộ, nghĩa là chúng cần một cách không đồng bộ để thông báo cho ứng dụng về các sự kiện mới đã xảy ra. Bạn có thể dùng giao diện HIDL làm lệnh gọi lại không đồng bộ vì các hàm giao diện HIDL có thể nhận đối tượng giao diện HIDL làm tham số.
Tệp giao diện mẫu IFooCallback.hal
:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
Ví dụ về phương thức mới trong IFoo
có tham số IFooCallback
:
package android.hardware.samples@1.0; interface IFoo { struct Foo { int64_t someValue; handle myHandle; }; someMethod(Foo foo) generates (int32_t ret); anotherMethod() generates (vec<uint32_t>); registerCallback(IFooCallback callback); };
Máy khách sử dụng giao diện IFoo
là máy chủ của giao diện IFooCallback
; giao diện này cung cấp cách triển khai IFooCallback
:
class FooCallback : public IFooCallback { Return<void> sendEvent(uint32_t event_id) { // process the event from the HAL } Return<void> sendData(const hidl_vec<uint8_t>& data) { // process data from the HAL } };
Bạn cũng có thể chỉ cần truyền giá trị đó qua một thực thể hiện có của giao diện IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
Máy chủ triển khai IFoo
sẽ nhận được thông tin này dưới dạng đối tượng sp<IFooCallback>
. Lớp này có thể lưu trữ lệnh gọi lại và gọi lại vào ứng dụng khách bất cứ khi nào ứng dụng khách muốn sử dụng giao diện này.
Người nhận thông báo về cái chết
Vì việc triển khai dịch vụ có thể chạy trong một quy trình khác, nên có thể xảy ra trường hợp quy trình triển khai giao diện bị gián đoạn trong khi ứng dụng vẫn hoạt động.
Mọi lệnh gọi trên đối tượng giao diện được lưu trữ trong một quy trình đã ngừng hoạt động sẽ không thành công với lỗi truyền tải (isOK()
trả về false
). Cách duy nhất để khôi phục lỗi như vậy là yêu cầu một thực thể mới của dịch vụ bằng cách gọi I<InterfaceName>::getService()
. Điều này chỉ hoạt động nếu quá trình gặp sự cố đã khởi động lại và đăng ký lại các dịch vụ của quá trình đó với servicemanager
(thường đúng với các hoạt động triển khai HAL).
Thay vì xử lý vấn đề này theo phản ứng, ứng dụng của giao diện cũng có thể đăng ký một người nhận khi dịch vụ ngừng hoạt động để nhận thông báo khi một dịch vụ ngừng hoạt động.
Để đăng ký nhận các thông báo như vậy trên giao diện IFoo
đã truy xuất, ứng dụng có thể làm như sau:
foo->linkToDeath(recipient, 1481 /* cookie */);
Tham số recipient
phải là một phương thức triển khai giao diện android::hardware::hidl_death_recipient
do HIDL cung cấp, trong đó chứa một phương thức serviceDied()
duy nhất được gọi từ một luồng trong nhóm luồng RPC khi quá trình lưu trữ giao diện bị gián đoạn:
class MyDeathRecipient : public android::hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) { // Deal with the fact that the service died } }
Tham số cookie
chứa cookie được truyền vào bằng linkToDeath()
, trong khi tham số who
chứa con trỏ yếu đến đối tượng đại diện cho dịch vụ trong ứng dụng. Với lệnh gọi mẫu ở trên, cookie
bằng 1481 và who
bằng foo
.
Bạn cũng có thể huỷ đăng ký người nhận quyền truy cập sau khi đăng ký:
foo->unlinkToDeath(recipient);