| # Extending CHRE with Vendor-specific Functionality |
| |
| [TOC] |
| |
| The CHRE framework is easily extensible with no modifications to the core |
| framework. Depending on the goals of the new API, one or more of the following |
| steps must be performed. At a high-level, to add a new vendor-specific API to |
| CHRE, one must: |
| |
| 1. Define new APIs in a header that can be referenced by both platform CHRE |
| framework code and vendor-specific nanoapps. |
| |
| 2. Expose the new APIs from the framework to nanoapps, and connect them to a new |
| module to provide the desired functionality |
| |
| 3. Integrate the new module with existing CHRE framework features, e.g. the |
| event subsystem, to provide complete functionality that fits within the |
| existing CHRE conventions |
| |
| It's best to refer to existing standard CHRE API feature areas, such as |
| `chre/wifi.h` and `WifiRequestManager`, and follow a similar design where |
| possible. |
| |
| ## Defining the API |
| |
| To prevent collision with future common CHRE API definitions, vendor extensions |
| must not use the plain ‘chre’ prefix followed by a capitalized letter. Instead, |
| it’s recommended to prefix the APIs with the vendor’s name as lowercase. For |
| example, if your company name is XYZ Semiconductor and you’re defining a new |
| ‘widget’ API, it’s recommended to use a naming scheme like |
| `chrexyzWidget<FunctionName>()`, and included indirectly via `#include |
| <chre_xyz.h>` or directly via `<chre_xyz/widget.h>`. The equivalent C++ |
| namespace would be `::chre::xyz`. |
| |
| There are reserved ranges for vendor/implementation-specific event types |
| (starting from `CHRE_EVENT_INTERNAL_EXTENDED_FIRST_EVENT`), and other cases |
| where vendors may wish or need to define a custom value in an existing field. To |
| prevent collision with future versions of the CHRE API, vendor extensions must |
| only use values within vendor-reserved ranges. If you would like to add a new |
| value to an existing field for a vendor extension and a vendor-reserved range |
| does not already exist, please reach out to the CHRE team for guidance - |
| solutions may involve creating a new reserved range in the common CHRE API, or |
| providing advice on a different method of defining the API. |
| |
| Vendors can only add on to the CHRE API - existing APIs must not be changed. Do |
| not modify core CHRE definitions, for example by adding on fields to common |
| structures, re-using event types, repurposing fields that are reserved for |
| future use, etc. |
| |
| It’s recommended that any vendor extensions consider compatibility when |
| designing it - see the Compatibility section for API design guidelines. |
| |
| If this API is intended to be open-sourced, it should be added to |
| `platform/<platform_name>/extensions/include`. Otherwise, it’s suggested that |
| the API be placed outside of the CHRE tree, in a separate Git project under |
| `vendor/` in the Android tree, to avoid potential conflicts when upgrading to a |
| new version of CHRE. |
| |
| ### Build Customization |
| |
| As part of the CHRE framework build system, the `CHRE_VARIANT_MK_INCLUDES` |
| environment variable can be used to inject an external `.mk` file into the |
| top-level build without any source code changes in the system/chre project. |
| Alternatively, if open sourcing, the `platform.mk` file should contain the |
| additions needed to support the new vendor API. Refer to the CHRE framework |
| build documentation for further details. |
| |
| To expose the new functionality to nanoapps, it’s recommended to create a single |
| `.mk` file that adds the necessary `COMMON_CFLAGS` entries (and potentially |
| other build configuration). For example, create a `chrexyz.mk` file which |
| nanoapps should include in their Makefile prior to including |
| `$(CHRE_PREFIX)/build/nanoapp/app.mk`. |
| |
| ## Threading Model |
| |
| Interactions with a nanoapp always happen from within the CHRE thread that runs |
| the EventLoop, so vendor extension code does not need to worry about race |
| conditions due to multiple nanoapps calling into APIs, and likewise nanoapps do |
| not need to worry about race conditions in its callbacks/handlers. However, it |
| is common for a platform module to receive data in a callback on another thread. |
| In that case, it is recommended to use `EventLoopManager::deferCallback()` to |
| pass the incoming data to the CHRE thread for processing, as opposed to using |
| mutexes or other synchronization primitives, to avoid multithreading-related |
| issues that can arise in rare conditions. Further, note that most of the core |
| CHRE functionality is only safe to call from within the CHRE thread (other than |
| posting an event, or methods that are explicitly marked as thread-safe). |
| |
| ## Initialization |
| |
| Since the new API will not be part of the core framework, it won’t be attached |
| to `EventLoopManager` or initialized as part of `chre::init()` or |
| `EventLoopManagerSingleton::get()->lateInit()`, since vendor-extension APIs are |
| by definition not part of the common code. Instead, a separate singleton object |
| should be created, for example `chre::xyz::VendorExtensionManager`, and |
| platform-specific initialization code should invoke any necessary initialization |
| **after** `chre::init` is called, but **before** loading any static nanoapps or |
| invoking `EventLoop::run()` to ensure that nanoapps don’t begin interacting with |
| the API before its state is ready. |
| |
| ## Handling Nanoapp API Calls |
| |
| Calls from a nanoapp into the CHRE framework first arrive in platform-specific |
| code (refer to the Framework Overview documentation for details). The first step |
| once an API call reaches the framework is usually to call |
| `EventLoopManager::validateChreApiCall(__func__)`. This fetches a pointer to the |
| `Nanoapp` object associated with the nanoapp that invoked the API, which will |
| fail if the API is called outside of the EventLoop thread context (see the |
| Threading Model above). From this point, the vendor extension singleton should |
| be used to invoke the appropriate functionality. |
| |
| ## Sending Events to Nanoapps |
| |
| Vendor extension APIs that need to pass data to a nanoapp asynchronously should |
| use the event susbsystem, using the vendor-reserved event type range (starting |
| at `CHRE_EVENT_INTERNAL_EXTENDED_FIRST_EVENT` and extending to |
| `CHRE_EVENT_INTERNAL_LAST_EVENT`). Event types for a given vendor extension |
| should be globally unique and stable over time. |
| |
| Synchronous API calls that can potentially block for periods greater than a few |
| milliseconds are discouraged, as these can prevent other nanoapps from |
| executing, and/or cause the pending event queue to grow excessively during |
| periods of high activity. Refer to the GNSS and WWAN APIs for design patterns |
| related to passing data to a nanoapp asynchronously, using custom event payloads |
| and/or `chreAsyncResult`. |
| |
| Events can either be unicast to a nanoapp identified by its instance ID |
| (`Nanoapp::getInstanceId()`), or broadcast to all nanoapps registered for the |
| given event type - see `Nanoapp::registerForBroadcastEvent()` and |
| `Nanoapp::unregisterForBroadcastEvent()`. |
| |
| Use `EventLoop::postEventOrDie()` or `EventLoop::postLowPriorityEventOrFree()` |
| (via `EventLoopManagerSingleton::get()->getEventLoop()`) to pass events to |
| nanoapps, depending on what error handling is desired in the case that the event |
| cannot be posted to the queue. Any memory referenced by `eventData` must not be |
| modified until `freeCallback` is invoked. |