인터페이스

HIDL 패키지에 정의된 모든 인터페이스에는 패키지의 네임스페이스 내에 자체 생성된 C++ 클래스가 있습니다. 클라이언트와 서버는 인터페이스를 다양한 방식으로 처리합니다.

  • 서버는 인터페이스를 구현합니다.
  • 클라이언트는 인터페이스의 메서드를 호출합니다.

인터페이스는 서버에서 이름으로 등록하거나 HIDL 정의된 메서드에 매개변수로 전달할 수 있습니다. 예를 들어, 프레임워크 코드는 인터페이스를 제공하여 HAL에서 비동기 메시지를 수신하고 인터페이스를 등록하지 않고 바로 HAL에 전달할 수 있습니다.

서버 구현

IFoo 인터페이스를 구현하는 서버에는 다음과 같이 자동 생성된 IFoo 헤더 파일이 포함되어야 합니다.

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

연결하는 IFoo 인터페이스의 공유 라이브러리에서 헤더를 자동으로 내보냅니다. IFoo.hal 예:

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

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();
    }
    ...
};

클라이언트가 서버 인터페이스 구현을 사용할 수 있게 하려면 다음을 실행합니다.

  1. hwservicemanager를 사용하여 인터페이스 구현을 등록합니다(아래 세부정보 참조).

    또는

  2. 인터페이스 메서드의 인수로 인터페이스 구현을 전달합니다(자세한 내용은 비동기 콜백 참고).

인터페이스 구현을 등록할 때 hwservicemanager 프로세스는 기기에서 실행 중인 등록된 HIDL 인터페이스를 이름 및 버전으로 추적합니다. 서버는 이름으로 HIDL 인터페이스 구현을 등록할 수 있으며 클라이언트는 이름과 버전으로 서비스 구현을 요청할 수 있습니다. 이 프로세스는 HIDL 인터페이스 android.hidl.manager@1.0::IServiceManager를 제공합니다.

자동 생성된 각 HIDL 인터페이스 헤더 파일(예: IFoo.h)에는 hwservicemanager로 인터페이스 구현을 등록하는 데 사용할 수 있는 registerAsService() 메서드가 있습니다. 클라이언트가 나중에 인터페이스 구현의 이름을 사용하여 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[package@version::interface, instance_name]의 조합을 고유한 것으로 취급하여 다른 인터페이스 또는 동일한 인터페이스의 다른 버전이 충돌 없이 동일한 인스턴스 이름으로 등록될 수 있도록 합니다. 정확히 동일한 패키지 버전, 인터페이스, 인스턴스 이름으로 registerAsService()를 호출하면 hwservicemanager는 이전에 등록된 서비스 참조를 삭제하고 새 서비스를 사용합니다.

클라이언트 구현

서버에서 하는 것처럼 클라이언트는 참조하는 모든 인터페이스를 #include해야 합니다.

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

클라이언트는 다음 두 가지 방법으로 인터페이스를 가져올 수 있습니다.

  • hwservicemanager를 경유하는 I<InterfaceName>::getService를 통해
  • 인터페이스 메서드를 통해

자동 생성된 각 인터페이스 헤더 파일에는 hwservicemanager에서 서비스 인스턴스를 검색하는 데 사용할 수 있는 정적 getService 메서드가 있습니다.

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

이제 클라이언트에는 IFoo 인터페이스가 있으며 마치 로컬 클래스 구현인 것처럼 인터페이스 메서드를 호출할 수 있습니다. 실제로 구현은 동일한 프로세스, 다른 프로세스 또는 다른 기기(HAL 원격 사용)에서도 실행할 수 있습니다. 클라이언트가 패키지의 1.0 버전에서 포함한 IFoo 객체의 getService를 호출하였으므로 hwservicemanager는 서버 구현을 구현이 1.0 클라이언트와 호환되는 경우에만 반환합니다. 실제로 이는 1.n 버전의 서버 구현만 의미합니다. 인터페이스의 x.(y+1) 버전은 x.y에서 상속해야(또는 이를 확장해야) 합니다.

또한 castFrom 메서드는 다른 인터페이스 간에 전송하기 위해 제공됩니다. 이 메서드는 원격 인터페이스에 IPC 호출을 실행하여 기본 유형이 요청 중인 유형과 동일한지 확인합니다. 요청된 유형을 사용할 수 없는 경우 nullptr이 반환됩니다.

sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService();
sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);

비동기 콜백

기존의 많은 HAL 구현이 비동기 하드웨어와 통신하며 이는 발생한 새로운 이벤트를 클라이언트에게 알리는 비동기식 방법이 필요함을 뜻합니다. HIDL 인터페이스 함수는 HIDL 인터페이스 객체를 매개변수로 사용할 수 있으므로 HIDL 인터페이스를 비동기 콜백으로 사용할 수 있습니다.

인터페이스 파일 IFooCallback.hal의 예:

package android.hardware.samples@1.0;
interface IFooCallback {
    sendEvent(uint32_t event_id);
    sendData(vec<uint8_t> data);
}

IFooCallback 매개변수를 가져오는 IFoo의 새로운 메서드의 예는 다음과 같습니다.

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);
};

IFoo 인터페이스를 사용하는 클라이언트IFooCallback 인터페이스의 서버이며 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
    }
};

IFoo 인터페이스의 기존 인스턴스에 전달할 수도 있습니다.

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

IFoo를 구현하는 서버는 이를 sp<IFooCallback> 객체로 수신합니다. 콜백을 저장하고 이 인터페이스를 사용하려고 할 때마다 클라이언트에 콜백할 수 있습니다.

종료 수신자

서비스 구현이 다른 프로세스에서 실행될 수 있으므로 클라이언트가 살아있는 동안 인터페이스를 구현하는 프로세스가 종료될 수 있습니다. 종료된 프로세스에서 호스팅된 인터페이스 객체의 모든 호출은 전송 오류(isOK()에서 false 반환)로 실패합니다. 이러한 실패를 복구하는 유일한 방법은 I<InterfaceName>::getService()를 호출하여 서비스의 새 인스턴스를 요청하는 것입니다. 이 작업은 비정상 종료된 프로세스가 HAL 구현에 일반적으로 true인 servicemanager로 서비스를 다시 시작 및 다시 등록한 경우에만 작동합니다.

이를 사후 대응적으로 처리하는 대신 인터페이스의 클라이언트가 death recipient를 등록하여 서비스가 종료될 때 알림을 받을 수도 있습니다. 검색된 IFoo 인터페이스에서 이러한 알림을 등록하려면 클라이언트는 다음을 실행할 수 있습니다.

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

recipient 매개변수는 HIDL에서 제공하는 android::hardware::hidl_death_recipient 인터페이스의 구현이어야 하며 인터페이스를 호스팅하는 프로세스가 종료될 때 RPC 스레드풀의 스레드에서 호출되는 단일 메서드 serviceDied()가 포함됩니다.

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
    }
}

cookie 매개변수에는 linkToDeath()로 전달된 쿠키가 포함되는 반면 who 매개변수에는 클라이언트의 서비스를 나타내는 객체의 약한 포인터가 포함됩니다. 위에 주어진 샘플 호출에서 cookie는 1481, whofoo와 같습니다.

종료 수신자를 등록한 후에 등록을 취소할 수도 있습니다.

foo->unlinkToDeath(recipient);