| Brian Duddie | 7f23a87 | 2020-07-01 14:27:31 -0700 | [diff] [blame] | 1 | # Nanoapp Developer Guide |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | Since CHRE is an open platform, anyone can write nanoapps. However, deploying to |
| 6 | a device requires cooperation of the device manufacturer, because CHRE is a |
| 7 | security-sensitive trusted environment, similar to other low-level firmware, and |
| 8 | it typically has tight resource constraints. This section assumes you have a |
| 9 | basic understanding of what a nanoapp is (if not, see the Nanoapp Overview |
| 10 | section), and provides some simple instructions to help you get started with |
| 11 | developing your own nanoapp. |
| 12 | |
| 13 | ## Getting Started |
| 14 | |
| 15 | When starting a new nanoapp, it’s helpful to start with the skeleton of an |
| 16 | existing nanoapp. The simplest example can be found at `apps/hello_world`. Start |
| 17 | by copying this folder to the location where you will develop the nanoapp - it |
| 18 | can be outside of the `system/chre` project, for example in a vendor-specific |
| 19 | Git repository in the `vendor/` folder of the Android tree. |
| 20 | |
| 21 | If you don’t plan to use this nanoapp as a *static nanoapp* (see the Nanoapp |
| 22 | Overview for details), remove the `hello_world.mk` file and delete the code |
| 23 | blocks wrapped in `#ifdef CHRE_NANOAPP_INTERNAL`. Rename the remaining files to |
| 24 | match your nanoapp. |
| 25 | |
| 26 | ### Picking a Nanoapp ID |
| 27 | |
| 28 | Nanoapps are uniquely identified by a 64-bit number. The most significant 5 |
| 29 | bytes of this number are the vendor identifier, and the remaining bytes identify |
| 30 | the nanoapp within the vendor’s namespace. The vendor identifier is usually |
| 31 | devised from an ASCII representation of the vendor’s name, for example Google |
| 32 | uses 0x476F6F676C (“Googl”). The remaining portion of the ID is typically just |
| 33 | an incrementing value for each nanoapp. |
| 34 | |
| 35 | Refer to `system/chre/chre_api/include/chre_api/chre/common.h` and |
| 36 | `util/include/chre/util/nanoapp/app_id.h` for some examples and utilities. |
| 37 | |
| 38 | Be sure to pick a unique nanoapp ID when creating a new nanoapp. |
| 39 | |
| 40 | ### Picking a Language |
| 41 | |
| karthik bharadwaj | 9503a1d | 2021-09-24 14:42:17 -0700 | [diff] [blame] | 42 | CHRE guarantees support for nanoapps written in C11 or C++17, though not all |
| Brian Duddie | 7f23a87 | 2020-07-01 14:27:31 -0700 | [diff] [blame] | 43 | standard library functions are supported (see below for details). For a |
| 44 | device-specific nanoapp, additional programming languages/versions *may* be |
| 45 | supported, but this can impact portability. |
| 46 | |
| 47 | ### Building the Nanoapp Binary |
| 48 | |
| 49 | While it’s possible to build a nanoapp with a different build system, just as it |
| 50 | is for the CHRE framework, it’s recommended to use the common build system |
| 51 | included in this project, as it makes it easy to support a variety of target |
| 52 | platforms. The rest of this section assumes you are using the CHRE build system |
| 53 | to create a non-static nanoapp. |
| 54 | |
| 55 | Update the `Makefile` in your nanoapp’s directory to: |
| 56 | |
| 57 | * Define nanoapp metadata, including: |
| 58 | * `NANOAPP_NAME`: sets the output filename of the binary |
| 59 | * `NANOAPP_ID`: 64-bit identifier, in hexadecimal format |
| 60 | * `NANOAPP_VERSION`: 32-bit version, in hexadecimal format (see versioning |
| 61 | section below) |
| 62 | * `NANOAPP_NAME_STRING`, `NANOAPP_VENDOR_STRING`: human-readable strings for |
| 63 | the name of the nanoapp and vendor, respectively |
| 64 | * `NANOAPP_IS_SYSTEM_NANOAPP`: 0 or 1 (see Nanoapp Overview) |
| 65 | * Populate `COMMON_SRCS` with the C or C++ source files to compile |
| 66 | * Populate `COMMON_CFLAGS` with compiler flags, like additional include paths |
| 67 | * Include any additional `.mk` files for vendor extensions, etc. before `app.mk` |
| 68 | |
| 69 | Refer to `build/nanoapp/app.mk` for full details. |
| 70 | |
| 71 | The nanoapp can then be built using a command like ``OPT_LEVEL=s make |
| 72 | <build_target> -j`nproc` `` (see the CHRE Framework Build System section for |
| 73 | details on build targets), which will produce build artifacts at |
| 74 | `out/<build_target>/<nanoapp_name>.*`. |
| 75 | |
| 76 | ### Loading onto a Device |
| 77 | |
| 78 | Exact steps to load a nanoapp binary can vary by device, but for developing a |
| 79 | preloaded nanoapp, this typically involves the following steps: |
| 80 | |
| 81 | * Perform any needed post-processing of the nanoapp binary to permit it to be |
| 82 | loaded (such as signing with a development or production key) |
| 83 | * Write the binary to the device’s storage (for example, using `adb push`) |
| 84 | * Update `preloaded_nanoapps.json` or other configuration as needed, so that |
| 85 | CHRE knows to load the new nanoapp |
| 86 | * Restart CHRE to reload all nanoapps, including the new one |
| 87 | |
| 88 | ## Nanoapp Versioning |
| 89 | |
| 90 | While not strictly enforced, nanoapps are recommended to follow the convention |
| 91 | of the CHRE framework and use [Semantic Versioning](http://semver.org). In the |
| 92 | case of a nanoapp, the key versioned “API” is considered the interface between |
| 93 | the client and nanoapp. Nanoapp versions are represented as a 32-bit integer, |
| 94 | where the most significant byte represents the major version, followed by one |
| 95 | byte for the minor version, and two bytes for the patch version. |
| 96 | |
| 97 | ## Using the CHRE API |
| 98 | |
| 99 | The CHRE API is the key interface between each nanoapp and the underlying |
| 100 | system. Refer to the extensive API documentation in the header files at |
| 101 | `chre_api/include`, as well as usage of the APIs by sample nanoapps. The CHRE |
| Karthik Bharadwaj | 86b1ed5 | 2023-01-12 20:45:22 +0000 | [diff] [blame] | 102 | API is normally included via ` |
| 103 | #include "chre_api/chre.h"`. |
| Brian Duddie | 7f23a87 | 2020-07-01 14:27:31 -0700 | [diff] [blame] | 104 | ## Utility Libraries |
| 105 | |
| 106 | Some source and header files under `util` are specifically designed to aid in |
| 107 | nanoapp development, and others were initially created for use in the framework |
| 108 | but can be leveraged by nanoapps as well. In general, any source and header file |
| 109 | there that does **not** include a header from `chre/platform` (part of the |
| 110 | internal CHRE framework implementation details) may be used by a nanoapp, and |
| 111 | files within a subdirectory called `nanoapp` are specifically targeted for use |
| 112 | by nanoapps. |
| 113 | |
| 114 | This includes `util/include/chre/util/nanoapp/log.h` (meant to be included via |
| 115 | `#include “chre/util/nanoapp/log.h”`), which provides macros like `LOGD` which |
| 116 | can be conditionally compiled, include a configurable prefix to help identify |
| 117 | the sender, and suppress double promotion warnings. |
| 118 | |
| 119 | The utilities library also includes a number of container classes, which are |
| 120 | meant to mimic the C++ standard library, but with a lightweight, CHRE-compatible |
| 121 | implementation. This includes: |
| 122 | |
| 123 | * `chre::DynamicVector`: an `std::vector` analogue |
| 124 | * `chre::FixedSizeVector`: accessed like `std::vector`, but only uses statically |
| 125 | allocated memory |
| 126 | * `chre::ArrayQueue`: can be used as a circular buffer |
| 127 | * `chre::UniquePtr`: an `std::unique_ptr` analogue |
| 128 | * `chre::Optional`: an analogue to `std::optional` from C++17 |
| 129 | * `chre::Singleton`: a container for a statically allocated object with explicit |
| 130 | initialization and deinitialization (e.g. enables construction of a global |
| 131 | object to be deferred until `nanoappStart()`) |
| 132 | |
| 133 | ## Interacting with the Host |
| 134 | |
| 135 | Nanoapps can interact with one or more clients on the host (applications |
| 136 | processor) through a flexible binary message-passing interface. For simple |
| 137 | interactions in cases where the lowest memory footprint is desired, using only |
| 138 | the built-in message type field with no additional payload, or passing |
| 139 | hand-rolled packed C-style structures (e.g. using Java’s ByteBuffer on the |
| 140 | client side) can work, though this approach can be error-prone. Using a |
| 141 | well-defined serialization format, such as Protocol Buffers (see the Using |
| 142 | NanoPB section below) or FlatBuffers, is usually a better choice. |
| 143 | |
| 144 | There are a few common tips to keep in mind when interacting with the host: |
| 145 | |
| 146 | 1. Nanoapp binaries are usually updated independently from client code - watch |
| 147 | out for compatibility issues arising from changes to the messaging protocol, and |
| 148 | use a serialization format like Protocol Buffers if possible. |
| 149 | |
| 150 | 2. Nanoapp messages to the host always wake it up if it’s asleep. If this is not |
| 151 | required, nanoapps are encouraged to batch their messages and opportunistically |
| 152 | send when the host wakes up for another reason (see |
| 153 | `chreConfigureHostSleepStateEvents()`). |
| 154 | |
| 155 | 3. After calling `chreSendMessageToHostEndpoint()`, ownership of the memory |
| 156 | associated with the message payload is assigned to the framework. Do not modify |
| 157 | it until the free callback is invoked. |
| 158 | |
| 159 | 4. Nanoapp messaging should be unicast, unless broadcast messaging is strictly |
| 160 | necessary. Design the messaging protocol such that the client initiates |
| 161 | communication, and save the host endpoint ID in the nanoapp to use when sending |
| 162 | replies. |
| 163 | |
| 164 | ## Interacting with Other Nanoapps |
| 165 | |
| 166 | While most nanoapps are only concerned with providing functionality for a single |
| 167 | client on the host, it is possible for a nanoapp to provide services to other |
| 168 | nanoapps within CHRE. Similar to how nanoapps communicate with the host by |
| 169 | passing *messages*, nanoapps can communicate with one another by passing |
| 170 | *events* with arbitrary binary payload. Event IDs starting in the range |
| 171 | `CHRE_EVENT_FIRST_USER_VALUE` are reserved for this purpose. |
| 172 | |
| 173 | Typically a nanoapp creates a *nanoapp client library* which other nanoapps can |
| 174 | include, which presents a simple, expressive API, and handles the implementation |
| 175 | details of passing events to the target nanoapp, and interpreting incoming |
| 176 | messages. |
| 177 | |
| 178 | Refer to the functions defined in `chre/event.h` for more details. |
| 179 | |
| 180 | ## Using TensorFlow Lite for Microcontrollers |
| 181 | |
| 182 | Many nanoapps use machine learning techniques to accomplish their functionality. |
| 183 | The CHRE build system has built-in support for integrating [TensorFlow Lite for |
| 184 | Microcontrollers](https://www.tensorflow.org/lite/microcontrollers) (TFLM) into |
| 185 | a nanoapp. Sync the TFLM sources, set `TFLM_PATH`, and define `USE_TFLM=true` in |
| 186 | your Makefile - see `apps/tflm_demo/README` for details and an example nanoapp. |
| 187 | |
| 188 | ## Using Nanopb |
| 189 | |
| 190 | The CHRE build system has integrated support for using |
| 191 | [Nanopb](https://jpa.kapsi.fi/nanopb/) to provide support for [Protocol |
| 192 | Buffers](https://developers.google.com/protocol-buffers) in a nanoapp. To |
| 193 | integrate this into your nanoapp’s Makefile, first install and configure |
| 194 | dependencies: |
| 195 | |
| 196 | * Sync the Nanopb source tree (e.g. from a release on GitHub), and define the |
| 197 | `NANOPB_PREFIX` environment variable to its path |
| 198 | * Download and install the protobuf compiler `protoc` and make it available in |
| 199 | your `$PATH`, or set the `PROTOC` environment variable |
| 200 | |
| 201 | Then in your nanoapp’s Makefile, populate `NANOPB_SRCS` with the desired |
| 202 | `.proto` file(s). That’s it! Though some additional options/parameters are |
| 203 | available - see `build/nanopb.mk` for details. |
| 204 | |
| 205 | ## Nanoapp Development Best Practices |
| 206 | |
| 207 | Even though CHRE aims to provide an environment for low-power and low-latency |
| 208 | contextual signal processing, these two are often conflicting goals. In |
| 209 | addition, CHRE is usually implemented in a resource-constrained environment with |
| 210 | limited memory available. |
| 211 | |
| 212 | As it requires collaboration from all nanoapps to optimize their resource usage |
| 213 | for CHRE to live up to its promises, some best practices are provided here as |
| 214 | guidance for nanoapp development. |
| 215 | |
| 216 | ### Memory Efficiency |
| 217 | |
| 218 | #### Avoid dynamic heap allocations where possible |
| 219 | |
| 220 | As CHRE is designed in a resource-constrained environment, there is no guarantee |
| 221 | runtime memory allocation will succeed. In addition, dynamic heap allocations |
| 222 | make it difficult to estimate the memory usage in advance. Developers are |
| 223 | therefore encouraged to use static allocations where possible. |
| 224 | |
| 225 | #### Be careful of stack usage |
| 226 | |
| 227 | Unlike Linux’s default stack of 8MB that scales dynamically, CHRE only has a |
| 228 | fixed stack of limited size (8KB is typical). Ensure you keep any allocations to |
| 229 | an absolute minimum and any large allocations should go out of scope prior to |
| 230 | navigating deeper into a stack. |
| 231 | |
| 232 | #### Prefer in-place algorithms |
| 233 | |
| 234 | Prefer in-place algorithms over out-of-place ones where efficiency allows to |
| 235 | minimize additional memory requirements. |
| 236 | |
| 237 | ### Power Efficiency |
| 238 | |
| 239 | #### Be opportunistic when possible |
| 240 | |
| 241 | Examples include: |
| 242 | |
| 243 | * If the host is asleep and doesn’t need to act on a nanoapp message |
| 244 | immediately, buffer until it wakes up for another reason. |
| 245 | * Make a WiFi on-demand scan request only if the WiFi scan monitor doesn’t |
| 246 | provide a scan result in time. |
| 247 | |
| 248 | #### Batch data at the source where possible |
| 249 | |
| 250 | By batching data at the source, it reduces the data delivery frequency and helps |
| 251 | keep CHRE asleep and improve power efficiency. Clients should make data requests |
| 252 | with the longest batch interval that still meets the latency requirement. |
| 253 | Examples include: |
| 254 | |
| 255 | * Make a sensor data request with the longest ``latency`` possible. |
| 256 | * Make an audio data request with the longest ``deliveryInterval`` possible. |
| 257 | |
| 258 | ### Standard Library Usage |
| 259 | |
| 260 | CHRE implementations are only required to support a subset of the standard C and |
| 261 | C++ libraries, as well as language features requiring run-time support. This |
| 262 | list is carefully considered to ensure memory usage and implementation |
| 263 | complexity are minimized. Following these principles, some features are |
| 264 | explicitly excluded due to their memory and/or extensive OS-level dependencies, |
| 265 | and others because they are supplanted by more suitable CHRE-specific APIs. |
| 266 | While not meant to be an exhaustive list and some platforms may differ, the |
| 267 | following standard library features are not meant to be used by nanoapps: |
| 268 | |
| 269 | * C++ exceptions and run-time type information (RTTI) |
| karthik bharadwaj | 9503a1d | 2021-09-24 14:42:17 -0700 | [diff] [blame] | 270 | * Standard library multi-threading support, including C++ library headers |
| 271 | ` <thread>`, `<mutex>`, `<atomic>`, `<future>`, etc. |
| Brian Duddie | 7f23a87 | 2020-07-01 14:27:31 -0700 | [diff] [blame] | 272 | * C and C++ Standard Input/Output libraries |
| 273 | * C++ Standard Template Library (STL) |
| 274 | * C++ Standard Regular Expressions library |
| 275 | * Dynamic memory allocation (`malloc`, `calloc`, `realloc`, `free`), and |
| 276 | libraries that inherently use dynamic allocation, such as `std::unique_ptr` |
| 277 | * Localization and Unicode character support |
| 278 | * Date and time libraries |
| 279 | * Functions that modify normal program flow, including `<setjmp.h>`, |
| 280 | `<signal.h>`, `abort`, `std::terminate`, etc. |
| 281 | * Accessing the host environment, including `system`, `getenv`, etc. |
| karthik bharadwaj | 9503a1d | 2021-09-24 14:42:17 -0700 | [diff] [blame] | 282 | * POSIX or other libraries not included in the C11 or C++17 language standards |
| Brian Duddie | 7f23a87 | 2020-07-01 14:27:31 -0700 | [diff] [blame] | 283 | |
| 284 | In many cases, equivalent functionality is available from CHRE API functions |
| 285 | and/or utility libraries. For example, `chreLog` may be used for debug logging, |
| 286 | where a more traditional program might use `printf`. |
| 287 | |
| 288 | ## Debugging |
| 289 | |
| 290 | Similar to the framework debugging methods, each has its nanoapp counterpart to |
| 291 | support nanoapp debugging through the framework. Please see the Framework |
| 292 | Debugging section for reference/context. |
| 293 | |
| 294 | ### Logging |
| 295 | |
| 296 | CHRE API `chreLog()` logs information into the system as part of the CHRE logs. |
| 297 | Normally this appears in logcat, but some platforms may route it to a different |
| 298 | logging system (a future version of the CHRE API is expected to make logcat |
| 299 | logging mandatory). |
| 300 | |
| 301 | Nanoapps are encouraged to `#include "chre/util/nanoapp/log.h"` and use the |
| 302 | `LOGx()` macros defined therein, which requires these additional steps: |
| 303 | |
| 304 | * Define `LOG_TAG` to a short, human-readable identifier for your nanoapp, as |
| 305 | this gets prepended to logs |
| 306 | * Define `NANOAPP_MINIMUM_LOG_LEVEL` to a `CHRE_LOG_LEVEL_\*` value in your |
| 307 | Makefile for compile time log level filtering - it’s recommended to use |
| 308 | `CHRE_LOG_LEVEL_DEBUG` for development, and `CHRE_LOG_LEVEL_INFO` for release |
| 309 | |
| 310 | See also the Framework Debugging section for more general guidance on logging in |
| 311 | CHRE. |
| 312 | |
| 313 | ### Debug Dump |
| 314 | |
| 315 | When running on CHRE v1.4+, nanoapps can also append information to the CHRE |
| 316 | framework debug dump. Nanoapps interested in using this capability should call |
| 317 | `chreConfigureDebugDumpEvent(true)` in `nanoappStart()`, then when |
| 318 | `CHRE_EVENT_DEBUG_DUMP` is received in `nanoappHandleEvent()`, use |
| 319 | `chreDebugDumpLog()` to write human-readable output to the debug dump, which |
| 320 | appears in bug reports under the Context Hub HAL debug section. In the reference |
| 321 | CHRE framework implementation, nanoapp debug dumps have the nanoapp name and ID |
| 322 | automatically prepended, for example: |
| 323 | |
| 324 | ``` |
| 325 | Nanoapp debug dumps: |
| 326 | |
| 327 | DebugDumpWorld 0x0123456789000011: |
| 328 | Debug event count: 2 |
| 329 | Total dwell time: 92 us |
| 330 | ``` |
| 331 | |
| 332 | Refer to the associated CHRE API documentation and Framework Debugging section |
| 333 | for more information. |
| 334 | |
| 335 | ### CHRE_ASSERT |
| 336 | |
| 337 | To help catch programming errors or other unexpected conditions, nanoapps can |
| 338 | use the `CHRE_ASSERT` macro provided by `#include "chre/util/nanoapp/assert.h"`. |
| 339 | Keep in mind that if one nanoapp encounters an assertion failure, it most likely |
| 340 | will cause a reset of the processor where CHRE is running, impacting other |
| 341 | functionality (though this can vary by platform). Therefore, assertions are only |
| 342 | recommended to be used during development. Define the `CHRE_ASSERTIONS_ENABLED` |
| 343 | variable in your Makefile to `false` to disable assertions at compile time. |