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