Interfaces

Every interface defined in a HIDL package has its own autogenerated C++ class inside its package's namespace. Clients and servers deal with interfaces in different ways:

  • Servers implement interfaces.
  • Clients call methods on interfaces.

Interfaces can either be registered by name by the server or passed as parameters to HIDL-defined methods. For example, framework code may serve an interface to receive asynchronous messages from the HAL and pass that interface directly to the HAL without registering it.

Server implementation

A server implementing the IFoo interface must include the IFoo header file that was autogenerated:

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

The header is automatically exported by the shared library of the IFoo interface to link against. Example IFoo.hal:

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

Example skeleton for a server implementation of the IFoo interface:

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

To make the implementation of a server interface available to a client, you can:

  1. Register the interface implementation with the hwservicemanager (see details below),

    OR

  2. Pass the interface implementation as an argument of an interface method (for detals, see Asynchronous callbacks).

When registering the interface implementation, the hwservicemanager process keeps track of registered HIDL interfaces running on the device by name and version. Servers can register a HIDL interface implementation by name and clients can request service implementations by name and version. This process serves the HIDL interface android.hidl.manager@1.0::IServiceManager.

Each auto-generated HIDL interface header file (such as IFoo.h) has a registerAsService() method that can be used to register the interface implementation with the hwservicemanager. The only required argument is the name of the interface implementations as clients will use this name to retrieve the interface from the hwservicemanager later:

::android::sp<IFoo> myFoo = new FooImpl();
::android::sp<IFoo> mySecondFoo = new FooAnotherImpl();
status_t status = myFoo->registerAsService();
status_t anotherStatus = mySecondFoo->registerAsService("another_foo");

The hwservicemanager treats the combination of [package@version::interface, instance_name] as unique to enable different interfaces (or different versions of the same interface) to register with identical instance names without conflicts. If you call registerAsService() with the exact same package version, interface, and instance name, the hwservicemanager drops its reference to the previously registered service and uses the new one.

Client implementation

Just as the server does, a client must #include every interface it refers to:

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

A client can obtain an interface in two ways:

  • Through I<InterfaceName>::getService (via the hwservicemanager)
  • Through an interface method

Each autogenerated interface header file has a static getService method that can be used to retrieve a service instance from the hwservicemanager:

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

Now the client has an an IFoo interface, and can call methods to it as if it were a local class implementation. In reality, the implementation may run in the same process, a different process, or even on another device (with HAL remoting). Because the client called getService on an IFoo object included from version 1.0 of the package, the hwservicemanager returns a server implementation only if that implementation is compatible with 1.0 clients. In practice, this means only server implementations with version 1.n (version x.(y+1) of an interface must extend (inherit from) x.y).

Additionally the method castFrom is provided to cast between different interfaces. This method works by making an IPC call to the remote interface to make sure the underlying type is the same as the type that is being requested. If the requested type is unavailable, then nullptr is returned.

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

Asynchronous callbacks

Many existing HAL implementations talk to asynchronous hardware, which means they need an asynchronous way to notify clients of new events that have occurred. A HIDL interface can be used as an asynchronous callback because HIDL interface functions can take HIDL interface objects as parameters.

Example interface file IFooCallback.hal:

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

Example new method in IFoo that takes an IFooCallback parameter:

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

The client using the IFoo interface is the server of the IFooCallback interface; it provides an implementation of 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
    }
};

It can also simply pass that over an existing instance of the IFoo interface:

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

The server implementing IFoo receives this as an sp<IFooCallback> object. It can store the callback, and call back into the client whenever it wants to use this interface.

Death recipients

As service implementations can run in a different process, it can happen that the process implementing an interface dies while the client stays alive. Any calls on an interface object hosted in a process that has died will fail with a transport error (isOK() will return false). The only way to recover from such a failure is to request a new instance of the service by calling I<InterfaceName>::getService(). This works only if the process that crashed has restarted and re-registered its services with the servicemanager (which is generally true for HAL implementations).

Instead of dealing with this reactively, clients of an interface can also register a death recipient to get a notification when a service dies. To register for such notifications on a retrieved IFoo interface, a client can do the following:

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

The recipient parameter must be an implementation of the android::hardware::hidl_death_recipient interface provided by HIDL, which contains a single method serviceDied() that will be called from a thread in the RPC threadpool when the process hosting the interface dies:

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

The cookie parameter contains the cookie that was passed in with linkToDeath(), whereas the who parameter contains a weak pointer to the object representing the service in the client. With the sample call given above, cookie equals 1481, and who equals foo.

It's also possible to unregister a death recipient after registering it:

foo->unlinkToDeath(recipient);