Giao diện

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ý các giao diện theo những cách khác nhau:

  • Máy chủ thực hiện giao diện.
  • Khách hàng gọi các phương thức trên giao diện.

Các giao diện có thể được máy chủ đăng ký theo tên hoặc được chuyển dưới dạng tham số cho các phương thức do HIDL xác định. Ví dụ: mã khung có thể phục vụ giao diện để nhận các tin nhắn không đồng bộ từ HAL và chuyển giao diện đó trực tiếp đế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>);
    ...
}

Bộ xương ví dụ về việc triển khai máy chủ 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 việc triển khai giao diện máy chủ cho máy khách, bạn có thể:

  1. Đăng ký triển khai giao diện với hwservicemanager (xem chi tiết bên dưới),

    HOẶC

  2. Chuyển việc triển khai giao diện làm đối số của một phương thức giao diện (để biết chi tiết, hãy xem Lệnh gọi lại không đồng bộ ).

Khi đăng ký triển khai giao diện, quy 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à khách hà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ục vụ 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ể được sử dụng để đăng ký triển khai giao diện với hwservicemanager . Đối số bắt buộc duy nhất là tên của việc triển khai giao diện vì sau này máy khách sẽ sử dụng tên này để truy xuất giao diện từ hwservicemanager :

::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 sự kết hợp của [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ý với các tên phiên bản giống hệt nhau mà không bị xung đột. Nếu bạn gọi registerAsService() với cùng phiên bản gói, giao diện và tên phiên bản, hwservicemanager sẽ bỏ tham chiếu của nó đến dịch vụ đã đăng ký trước đó và sử dụng dịch vụ mới.

Triển khai khách hàng

Cũng giống như máy chủ, máy khách phải #include mọi giao diện mà nó đề cập đến:

#include <android/hardware/samples/1.0/IFoo.h>

Một khách hàng có thể có được một giao diện theo hai cách:

  • Thông qua I<InterfaceName>::getService (thông qua hwservicemanager )
  • Thông qua một phương pháp giao diện

Mỗi tệp tiêu đề giao diện được tạo tự động có một phương thức getService tĩnh có thể được sử dụng để truy xuất phiên bản dịch vụ từ hwservicemanager :

// getService will return nullptr if the service can't be found
sp<IFoo> myFoo = IFoo::getService();
sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");

Bây giờ máy khách có giao diện IFoo và có thể gọi các phương thức tới nó như thể nó là một triển khai lớp cục bộ. Trong thực tế, việc 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ì máy khách được gọi là 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 triển khai máy chủ nếu việc triển khai đó tương thích với các máy khách 1.0 . Trong thực tế, điều này có nghĩa là chỉ việc triển khai máy chủ với phiên bản 1.n (phiên bản x.(y+1) của giao diện mới phải mở rộng (kế thừa từ) xy ).

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 pháp này hoạt động bằng cách thực hiện cuộc gọi IPC tới 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 loại được yêu cầu không có sẵn 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);

Cuộc gọi lại không đồng bộ

Nhiều triển khai HAL hiện tại sử dụng phần cứng không đồng bộ, có nghĩa là chúng cần một cách không đồng bộ để thông báo cho khách hàng về các sự kiện mới đã xảy ra. Giao diện HIDL có thể được sử dụng làm lệnh gọi lại không đồng bộ vì các hàm giao diện HIDL có thể lấy các đối tượng giao diện HIDL làm tham số.

File giao diện ví dụ 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 IFoomáy chủ của giao diện IFooCallback ; nó 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
    }
};

Nó cũng có thể đơn giản chuyển nó qua một phiên bản hiện có của giao diện IFoo :

sp<IFooCallback> myFooCallback = new FooCallback();
myFoo.registerCallback(myFooCallback);

Máy chủ triển khai IFoo nhận thông tin này dưới dạng đối tượng sp<IFooCallback> . Nó có thể lưu trữ cuộc gọi lại và gọi lại máy khách bất cứ khi nào nó muốn sử dụng giao diện này.

Người nhận tử vong

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 một giao diện bị chết trong khi máy khách vẫn tồn tại. Bất kỳ cuộc gọi nào trên một đối tượng giao diện được lưu trữ trong một quy trình đã chết sẽ không thành công do lỗi truyền tải ( isOK() sẽ trả về sai). Cách duy nhất để phục hồi sau lỗi như vậy là yêu cầu một phiên bản dịch vụ mới 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 dịch vụ của nó với servicemanager (điều này thường đúng với việc triển khai HAL).

Thay vì xử lý vấn đề này một cách phản ứng, các máy khách của giao diện cũng có thể đăng ký người nhận bị ngừng hoạt động để nhận thông báo khi dịch vụ ngừng hoạt động. Để đăng ký các thông báo như vậy trên giao diện IFoo được truy xuất, khách hàng có thể thực hiện như sau:

foo->linkToDeath(recipient, 1481 /* cookie */);

Tham số recipient phải là một triển khai của giao diện android::hardware::hidl_death_recipient do HIDL cung cấp, chứa một phương thức duy nhất serviceDied() sẽ được gọi từ một luồng trong luồng RPC khi quá trình lưu trữ giao diện ngừng hoạt động:

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 tới đối tượng đại diện cho dịch vụ trong máy khách. Với lệnh gọi mẫu ở trên, cookie bằng 1481 và who bằng foo .

Cũng có thể hủy đăng ký người nhận đã qua đời sau khi đăng ký:

foo->unlinkToDeath(recipient);