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 can 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:
- Register the interface implementation with the
hwservicemanager
(see details below),
OR
- 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
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 thehwservicemanager
) - 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 returns 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
can 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 fails
with a transport error (isOK()
returns 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's 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);