• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..--

adapter/22-Nov-2023-1,4501,088

common/22-Nov-2023-4,6962,993

service/22-Nov-2023-175117

OWNERSD22-Nov-2023222 1211

README.mdD22-Nov-20236.9 KiB127102

README.md

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