Giao diện

Mỗi giao diện được xác định trong một 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ủ giao dịch với giao diện theo các cách khác nhau:

  • Máy chủ triển khai giao diện.
  • Phương thức gọi ứng dụng trên giao diện.

Giao diện có thể được đăng ký theo tên bởi máy chủ hoặc được chuyển dưới dạng tham số vào phương thức do HIDL xác định. Ví dụ: mã khung có thể phân phát để nhận thông báo không đồng bộ từ HAL và chuyển giao diện đó trực tiếp với HAL mà không cần đăng ký.

Triển khai máy chủ

Một 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 thư viện chia sẻ tự động xuất Giao diện IFoo để liên kết. Ví dụ IFoo.hal:

// IFoo.hal
interface IFoo {
    someMethod() generates (vec<uint32_t>);
    ...
}

Khung mẫu cho 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();
    }
    ...
};

Để triển khai giao diện máy chủ cho khách hàng, bạn có thể:

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

    HOẶC

  2. Truyền phương thức triển khai giao diện dưới dạng đối số của phương thức giao diện (để biết chi tiết, hãy xem phần Không đồng bộ lệnh gọi lại).

Khi đăng ký triển khai giao diện, Quy trình hwservicemanager 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ý giao diện HIDL phương thức triển khai theo tên và ứng dụng khách có thể yêu cầu phương thức triển khai dịch vụ theo tên và phiên bản. Quá trình này cung cấp 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ó phương thức registerAsService() có thể dùng để đăng ký triển khai giao diện bằng hwservicemanager. Chỉ đối số bắt buộc là tên của việc triển khai giao diện làm ứng dụng dùng tên này để truy xuất giao diện từ hwservicemanager sau:

::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 xử lý kết hợp của [package@version::interface, instance_name] là duy nhất để bật 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ý có tên thực thể giống hệt nhau mà không có xung đột. Nếu bạn gọi registerAsService() có cùng phiên bản gói, giao diện và tên thực thể, hwservicemanager sẽ bỏ tham chiếu đến đã đă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 điều đó đề cập đế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 qua hwservicemanager)
  • Thông qua một phương thức giao diện

Mỗi tệp tiêu đề giao diện được tạo tự động có một getService tĩnh có thể dùng để truy xuất phiên bản 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");

Ứng dụng hiện đã có giao diện IFoo và có thể gọi các phương thức đến như thể là một hoạt động 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 thiết bị khác (với HAL từ xa). Do ứng dụng khách đã gọi getService trên một đối tượng IFoo được bao gồm từ phiên bản 1.0 của gói, hwservicemanager chỉ trả về triển khai máy chủ nếu Triển khai này tương thích với ứng dụng 1.0. Trong thực tế, đây là có nghĩa là chỉ những cách triển khai máy chủ với phiên bản 1.n (phiên bản x.(y+1) của một 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 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 điều khiển từ xa để đảm bảo rằng loại cơ bản giống với loại đang được đã yêu cầu. Nếu loại đã yêu cầu không có sẵn, thì nullptr sẽ bị trả lại.

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 phương thức triển khai HAL (Lớp trừu tượng phần cứng) hiện có giao tiếp với phần cứng không đồng bộ, có nghĩa là họ 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 có đã xảy ra. Giao diện HIDL có thể được dùng làm lệnh gọi lại không đồng bộ vì HIDL hàm giao diện có thể lấy đố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 sẽ lấy giá trị 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);
};

Ứng dụng sử dụng giao diện IFooserver (máy chủ) của giao diện IFooCallback; điều này mang đến 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ể chỉ cần chuyển câu lệnh đó 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 nhận dữ liệu này dưới dạng một Đối tượng sp<IFooCallback>. Mã này có thể lưu trữ lệnh gọi lại và gọi trở 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ì quá trình triển khai dịch vụ có thể chạy trong một quy trình khác, nên nó có thể xảy ra rằng quy trình triển khai một giao diện sẽ bị vô hiệu hoá trong khi ứng dụng vẫn hoạt động. Mọi lệnh gọi trên một đối tượng giao diện được lưu trữ trong một quy trình đã bị gián đoạn sẽ không thành công có lỗi truyền tải (isOK() trả về false). Cách duy nhất để khôi phục từ lỗi như vậy là yêu cầu bản sao mới của dịch vụ bằng đang gọi I<InterfaceName>::getService(). Cách này chỉ hiệu quả nếu quy trình gặp sự cố đã khởi động lại và đăng ký lại dịch vụ của mình bằng servicemanager (thường đúng với trường hợp triển khai HAL).

Thay vì xử lý vấn đề này một cách thụ động, ứng dụng khách của một giao diện cũng có thể đăng ký người nhận đã qua đời để nhận thông báo khi một dịch vụ không còn 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, khách hàng có thể làm những việc sau:

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

Tham số recipient phải là 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() được gọi là từ một luồng trong nhóm luồng RPC khi quy 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 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 nê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 đã chết sau khi đăng ký:

foo->unlinkToDeath(recipient);