xref: /aosp_15_r20/hardware/interfaces/neuralnetworks/utils/README.md (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
1*4d7e907cSAndroid Build Coastguard Worker# NNAPI Conversions
2*4d7e907cSAndroid Build Coastguard Worker
3*4d7e907cSAndroid Build Coastguard Worker`convert` fails if either the source type or the destination type is invalid, and it yields a valid
4*4d7e907cSAndroid Build Coastguard Workerobject if the conversion succeeds. For example, let's say that an enumeration in the current version
5*4d7e907cSAndroid Build Coastguard Workerhas fewer possible values than the "same" canonical enumeration, such as `OperationType`. The new
6*4d7e907cSAndroid Build Coastguard Workervalue of `HARD_SWISH` (introduced in Android R / NN HAL 1.3) does not map to any valid existing
7*4d7e907cSAndroid Build Coastguard Workervalue in `OperationType`, but an older value of `ADD` (introduced in Android OC-MR1 / NN HAL 1.0) is
8*4d7e907cSAndroid Build Coastguard Workervalid. This can be seen in the following model conversions:
9*4d7e907cSAndroid Build Coastguard Worker
10*4d7e907cSAndroid Build Coastguard Worker```cpp
11*4d7e907cSAndroid Build Coastguard Worker// Unsuccessful conversion
12*4d7e907cSAndroid Build Coastguard Workerconst nn::Model canonicalModel = createModelWhichHasV1_3Operations();
13*4d7e907cSAndroid Build Coastguard Workerconst nn::Result<V1_0::Model> maybeVersionedModel = V1_0::utils::convert(canonicalModel);
14*4d7e907cSAndroid Build Coastguard WorkerEXPECT_FALSE(maybeVersionedModel.has_value());
15*4d7e907cSAndroid Build Coastguard Worker```
16*4d7e907cSAndroid Build Coastguard Worker```cpp
17*4d7e907cSAndroid Build Coastguard Worker// Successful conversion
18*4d7e907cSAndroid Build Coastguard Workerconst nn::Model canonicalModel = createModelWhichHasOnlyV1_0Operations();
19*4d7e907cSAndroid Build Coastguard Workerconst nn::Result<V1_0::Model> maybeVersionedModel = V1_0::utils::convert(canonicalModel);
20*4d7e907cSAndroid Build Coastguard WorkerASSERT_TRUE(maybeVersionedModel.has_value());
21*4d7e907cSAndroid Build Coastguard Workerconst V1_0::Model& versionedModel = maybeVersionedModel.value();
22*4d7e907cSAndroid Build Coastguard WorkerEXPECT_TRUE(V1_0::utils::valid(versionedModel));
23*4d7e907cSAndroid Build Coastguard Worker```
24*4d7e907cSAndroid Build Coastguard Worker
25*4d7e907cSAndroid Build Coastguard Worker`V1_X::utils::convert` does not guarantee that all information is preserved. For example, In the
26*4d7e907cSAndroid Build Coastguard Workercase of `nn::ErrorStatus`, the new value of `MISSED_DEADLINE_TRANSIENT` can be represented by the
27*4d7e907cSAndroid Build Coastguard Workerexisting value of `V1_0::GENERAL_FAILURE`:
28*4d7e907cSAndroid Build Coastguard Worker
29*4d7e907cSAndroid Build Coastguard Worker```cpp
30*4d7e907cSAndroid Build Coastguard Worker// Lossy Canonical -> HAL -> Canonical conversion
31*4d7e907cSAndroid Build Coastguard Workerconst nn::ErrorStatus canonicalBefore = nn::ErrorStatus::MISSED_DEADLINE_TRANSIENT;
32*4d7e907cSAndroid Build Coastguard Workerconst V1_0::ErrorStatus versioned = V1_0::utils::convert(canonicalBefore).value();
33*4d7e907cSAndroid Build Coastguard Workerconst nn::ErrorStatus canonicalAfter = nn::convert(versioned).value();
34*4d7e907cSAndroid Build Coastguard WorkerEXPECT_NE(canonicalBefore, canonicalAfter);
35*4d7e907cSAndroid Build Coastguard Worker```
36*4d7e907cSAndroid Build Coastguard Worker
37*4d7e907cSAndroid Build Coastguard WorkerHowever, `nn::convert` is guaranteed to preserve all information:
38*4d7e907cSAndroid Build Coastguard Worker
39*4d7e907cSAndroid Build Coastguard Worker```cpp
40*4d7e907cSAndroid Build Coastguard Worker// Lossless HAL -> Canonical -> HAL conversion
41*4d7e907cSAndroid Build Coastguard Workerconst V1_0::ErrorStatus versionedBefore = V1_0::ErrorStatus::GENERAL_FAILURE;
42*4d7e907cSAndroid Build Coastguard Workerconst nn::ErrorStatus canonical = nn::convert(versionedBefore).value();
43*4d7e907cSAndroid Build Coastguard Workerconst V1_0::ErrorStatus versionedAfter = V1_0::utils::convert(canonical).value();
44*4d7e907cSAndroid Build Coastguard WorkerEXPECT_EQ(versionedBefore, versionedAfter);
45*4d7e907cSAndroid Build Coastguard Worker```
46*4d7e907cSAndroid Build Coastguard Worker
47*4d7e907cSAndroid Build Coastguard WorkerThe `convert` functions operate only on types that are used in a HIDL method call directly. The
48*4d7e907cSAndroid Build Coastguard Worker`unvalidatedConvert` functions operate on types that are either used in a HIDL method call directly
49*4d7e907cSAndroid Build Coastguard Worker(i.e., not as a nested class) or used in a subsequent version of the NN HAL. Prefer using `convert`
50*4d7e907cSAndroid Build Coastguard Workerover `unvalidatedConvert`.
51*4d7e907cSAndroid Build Coastguard Worker
52*4d7e907cSAndroid Build Coastguard Worker# Interface Lifetimes across Processes
53*4d7e907cSAndroid Build Coastguard Worker
54*4d7e907cSAndroid Build Coastguard Worker## HIDL
55*4d7e907cSAndroid Build Coastguard Worker
56*4d7e907cSAndroid Build Coastguard WorkerSome notes about HIDL interface objects and lifetimes across processes:
57*4d7e907cSAndroid Build Coastguard Worker
58*4d7e907cSAndroid Build Coastguard WorkerAll HIDL interface objects inherit from `IBase`, which itself inherits from `::android::RefBase`. As
59*4d7e907cSAndroid Build Coastguard Workersuch, all HIDL interface objects are reference counted and must be owned through `::android::sp` (or
60*4d7e907cSAndroid Build Coastguard Workerreferenced through `::android::wp`). Allocating `RefBase` objects on the stack will log errors and
61*4d7e907cSAndroid Build Coastguard Workermay result in crashes, and deleting a `RefBase` object through another means (e.g., "delete",
62*4d7e907cSAndroid Build Coastguard Worker"free", or RAII-cleanup through `std::unique_ptr` or some equivalent) will result in double-free
63*4d7e907cSAndroid Build Coastguard Workerand/or use-after-free undefined behavior.
64*4d7e907cSAndroid Build Coastguard Worker
65*4d7e907cSAndroid Build Coastguard WorkerHIDL/Binder manages the reference count of HIDL interface objects automatically across processes. If
66*4d7e907cSAndroid Build Coastguard Workera process that references (but did not create) the HIDL interface object dies, HIDL/Binder ensures
67*4d7e907cSAndroid Build Coastguard Workerany reference count it held is properly released. (Caveat: it might be possible that HIDL/Binder
68*4d7e907cSAndroid Build Coastguard Workerbehave strangely with `::android::wp` references.)
69*4d7e907cSAndroid Build Coastguard Worker
70*4d7e907cSAndroid Build Coastguard WorkerIf the process which created the HIDL interface object dies, any call on this object from another
71*4d7e907cSAndroid Build Coastguard Workerprocess will result in a HIDL transport error with the code `DEAD_OBJECT`.
72*4d7e907cSAndroid Build Coastguard Worker
73*4d7e907cSAndroid Build Coastguard Worker## AIDL
74*4d7e907cSAndroid Build Coastguard Worker
75*4d7e907cSAndroid Build Coastguard WorkerWe use NDK backend for AIDL interfaces. Handling of lifetimes is generally the same with the
76*4d7e907cSAndroid Build Coastguard Workerfollowing differences:
77*4d7e907cSAndroid Build Coastguard Worker* Interfaces inherit from `ndk::ICInterface`, which inherits from `ndk::SharedRefBase`. The latter
78*4d7e907cSAndroid Build Coastguard Worker  is an analog of `::android::RefBase` using `std::shared_ptr` for reference counting.
79*4d7e907cSAndroid Build Coastguard Worker* AIDL calls return `ndk::ScopedAStatus` which wraps fields of types `binder_status_t` and
80*4d7e907cSAndroid Build Coastguard Worker  `binder_exception_t`. In case the call is made on a dead object, the call will return
81*4d7e907cSAndroid Build Coastguard Worker  `ndk::ScopedAStatus` with exception `EX_TRANSACTION_FAILED` and binder status
82*4d7e907cSAndroid Build Coastguard Worker  `STATUS_DEAD_OBJECT`.
83*4d7e907cSAndroid Build Coastguard Worker
84*4d7e907cSAndroid Build Coastguard Worker# Protecting Asynchronous Calls
85*4d7e907cSAndroid Build Coastguard Worker
86*4d7e907cSAndroid Build Coastguard Worker## Across HIDL
87*4d7e907cSAndroid Build Coastguard Worker
88*4d7e907cSAndroid Build Coastguard WorkerSome notes about asynchronous calls across HIDL:
89*4d7e907cSAndroid Build Coastguard Worker
90*4d7e907cSAndroid Build Coastguard WorkerFor synchronous calls across HIDL, if an error occurs after the function was called but before it
91*4d7e907cSAndroid Build Coastguard Workerreturns, HIDL will return a transport error. For example, if the message cannot be delivered to the
92*4d7e907cSAndroid Build Coastguard Workerserver process or if the server process dies before returning a result, HIDL will return from the
93*4d7e907cSAndroid Build Coastguard Workerfunction with the appropriate transport error in the `Return<>` object, which can be queried with
94*4d7e907cSAndroid Build Coastguard Worker`Return<>::isOk()`, `Return<>::isDeadObject()`, `Return<>::description()`, etc.
95*4d7e907cSAndroid Build Coastguard Worker
96*4d7e907cSAndroid Build Coastguard WorkerHowever, HIDL offers no such error management in the case of asynchronous calls. By default, if the
97*4d7e907cSAndroid Build Coastguard Workerclient launches an asynchronous task and the server fails to return a result through the callback,
98*4d7e907cSAndroid Build Coastguard Workerthe client will be left waiting indefinitely for a result it will never receive.
99*4d7e907cSAndroid Build Coastguard Worker
100*4d7e907cSAndroid Build Coastguard WorkerIn the NNAPI, `IDevice::prepareModel*` and `IPreparedModel::execute*` (but not
101*4d7e907cSAndroid Build Coastguard Worker`IPreparedModel::executeSynchronously*`) are asynchronous calls across HIDL. Specifically, these
102*4d7e907cSAndroid Build Coastguard Workerasynchronous functions are called with a HIDL interface callback object (`IPrepareModelCallback` for
103*4d7e907cSAndroid Build Coastguard Worker`IDevice::prepareModel*` and `IExecutionCallback` for `IPreparedModel::execute*`) and are expected
104*4d7e907cSAndroid Build Coastguard Workerto quickly return, and the results are returned at a later time through these callback objects.
105*4d7e907cSAndroid Build Coastguard Worker
106*4d7e907cSAndroid Build Coastguard WorkerTo protect against the case when the server dies after the asynchronous task was called successfully
107*4d7e907cSAndroid Build Coastguard Workerbut before the results could be returned, HIDL provides an object called a "`hidl_death_recipient`,"
108*4d7e907cSAndroid Build Coastguard Workerwhich can be used to detect when an interface object (and more generally, the server process) has
109*4d7e907cSAndroid Build Coastguard Workerdied. nnapi/hal/ProtectCallback.h's `DeathHandler` uses `hidl_death_recipient`s to detect when the
110*4d7e907cSAndroid Build Coastguard Workerdriver process has died, and `DeathHandler` will unblock any thread waiting on the results of an
111*4d7e907cSAndroid Build Coastguard Worker`IProtectedCallback` callback object that may otherwise not be signaled. In order for this to work,
112*4d7e907cSAndroid Build Coastguard Workerthe `IProtectedCallback` object must have been registered via `DeathHandler::protectCallback()`.
113*4d7e907cSAndroid Build Coastguard Worker
114*4d7e907cSAndroid Build Coastguard Worker## Across AIDL
115*4d7e907cSAndroid Build Coastguard Worker
116*4d7e907cSAndroid Build Coastguard WorkerWe use NDK backend for AIDL interfaces. Handling of asynchronous calls is generally the same with
117*4d7e907cSAndroid Build Coastguard Workerthe following differences:
118*4d7e907cSAndroid Build Coastguard Worker* AIDL calls return `ndk::ScopedAStatus` which wraps fields of types `binder_status_t` and
119*4d7e907cSAndroid Build Coastguard Worker  `binder_exception_t`. In case the call is made on a dead object, the call will return
120*4d7e907cSAndroid Build Coastguard Worker  `ndk::ScopedAStatus` with exception `EX_TRANSACTION_FAILED` and binder status
121*4d7e907cSAndroid Build Coastguard Worker  `STATUS_DEAD_OBJECT`.
122*4d7e907cSAndroid Build Coastguard Worker* AIDL interface doesn't contain asynchronous `IPreparedModel::execute`.
123*4d7e907cSAndroid Build Coastguard Worker* Service death is handled using `AIBinder_DeathRecipient` object which is linked to an interface
124*4d7e907cSAndroid Build Coastguard Worker  object using `AIBinder_linkToDeath`. nnapi/hal/aidl/ProtectCallback.h provides `DeathHandler`
125*4d7e907cSAndroid Build Coastguard Worker  object that is a direct analog of HIDL `DeathHandler`, only using libbinder_ndk objects for
126*4d7e907cSAndroid Build Coastguard Worker  implementation.
127