HIDL C++

Android O는 Android OS를 다시 설계하여 기기 독립적 Android 플랫폼과 기기별 코드 및 공급업체별 코드 간의 명확한 인터페이스를 정의합니다. Android는 이미 hardware/libhardware의 C 헤더로 정의된 HAL 인터페이스의 형태로 많은 인터페이스를 정의합니다. HIDL은 이러한 HAL 인터페이스를 C++의 클라이언트 측 및 서버 측 HIDL 인터페이스 또는 아래 설명된 자바의 안정적인 버전 인터페이스로 대체합니다.

이 섹션의 페이지에서는 hidl-gen 컴파일러에서 HIDL .hal 파일로 자동 생성된 파일, 이러한 파일의 패키징 방법, 이러한 파일을 사용하는 C++ 코드와 파일을 통합하는 방법 등 HIDL 인터페이스의 C++ 구현을 설명합니다.

클라이언트 및 서버 구현

HIDL 인터페이스에는 클라이언트 및 서버 구현이 있습니다.

  • HIDL 인터페이스의 클라이언트는 인터페이스에서 메서드를 호출하여 인터페이스를 사용하는 코드입니다.
  • 서버는 클라이언트에서 호출을 수신하고 필요한 경우 결과를 반환하는 HIDL 인터페이스의 구현입니다.

libhardware HAL에서 HIDL HAL로 전환할 때 HAL 구현이 서버가 되고 HAL로 호출하는 프로세스가 클라이언트가 됩니다. 기본 구현은 패스스루 및 바인더화된 HAL을 모두 제공할 수 있으며 시간이 지남에 따라 변경될 수 있습니다.

그림 1. 기존 HAL의 개발 진행

HAL 클라이언트 만들기

먼저 makefile에서 HAL 라이브러리를 포함합니다.

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

다음으로 HAL 헤더 파일을 포함합니다.

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

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

HAL 서버 만들기

HAL 구현을 만들려면 HAL을 나타내며 hidl-gen에서 -Lmakefile 또는 -Landroidbp를 사용하여 이미 생성된 makefile이 있는 .hal 파일이 있어야 합니다(./hardware/interfaces/update-makefiles.sh는 내부 HAL 파일에 관해 이를 실행하며 좋은 참조임). libhardware에서 HAL을 전송할 때 c2hal을 사용하여 이 작업의 많은 부분을 쉽게 실행할 수 있습니다.

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을 패스스루 모드에서 작동하려면 함수 HIDL_FETCH_IModuleName/(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so에 있어야 하며 여기서 OPTIONAL_IDENTIFIER는 패스스루 구현을 식별하는 문자열입니다. 패스스루 모드 요구사항은 위의 명령어에 의해 자동으로 충족됩니다. 이 명령어에 따라 android.hardware.nfc@1.0-impl 타겟도 생성되지만 확장을 사용할 수 있습니다. 예를 들어 android.hardware.nfc@1.0-impl-foo-foo를 사용하여 자체를 구분합니다.

HAL이 부 버전이거나 다른 HAL의 확장인 경우 기본 HAL을 사용하여 이 바이너리의 이름을 지정해야 합니다. 예를 들어 android.hardware.graphics.mapper@2.1 구현은 여전히 android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER)이라는 바이너리에 있어야 합니다. 여기서 OPTIONAL_IDENTIFIER에는 대체로 실제 HAL 버전이 포함됩니다. 이렇게 바이너리의 이름을 지정하면 2.0 클라이언트가 직접 바이너리를 검색할 수 있으며 2.1 클라이언트가 구현을 업캐스트할 수 있습니다.

다음으로, 스터브를 기능으로 작성하고 데몬을 설정합니다. 데몬 코드의 예(패스스루 지원)는 다음과 같습니다.

#include <hidl/LegacySupport.h>

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

defaultPassthroughServiceImplementation은 제공된 -impl 라이브러리를 dlopen()하여 바인더화된 서비스로 제공합니다. 데몬 코드의 예(완전한 바인더화된 서비스)는 다음과 같습니다.

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 will never exceed
    // 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
}

이 데몬은 대개 $PACKAGE + "-service-suffix"(예: android.hardware.nfc@1.0-service)에 있지만 어느 위치에나 있을 수 있습니다. HAL의 특정 클래스에 관한 sepolicyhal_<module> 속성(예: hal_nfc))입니다. 이 속성은 특정 HAL을 실행하는 데몬에 적용되어야 합니다(동일한 프로세스가 여러 HAL을 처리하는 경우 여러 속성을 적용할 수 있음).