HIDL C++

Android 8 thiết kế lại hệ điều hành Android để 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ó thể là giao diện HIDL phía máy khách và phía máy chủ trong C++ (mô tả bên dưới) hoặc Java.

Các trang trong phần này mô tả cách triển khai C++ của giao diện HIDL, bao gồm thông tin chi tiết về các tệp do trình biên dịch hidl-gen tạo tự động từ các tệp .hal HIDL, cách đóng gói các tệp này và cách tích hợp các tệp này với mã C++ sử dụng các tệp đó.

Triển khai máy khách và máy chủ

Giao diện HIDL có các phương thức triển khai máy khách và máy chủ:

  • Ứng dụng của giao diện HIDL là mã sử dụng giao diện bằng cách gọi các phương thức trên giao diện đó.
  • Máy chủ là hoạt động triển khai giao diện HIDL nhận lệnh gọi từ ứng dụng và trả về kết quả (nếu cần).

Trong quá trình chuyển đổi từ HAL libhardware sang HAL HIDL, quá trình triển khai HAL sẽ trở thành máy chủ và quá trình gọi vào HAL sẽ trở thành ứng dụng. Các phương thức triển khai mặc định có thể phân phát cả HAL truyền qua và liên kết, đồng thời có thể thay đổi theo thời gian:

Hình 1. Tiến trình phát triển cho các HAL cũ.

Tạo ứng dụng HAL

Bắt đầu bằng cách đưa các thư viện HAL vào tệp makefile:

  • Tạo: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Soong: shared_libs: [ …, android.hardware.nfc@1.0 ]

Tiếp theo, hãy thêm các tệp tiêu đề HAL:

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

// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

Tạo máy chủ HAL

Để tạo quá trình triển khai HAL, bạn phải có các tệp .hal đại diện cho HAL và đã tạo tệp makefile cho HAL bằng cách sử dụng -Lmakefile hoặc -Landroidbp trên hidl-gen (./hardware/interfaces/update-makefiles.sh thực hiện việc này cho các tệp HAL nội bộ và là tài liệu tham khảo hữu ích). Khi chuyển các HAL qua libhardware, bạn có thể dễ dàng thực hiện nhiều thao tác này bằng cách sử dụng c2hal.

Cách tạo các tệp cần thiết để triển khai HAL:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

Để HAL hoạt động ở chế độ truyền thẳng, bạn phải có hàm HIDL_FETCH_IModuleName nằm trong /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so, trong đó OPTIONAL_IDENTIFIER là một chuỗi xác định việc triển khai truyền thẳng. Các yêu cầu về chế độ chuyển tiếp sẽ tự động được đáp ứng bằng các lệnh trên, các lệnh này cũng tạo mục tiêu android.hardware.nfc@1.0-impl, nhưng bạn có thể sử dụng bất kỳ tiện ích nào. Ví dụ: android.hardware.nfc@1.0-impl-foo sử dụng -foo để phân biệt chính nó.

Nếu một HAL là phiên bản phụ hoặc phần mở rộng của một HAL khác, thì bạn nên sử dụng HAL cơ sở để đặt tên cho tệp nhị phân này. Ví dụ: các hoạt động triển khai android.hardware.graphics.mapper@2.1 vẫn phải ở dạng tệp nhị phân có tên là android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER). Thông thường, OPTIONAL_IDENTIFIER ở đây sẽ bao gồm phiên bản HAL thực tế. Bằng cách đặt tên tệp nhị phân như vậy, ứng dụng 2.0 có thể truy xuất tệp nhị phân đó trực tiếp, còn ứng dụng 2.1 có thể chuyển đổi phương thức triển khai lên trên.

Tiếp theo, hãy điền chức năng vào các mã giả lập và thiết lập trình nền. Mã trình nền mẫu (hỗ trợ truyền qua):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation gọi dlopen() cho thư viện -impl được cung cấp và cung cấp thư viện đó dưới dạng dịch vụ liên kết. Mã trình nền mẫu (dành cho dịch vụ liên kết thuần tuý):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool never exceeds
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

Trình nền này thường nằm trong $PACKAGE + "-service-suffix" (ví dụ: android.hardware.nfc@1.0-service), nhưng có thể nằm ở bất kỳ đâu. sepolicy cho một lớp HAL cụ thể là thuộc tính hal_<module> (ví dụ: hal_nfc). Bạn phải áp dụng thuộc tính này cho trình nền chạy một HAL cụ thể (nếu cùng một quy trình phân phát nhiều HAL, thì bạn có thể áp dụng nhiều thuộc tính cho quy trình đó).