diff options
-rw-r--r-- | README.md | 328 | ||||
-rw-r--r-- | doc/compatibility.md | 218 | ||||
-rw-r--r-- | doc/contributing.md | 10 | ||||
-rw-r--r-- | doc/framework_build.md | 149 | ||||
-rw-r--r-- | doc/framework_debugging.md | 68 | ||||
-rw-r--r-- | doc/framework_overview.md | 205 | ||||
-rw-r--r-- | doc/framework_testing.md | 58 | ||||
-rw-r--r-- | doc/nanoapp_clients.md | 68 | ||||
-rw-r--r-- | doc/nanoapp_developer_guide.md | 343 | ||||
-rw-r--r-- | doc/nanoapp_overview.md | 199 | ||||
-rw-r--r-- | doc/porting_guide.md | 253 | ||||
-rw-r--r-- | doc/vendor_extensions.md | 135 | ||||
-rw-r--r-- | navbar.md | 3 |
13 files changed, 1806 insertions, 231 deletions
@@ -1,233 +1,99 @@ # Context Hub Runtime Environment (CHRE) -## Build Instructions - -Build targets are arranged in the form of a variant triple consisting of: - -``vendor_arch_variant`` - -The vendor is the provider of the CHRE implementation (ex: google, qcom). The -arch is the CPU architecture (ie: hexagonv60, x86, cm4). The variant is the -target platform (ie: slpi, nanohub, linux, googletest). - -A debug build can be obtained by appending ``_debug`` to the variant triple. As -an example: - -``make google_hexagonv62_slpi`` -``make google_hexagonv62_slpi_debug`` - -### Linux - -CHRE is compatible with Linux as a simulator. - -#### Linux Build/Run - -The simulator has system dependencies: - -- TCLAP - - Command-line argument parsing. -- libsndfile - - WAV file parsing for audio support. - -These are the commands to install these dependencies for Ubuntu: - - sudo apt-get install libtclap-dev - sudo apt-get install libsndfile1-dev - -The build target for x86 linux is ``google_x86_linux``. You can build/run the -simulator with the following command: - - ./run_sim.sh - -#### Linux Unit Tests - -You can run all unit tests with the following command. Pass arguments to this -script and they are passed to the gtest framework. (example: -``--gtest_filter=DynamicVector.*``) - - ./run_tests.sh - -### CHRE Simulator for Android - -CHRE is also compatible with Android as a simulator. - -This is not intended to be a production implementation but is suitable for -testing CHRE nanoapps on the applications processor where Android runs. It uses -Android NDK APIs to interact with the system. - -### SLPI Hexagon - -First, setup paths to the Hexagon Tools (v8.x.x), SDK (v3.0), and SLPI source -tree, for example: - - export HEXAGON_TOOLS_PREFIX=~/Qualcomm/HEXAGON_Tools/8.0 - export HEXAGON_SDK_PREFIX=~/Qualcomm/Hexagon_SDK/3.0 - export SLPI_PREFIX=~/Qualcomm/msm8998/slpi_proc - -Then use the provided Makefiles to build: - - make google_hexagonv62_slpi -j - -## Directory Structure - -The CHRE project is organized as follows: - -- ``chre_api`` - - The stable API exposed to nanoapps -- ``core`` - - Common code that applies to all CHRE platforms, most notably event - management. -- ``pal`` - - An abstraction layer that implementers must supply to access - device-specific functionality (such as GPS and Wi-Fi). The PAL is a C API - which allows it to be implemented using a vendor-supplied library. -- ``platform`` - - Contains the system interface that all plaforms must implement, along with - implementations for individual platforms. This includes the implementation - of the CHRE API. - - ``platform/shared`` - - Contains code that will apply to multiple platforms, but not - necessarily all. - - ``platform/linux`` - - This directory contains the canonical example for running CHRE on - desktop machines, primarily for simulation and testing. -- ``apps`` - - A small number of sample applications are provided. These are intended to - guide developers of new applications and help implementers test basic - functionality quickly. - - This is reference code and is not required for the CHRE to function. -- ``util`` - - Contains data structures used throughout CHRE and common utility code. -- ``variant/simulator`` - - Contains the CHRE variant for the simulator. This is a good example to - start from when porting to new devices. Variants are explained in more - detail below. - -Within each of these directories, you may find a ``tests`` subdirectory -containing tests written against the googletest framework. - -### Platform Directory Structure - -The platform directory contains an interface that common code under ``core`` -leverages to implement the runtime. All platforms are required to implement the -interface provided in ``platform/include``. - -The following gives a more detailed explanation of the directory structure. - -- ``platform`` - The top-level directory for platform-specific code. - - ``include`` - The interface that platforms are required to implement. - - ``shared`` - Code that may be shared by more than one platform but not - necessarily required for all. - - ``slpi`` - The implementation of the common interface for the SLPI and any - SLPI-specific code. - - ``linux`` - The implementation of the common interface for the simulator - running on Linux and any simulator-specific code. - -Common CHRE code that is expected to run across all platforms is located in -``core``. This code must have a stable way to access the platform-specific -implementation of the common platform API. This is handled by providing a stable -include path and changing the search path for the platform implementation. Here -is an example directory layout: - -- ``platform`` - - ``<platform_name>`` - - ``include`` - - ``chre`` - - ``target_platform`` - -The build system will add ``platform/<platform_name>/include`` to the include -search path allowing common code to find the implementation of the platform -interface. Here is an example of core code including a platform-specific header -in this way: - -``#include "chre/target_platform/log.h"`` - -When building for the linux platform, the file is included from: - -``platform/linux/include/chre/target_platform/log.h`` - -## Supplied Nanoapps - -This project includes a number of nanoapps that serve as both examples of how to -use CHRE, debugging tools and can perform some useful function. - -All nanoapps in the ``apps`` directory are placed in a namespace when built -statically with this CHRE implementation. When compiled as standalone nanoapps, -there is no outer namespace on their entry points. This allows testing various -CHRE subsystems without requiring dynamic loading and allows these nanoapps to -coexist within a CHRE binary. Refer to ``apps/hello_world/hello_world.cc`` for -a minimal example. - -### FeatureWorld - -Any of the nanoapps that end with the term World are intended to test some -feature of the system. The HelloWorld nanoapp simply exercises logging -functionality, TimerWorld exercises timers and WifiWorld uses wifi, for example. -These nanoapps log all results via chreLog which makes them effective tools when -bringing up a new CHRE implementation. - -### ImuCal - -This nanoapp implements IMU calibration. - -## Porting CHRE - -This codebase is intended to be ported to a variety of operating systems. If you -wish to port CHRE to a new OS, refer to the ``platform`` directory. An example of -the Linux port is provided under ``platform/linux``. - -There are notes regarding initialization under -``platform/include/chre/platform/init.h`` that will also be helpful. - -### Important Considerations - -Platforms are required to implement support for invoking the constructors and -destructors of global, non-POD types at load and unload time, respectively. This -is required for both the runtime and nanoapps. - -## Coding conventions - -There are many well-established coding standards within Google. The official -C++ style guide is used with the exception of Android naming conventions for -methods and variables. This means 2 space indents, camelCase method names, an -mPrefix on class members and so on. Style rules that are not specified in the -Android style guide are inherited from Google. - -## CHRE Variants - -A CHRE variant allows injecting additional source files into the build on a -per-device basis. This can be used to inject: - -* A version string - * Set to ``undefined`` if not specified -* A static nanoapp list - * Empty if left undefined -* Additional static nanoapp includes - * Vendor-specific nanoapps could be specified in the variant - -Export the ``CHRE_VARIANT_MK_INCLUDES`` containing the mk files that you wish to -be included the CHRE variant build. Refer to ``run_sim.sh`` and the -``variant/simulator`` subdirectory for an example as used by the simulator. - -* [Google C++ Style][1] - -[1]: https://google.github.io/styleguide/cppguide.html - -### Use of C++ - -This project uses C++11, but with two main caveats: - - 1. General considerations for using C++ in an embedded environment apply. This - means avoiding language features that can impose runtime overhead should - be avoided, due to the relative scarcity of memory and CPU resources, and - power considerations. Examples include RTTI, exceptions, overuse of dynamic - memory allocation, etc. Refer to existing literature on this topic - including this [Technical Report on C++ Performance][2] and so on. - 2. Support of C++ standard libraries are not generally expected to be - extensive or widespread in the embedded environments where this code will - run. That means that things like <thread> and <mutex> should not be used, - in favor of simple platform abstractions that can be implemented directly - with less effort (potentially using those libraries if they are known to be - available). - -[2]: http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf +This project contains the AOSP reference implementation of the Context Hub +Runtime Environment (CHRE), which is Android’s platform for developing always-on +applications, called *nanoapps*. CHRE runs in a vendor-specific processor that +is independent of the main applications processor that runs Android. This +enables CHRE and its nanoapps to be more power-efficient for use cases that +require frequent background processing of contextual data, like sensor inputs. +Nanoapps are written to the CHRE API, which is standardized across all +platforms, enabling them to be code-compatible across different devices. + +The CHRE reference implementation (“CHRE framework”) is designed to be portable +across hardware and software platforms. While Android does not require a +particular implementation (only compliance to the contracts given in the CHRE +API) using the reference implementation helps to reduce the work needed to +support CHRE, while also ensuring more consistent behavior for scenarios that +can be difficult to enforce via testing. + +## Navigating the docs + +Use the navigation bar at the top and/or the links below to move through the +documentation. Raw files can also be found in the `/doc` folder. + +* Documentation related to the CHRE framework: + * [Framework Overview](/doc/framework_overview.md) + * [Porting Guide](/doc/porting_guide.md) + * [Build System](/doc/framework_build.md) + * [Debugging](/doc/framework_debugging.md) + * [Testing](/doc/framework_testing.md) + * [Vendor Extensions](/doc/vendor_extensions.md) +* Documentation related to nanoapps: + * [Nanoapp Overview](/doc/nanoapp_overview.md) + * [Developer Guide](/doc/nanoapp_developer_guide.md) + * [Interacting with Nanoapps](/doc/nanoapp_clients.md) +* General documentation: + * [Compatibility Design](/doc/compatibility.md) + * [Contributing](/doc/contributing.md) + +The CHRE API and PAL API are also extensively documented using Doxygen syntax. +Run `doxygen` in the same folder as `Doxyfile` to generate a browseable HTML +version of the documentation. + +## Navigating the code + +This repository (system/chre) contains an assortment of code, structured as +follows: + +- ``apps/``: Public nanoapp source code, including sample nanoapps intended to + showcase how to use the CHRE APIs, and test nanoapps used to validate API + functionality +- ``build/``: Files related to CHRE’s Makefile-based build system, which + supports building the CHRE framework and nanoapps using a configurable + toolchain +- ``chpp/``: Context Hub Peripheral Protocol (CHPP) source code - see the nested + README and associated documentation for details +- ``chre_api/``: Contains the official definition of the CHRE API (current and + past versions), which all CHRE implementations must adhere to +- ``core/``: Common CHRE framework code, which is applicable to every platform + (contrast to ``platform/``) +- ``doc/``: Contains documentation for the CHRE framework and associated topics +- ``external/``: Code developed primarily outside of AOSP which (can be) + leveraged by CHRE, and is potentially customized for CHRE (e.g. flatbuffers) +- ``host/``: Reference code which supports the CHRE implementation, but runs on + the applications processor (“host”), for example the Context Hub HAL +- ``java/``: Java test code used in conjunction with test nanoapps +- ``pal/``: The binary-stable Platform Abstraction Layer (PAL) C API definitions + and tests (these PALs may optionally be used by the platform implementation) +- ``platform/``: Code related to the implementation of CHRE on a particular + platform/device (compare to ``core/``), divided into sub-folders as follows: + - ``platform/include``: The interface between common code in ``core/`` and + platform-specific code implemented elsewhere in ``platform/`` + - ``platform/shared``: Code that may apply to multiple platforms, but is not + necessarily applicable to _all_ platforms (as in ``core/``). For example, + multiple platforms may choose to use the binary-stable PAL interface - this + folder contains source files translating from the C++ platform abstractions + to the C PAL APIs. + - ``platform/<platform_name>``: Code specific to the platform indicated by + ``platform_name`` +- ``util/``: Utility code that is not platform-specific, but not part of the + core framework implementation. Includes code that is usable by nanoapps. +- ``variant/``: Code/configuration that is specific to a particular device (more + detail on variants can be found below). For example, multiple generations of a + given chip may use the same platform code, but the code may be configured on a + per-device basis in the build via ``variant.mk``. + +Code related to CHRE also exists in other parts of the Android tree, including: + +- ``hardware/interfaces/contexthub/``: The Context Hub HAL definition +- ``frameworks/base/core/java/android/hardware/location/ContextHub*.java``: The + APIs used by privileged apps to interact with CHRE and nanoapps +- ``frameworks/base/services/core/java/com/android/server/location/ContextHub*.java``: + The Context Hub service implementation in system server + +# Have Questions? + +If you’re unable to find the answers you’re looking for in CHRE documentation +or are looking for specific guidance for your platform, device, or nanoapp, +please reach out to the CHRE team via your TAM or through the [Google Issue +Tracker](https://developers.google.com/issue-tracker). diff --git a/doc/compatibility.md b/doc/compatibility.md new file mode 100644 index 00000000..a62c10cc --- /dev/null +++ b/doc/compatibility.md @@ -0,0 +1,218 @@ +# Design for Compatibility + +[TOC] + +Compatibility is an important attribute of CHRE, which is accomplished through a +combination of thoughtful API and framework design. When we refer to +compatibility within the scope of CHRE, there are two main categories: + +* **Code compatibility**, which means that a nanoapp can be recompiled to run on + a new platform without needing any code changes. CHRE provides this + cross-device compatibility for all nanoapps which are written in a supported + programming language (C99 or C++11), and reference only the standard CHRE APIs + and mandatory standard library elements (or have these standard library + functions statically linked into their binary). + +* **Binary compatibility**, which means that a nanoapp binary which has been + compiled against a particular version of the CHRE API can run on a CHRE + framework implementation which was compiled against a different version of the + API. This is also called *cross-version compatibility*. Note that this does + *not* mean that a nanoapp compiled against one version of the CHRE API can be + compiled against a different version of the CHRE API without compiler errors - + although rare, compile-time breakages are permitted with sufficient + justification, since nanoapp developers can update their code at the time they + migrate to the new API version. + +This section provides an overview of the mechanisms used to ensure +compatibility. + +## CHRE API + +The CHRE API is a native C API that defines the interface between a nanoapp and +any underlying CHRE implementation to provide cross-platform and cross-version +compatibility. It is designed to be supportable even in very memory-constrained +environments (total system memory in the hundreds of kilobytes range), and is +thoroughly documented to clearly indicate the intended behavior. + +The CHRE API follows [semantic versioning](https://semver.org) principles to +maintain binary compatibility. In short, this means that the minor version is +incremented when new features and changes are introduced in a backwards +compatible way, and the major version is only incremented on a +compatibility-breaking change. One key design goal of the CHRE API is to avoid +major version changes if at all possible, through use of +compatibility-preserving code in the framework and Nanoapp Support Library +(NSL). + +Minor version updates to the CHRE API typically occur alongside each Android +release, but the CHRE version and Android version are not intrinsically related. +Nanoapps should be compiled against the latest version to be able to use any +newly added features, though nanoapp binaries are compatible across minor +version changes. + +### API Compatibility Design Principles + +API design principles applied within CHRE to ensure compatibility include the +following (not an exhaustive list). These are recommended to be followed for any +vendor-specific API extensions as well. + +* Functionality must not be removed unless it was optional at the time of + introduction, for example as indicated by a capabilities flag (an exception + exists if it has no impact on the regular functionality of a nanoapp, for + example a feature that only aids in debugging) +* Reserved fields must be set to 0 by the sender and ignored by the recipient +* Fields within a structure must not be reordered - new fields may only be + introduced by reclaiming reserved fields (preferred), or adding to the end of + a structure +* When reclaiming a reserved field, the default value of 0 must indicate a + property that is guaranteed to hold for previous API versions, or “unknown” +* Arguments to a function must not be added or removed - introduce a new + function instead +* The meaning of constants (e.g. event types) must never be changed, but may be + deprecated and eventually replaced + +## Binary Backward Compatibility and the NSL + +This is where we want a nanoapp compiled against e.g. v1.2 to run on a CHRE v1.1 +or older implementation. This is done through a combination of runtime feature +discovery, and compatibility behaviors included in the Nanoapp Support Library +(NSL). + +Runtime feature discovery involves a nanoapp querying for the support of a +feature (e.g. RTT support indicated in `chreWifiGetCapabilities()`, or querying +for a specific sensor in `chreSensorFindDefault()`), which allows it determine +whether the associated functionality is expected to work. The nanoapp may also +query `chreGetApiVersion()` to find out the version of the CHRE API supported by +the platform it is running on. If a nanoapp has a hard requirement on some +missing functionality, it may choose to return false from `nanoappStart()` to +abort initialization. + +However, a CHRE implementation cannot anticipate all future API changes and +automatically provide compatibility. So the NSL serves as a transparent shim +which is compiled into the nanoapp binary to ensure this compatibility. For +example, a nanoapp compiled against v1.2 must be able to reference and call +`chreConfigureHostSleepStateEvents()` when running on a CHRE v1.1 or earlier, +although such a function call would have no effect in that case. Typical dynamic +linking approaches would find an unsatisfied dependency and fail to load the +nanoapp, even if it does not actually call the function, for example by wrapping +it in a condition that first checks the CHRE version. In +`platform/shared/nanoapp/nanoapp_support_lib_dso.cc`, this is supported by +intercepting CHRE API function calls and either calling through to the +underlying platform if it’s supported, or replacing it with stub functionality. + +Along similar lines, if new fields are added to the end of a structure without +repurposing a reserved field in an update to the CHRE API, as was the case with +`bearing_accuracy` in `chreGnssLocationEvent`, the nanoapp must be able to +reference the new field without reading uninitialized memory. This is enabled by +the NSL, which can intercept the event, and copy it into the new, larger +structure, and set the new fields to their default values. + +Since these NSL compatibility behaviors carry some amount of overhead (even if +very slight), they can be disabled if it is known that a nanoapp will never run +on an older CHRE version. This may be the case for a nanoapp developed for a +specific device, for example. The NSL may also limit its compatibility range +based on knowledge of the API version at which support for given hardware was +introduced. For example, if a new hardware family first added support for the +CHRE framework at API v1.1, then NSL support for v1.0 is unnecessary. + +Outside of these cases, the NSL must provide backwards compatibility for at +least 3 previous versions, and is strongly recommended to provide support for +all available versions. This means that if the first API supported by a target +device is v1.0, then a nanoapp compiled against API v1.4 must have NSL support +for v1.1 through v1.4, and should ideally also support v1.0. + +## Binary Forward Compatibility and Framework Requirements + +Conversely, this is where we want a nanoapp compiled against e.g. v1.1 to run +against CHRE v1.2 or later implementations. The NSL cannot directly provide this +kind of compatibility, so it must be ensured through a combination of careful +CHRE API design, and compatibility behaviors in the CHRE framework. + +Similar to how Android apps have a “target SDK” attribute, nanoapps have a +“target API version” which indicates the version of the CHRE API they were +compiled against. The framework can inspect this value and provide compatibility +behavior as needed. For example, `chreGetSensorInfo()` populates memory provided +by the nanoapp with information about a given sensor. In CHRE API v1.1, this +structure was extended with a new field, `minInterval`. Therefore, the framework +must check if the nanoapp’s target API is v1.1 or later before writing this +field. + +To avoid carrying forward compatibility code indefinitely, it is permitted for a +CHRE implementation to reject compatibility with nanoapps compiled against an +API minor version that is 2 or more generations older. For example, a CHRE v1.4 +implementation may reject attempts to load a nanoapp compiled against CHRE API +v1.2, but it must ensure compatibility with v1.3. However, providing the full +range of compatibility generally does not require significant effort on behalf +of the CHRE implementation, so this is recommended for maximum flexibility. + +## ABI Stability + +CHRE does not define a standard Application Binary Interface (ABI) - this is +left as a platform responsibility in order to provide maximum flexibility. +However, CHRE implementations must ensure that binary compatibility is +maintained with nanoapps, by choosing a design that provides this property. For +example, if a syscall-like approach is used (with the help of the NSL) to call +from position-independent nanoapp code into fixed-position CHRE API functions +(e.g. in a statically linked monolithic firmware image), syscall IDs and their +calling conventions must remain stable. It is not acceptable to require all +nanoapps to be recompiled to be able to work with an updated CHRE +implementation. + +## CHRE PALs + +Since the PAL APIs are largely based on the CHRE APIs, they benefit from many of +the compatibility efforts by default. Overall, binary compatibility in the CHRE +PAL APIs are less involved than the CHRE APIs, because we expect CHRE and CHRE +PAL implementations to be built into the vendor image together, and usually run +at the same version except for limited periods during development. However, a +PAL implementation can simultaneously support multiple PAL API versions from a +single codebase by adapting its behavior based on the `requestedApiVersion` +parameter in the \*GetApi method, e.g. `chrePalWifiGetApi()`. + +## Deprecation Strategy + +In general, nanoapp compilation may be broken in a minor update (given +sufficient justification - this is not a light decision to make, considering the +downstream impact to nanoapp developers), but deprecation of functionality at a +binary level occurs over a minimum of 2 years (minor versions). The general +process for deprecating a function in the CHRE API is as follows: + +* In a new minor version `N` of the CHRE API, the function is marked with + `@deprecated`, with a description of the recommended alternative, and ideally + the justification for the deprecation, so nanoapp developers know why it's + important to update. + + * Depending on the severity of impact, the function may also be tagged with a + compiler attribute to generate a warning (e.g. `CHRE_DEPRECATED`) that may + be ignored. Or, version `N` or later, an attribute or other method may be + used to break compilation of nanoapps using the deprecated function, forcing + them to update. If not considered a high severity issue and compatibility is + easy to maintain, it is recommended to break compilation only in version + `N+2` or later. + + * Binary compatibility at this stage must be maintained. For example the NSL + should map the new functionality to the deprecated function when running on + CHRE `N-1` or older, or a suitable alternative must be devised. Likewise, + CHRE must continue to provide the deprecated function to support nanoapps + built against `N-1`. + +* Impacts to binary compatibility on the CHRE side may occur 2 versions after + the function is made compilation-breaking for nanoapps, since forward + compatibility is guaranteed for 2 minor versions. If done, the nanoapp must be + rejected at load time. + +* Impacts to binary compatibility on the nanoapp side may occur 4 versions after + the function is marked deprecated (at `N+4`), since backward compatibility is + guaranteed for 4 minor versions. If done, the NSL must cause `nanoappStart()` + to return false on version `N` or older. + +For example, if a function is marked deprecated in `N`, and becomes a +compilation-breaking error in `N+2`, then a CHRE implementation at `N+4` may +remove the deprecated functionality only if it rejects a nanoapp built against +`N+1` or older at load time. Likewise, the NSL can remove compatibility code for +the deprecated function at `N+4`. CHRE and NSL implementations must not break +compatibility in a fragmented, unpredictable, or hidden way, for example by +replacing the deprecated function with a stub that does nothing. If it is +possible for CHRE and/or the NSL to detect only nanoapps that use the deprecated +functionality, then it is permissible to block loading of only those nanoapps, +but otherwise this must be a blanket ban of all nanoapps compiled against the +old API version. diff --git a/doc/contributing.md b/doc/contributing.md new file mode 100644 index 00000000..f5de7afa --- /dev/null +++ b/doc/contributing.md @@ -0,0 +1,10 @@ +# Contributing to CHRE + +It is encouraged that vendors implementing CHRE for their platforms contribute +their code back to the open source project when possible as this will help +provide more reference code for others and give the CHRE team better insight +into the various use cases of users of the project. + +Anyone can upload patches to the Android Open Source Project (AOSP) and have +the CHRE team review them using [these +instructions](https://source.android.com/setup/contribute/submit-patches). diff --git a/doc/framework_build.md b/doc/framework_build.md new file mode 100644 index 00000000..25484494 --- /dev/null +++ b/doc/framework_build.md @@ -0,0 +1,149 @@ +# CHRE Framework Build System + +[TOC] + +The CHRE build system is based on Make, and uses a set of Makefiles that allow +building the CHRE framework for a variety of hardware and software architectures +and variants (e.g. different combinations of CPU and OS/underlying system). It +is also flexible to different build toolchains (though LLVM/Clang or GCC are +recommended), by abstracting out key operations to a few Make or environment +variables. While the CHRE framework source code may be integrated into another +build system, it can be beneficial to leverage the existing build system to +reduce maintenance burden when the CHRE framework is updated. Additionally, it +should be possible to build nanoapps from the CHRE build system (to have +commonality across devices), and the CHRE framework build shares configuration +with the nanoapp build. + +By default, the output of the build is linked into both a static archive +`libchre.a` and a shared library `libchre.so`, though generally only one of the +two is used, depending on the target device details. + +## Design + +The CHRE build system was originally designed around the philosophy that a +vanilla invocation of `make` or `make all` should build any given nanoapp for +all targets. This allows for efficient use of the job scheduler in the Make +build system for multi-threaded builds and also promotes a level of separation +of concerns between targets (this is not enforced by any Make language +construct, merely convention). In practice, the CHRE build system is rarely used +to build multiple targets with one invocation of Make. However, the design goal +is carried forward for the variant support and separation of concerns between +targets. + +All variant-specific compiler and linker flags are held under variables that +only apply to their specific target. This encourages developers to avoid leaking +build details between targets. This is important because not all compiler or +linker flags are compatible with all toolchains. For example: if a target uses a +compiler that does not support `-Wdouble-promotion`, but this were to be +enforced for all builds, then by definition this target would not compatible +with the CHRE build. The goal is for the CHRE build to be as flexible as +possible. + +### Build Template + +The CHRE build system is implemented using template meta-programming techniques. +A build template is used to create Make rules for tasks that are common to all +targets. This includes compiling C/C++/assembly sources, linking, nanoapp header +generation, etc. The rationale behind this approach is to reduce boilerplate +when adding support for a new build target. + +The build template is located at `build/build_template.mk`, and is documented +with all the variables used to generate build targets, like `TARGET_CFLAGS`. + +## Build Targets (Variants) + +Compiling the framework for different devices is done by specifying the build +target when invoking `make`. Conventionally, build targets consist of three +parts: (software) vendor, architecture and variant and follow the +`<vendor>_<arch>_<variant>` pattern. A “vendor” is typically the company that +created the CHRE implementation, which may bring with it some details related to +nanoapp compatibility, for example the Nanoapp Support Library from the same +vendor may be required. The “arch” field refers to the Instruction Set +Architecture (ISA) and related compiler configuration to create a binary for the +target processor. The “variant” is primarily related to the underlying platform +software that the CHRE framework builds on top of, such as the combination of +operating system and other software needed to select the appropriate combination +of code in the `platform/` folder, but can also define other attributes of the +build, such as the target memory region for the binary. If a vendor, +architecture, or variant consist of multiple words or components, then they +should be separated by a hyphen and not an underscore. + +For example, if we assume that a fictional company named Aperture developed its +own CHRE framework implementation, targeting a CPU family called Potato, and a +collection of platform software called GladOS/Cake, then a suitable build target +name would be `aperture_potato_glados-cake`. + +The build target may optionally have `_debug` appended, which is a common suffix +which enables `-g` and any additional target-specific debug flags. + +### Creating a New Build Target + +#### Architecture Support + +The architecture-specific portion of the build deals with mainly the build +toolchain, and its associated flags. + +It is easiest to check if the architecture is currently listed in `build/arch`, +and if it is, _Hooray! You're (almost) done_. It is still worthwhile to quickly +read through to know how the build is layered. + +CHRE expects the build toolchain to be exported via Makefile variables, +specifically the compiler (`TARGET_CC`), archiver (`TARGET_AR`), and the linker +(`TARGET_LD`). Architecture specific compiler and linker flags are passed in via +the `TARGET_CFLAGS` and `TARGET_LDFLAGS` respectively. Additional +architecture-specific configuration is possible - refer to existing files under +`build/arch` and `build/build_template.mk` for details. + +#### Build Target Makefile + +Makefiles for each build target can be found at +`build/variant/<target_name>.mk`. These files are included at the end of the +top-level Makefile, and has the responsibility of collecting arguments for the +build template and invoking it to instantiate build rules. This involves doing +steps including (not an exhaustive listing): + +* Setting the target name and platform ID + +* Configuring (if needed) and including the apporpriate `build/arch/*.mk` file + +* Collecting sources and flags specific to the platform into + `TARGET_VARIANT_SRCS` and `TARGET_CFLAGS` + +* Including `build/build_template.mk` to instantiate the build targets - this + must be the last step, as the make targets cannot be modified once generated + +Refer to existing files under `build/variant` for examples. + +## Device Variants + +While the build target is primarily concerned with configuring the CHRE build +for a particular chipset, the same chipset can appear in multiple device +models/SKUs, potentially with different peripheral hardware, targeted levels of +feature support, etc. Additionally, a device/chip vendor may wish to provide +additional build customization outside of the Makefiles contained in the +system/chre project. The build system supports configuration at this level via +the device variant makefile, typically named `variant.mk`, which is injected +into the build by setting the `CHRE_VARIANT_MK_INCLUDES` environment variable +when invoking the top-level Makefile. Refer to the file +`variant/android/variant.mk` for an example. + +## Platform Sources + +The file at `platform/platform.mk` lists sources and flags needed to compile the +CHRE framework for each supported platform. These must be added to Make +variables prefixed with the platform name (for example, `SIM_SRCS` for platform +sources used with the simulator build target), and not `COMMON_SRCS` or other +common variables, to avoid affecting other build targets. + +## Build Artifacts + +At the end of a successful build, the following are generated in the `out` +directory: + +* `<build_target>/libchre.so` and `libchre.a`: the resulting CHRE framework + binary, built as a dynamic/static library + +* `<build_target>_objs/`: Directory with object files and other intermediates + +* Depending on the build target, additional intermediates (e.g. `nanopb_gen` for + files generated for use with NanoPB) diff --git a/doc/framework_debugging.md b/doc/framework_debugging.md new file mode 100644 index 00000000..67f1bbf1 --- /dev/null +++ b/doc/framework_debugging.md @@ -0,0 +1,68 @@ +# Debugging the CHRE Framework + +[TOC] + +This section lists the methods that can be used to aid in CHRE framework +debugging. + +## Logging + +The CHRE framework invokes the `LOGx()` macros defined in +`platform/include/chre/platform/log.h` to log information into the system, +for printf-style debugging. This capability is also exposed to nanoapps via +`chreLog()`. Although it may not be strictly required, it is strongly +recommended that the platform implementation directs these log messages to +Android’s logcat system under the “CHRE” tag, where they will be automatically +collected in bug reports. These logs must not wake the applications processor +(AP), so they should be buffered on a best-effort basis and flushed to the AP +opportunistically when it wakes up. + +### Log levels + +CHRE framework currently supports four levels, namely Error `LOGE()`, Warning +` LOGW()`, Info `LOGI()` and Debug `LOGD()`. These correspond to the +equivalent [logcat +levels](https://source.android.com/setup/contribute/code-style#log-sparingly). +Choosing an appropriate level for logs, and logging only the necessary +information to identify and debug failures can help avoid issues with “log +spam”, such as log output that is difficult to read, or uninteresting “spammy” +logs causing useful log messages to be pushed out of limited buffering space. + +### Log level filtering + +The CHRE framework currently only supports compile-time log level filtering. +While it’s recommended to leave it at the default setting, the +`CHRE_MINIMUM_LOG_LEVEL` build flag can be defined to one of the values set +in `util/include/chre/util/log_common.h` to control which log levels are +included in the binary. + +## Debug Dumps + +A debug dump is human-readable text produced on-demand for debugging purposes. +While `LOGx()` is useful for logging events as they happen, the debug dump is +a complementary function typically used to output a snapshot of the framework's +state, history, vital statistics, etc. The debug dump is especially useful for +information that would be too spammy to log on every change, but is useful to +diagnose potential issues. The CHRE framework produces a debug dump when +requested via the Context Hub HAL’s built-in `debug()` method, which is +automatically collected as part of the bug report process, and can be manually +triggered via ADB using the following command: + +`adb root && adb shell lshal debug android.hardware.contexthub@1.0::IContexthub/default` + +`DebugDumpManager` is the framework module responsible for collecting debug +dumps from the CHRE framework and nanoapps. Refer to the associated +documentation in the source code for details on this process. + +## CHRE_ASSERT and CHRE_ASSERT_LOG + +`CHRE_ASSERT()` and `CHRE_ASSERT_LOG()` can be used to help catch +programmatic errors during development. However, since their use may have +memory impact, they can be disabled by setting `CHRE_ASSERTIONS_ENABLED` to +false in the Makefile. In general, assertions should be used sparingly - they +are best applied to situations that would lead to potentially unrecoverable +logical errors that are not handled by the code. For comparison, asserting that +a pointer is not null is generally an anti-pattern (though the current CHRE +codebase is not free of this), as dereferencing null would produce a crash +anyways which should have equivalent debuggability as an assertion, among other +reasons. diff --git a/doc/framework_overview.md b/doc/framework_overview.md new file mode 100644 index 00000000..ab9d0b22 --- /dev/null +++ b/doc/framework_overview.md @@ -0,0 +1,205 @@ +*** aside +See also: +[Porting Guide](/doc/porting_guide.md) | +[Build System](/doc/framework_build.md) | +[Debugging](/doc/framework_debugging.md) | +[Testing](/doc/framework_testing.md) | +[Vendor Extensions](/doc/vendor_extensions.md) +*** + +# CHRE Framework Overview + +[TOC] + +The CHRE reference implementation (hereafter referred to just as "CHRE" or "the +CHRE framework") is developed primarily in C++11 using a modular object-oriented +approach that separates common code from platform-specific code. CHRE is an +event-based system, so CHRE is built around an event loop which executes nanoapp +code as well as CHRE system callbacks. Per the CHRE API, nanoapps can’t execute +in more than one thread at a time, so CHRE is structured around a single thread +that executes the event loop, although there may be other threads in the system +that support CHRE. The EventLoopManager is a Singleton object which owns the +main state of the CHRE framework, including EventLoop and \*Manager classes for +the various subsystems supported by CHRE. + +To get a better understanding of code structure and how it weaves between common +and platform-specific components, it is helpful to trace the flow through a few +example scenarios. Note that this is not meant to be an exhaustive list of +everything that happens in each case (for that, refer to the code itself), but +rather an overview of key points to serve as an introduction. + +## Loading a nanoapp via the HAL + +There are multiple ways by which a nanoapp can be loaded (see the relevant +section below for details), but this example traces the flow for dynamically +loading a nanoapp that has been passed in via the Context Hub HAL's +`loadNanoapp()` method. + +1. The nanoapp binary reaches the HAL implementation, and it is loaded into the + processor where CHRE is running using a platform-specific method. While the + path this takes can vary, one common approach is to transmit the binary into + CHRE via the platform-specific HostLink implementation, then verify its + digital signature, and parse the binary file format (e.g. ELF) to load and + link the code. + +2. Once the nanoapp code is loaded, the platform code calls + `EventLoopManager::deferCallback()` to switch context to the main CHRE thread + (if needed), so it can complete loading and starting the nanoapp. + `deferCallback()` effectively posts an event to the main event loop which + does not get delivered to any nanoapps. Instead, the purpose is to invoke the + supplied callback from the CHRE thread once the event is popped off the + queue. + +3. The (platform-specific) callback finalizes the newly constructed `Nanoapp` + object as needed, and passes it to `EventLoop::startNanoapp()` - this marks a + transition from platform-specific to common code. + +4. `EventLoop` takes ownership of the `Nanoapp` object (which is a composite of + common and platform-specific data and functions, as described in the Platform + Abstractions section), includes it in the collection of loaded nanoapps to + execute in the main event loop, updates `mCurrentNanoapp` to reference the + nanoapp it's about to execute, and calls into `PlatformNanoapp::start()`. + +5. Since the mechanism of supporting dynamic linkage and position independent + code can vary by platform, transferring control from the framework to a + nanoapp is considered part of the platform layer. So + `PlatformNanoapp::start()` performs any necessary tasks for this, and calls + into the `nanoappStart()` function defined in the nanoapp binary. + +## Invoking a CHRE API from a nanoapp + +Let's assume the nanoapp we've loaded in the previous section calls the +`chreSensorConfigure()` CHRE API function within `nanoappStart()`: + +1. The nanoapp invokes `chreSensorConfigure()` with parameters to enable the + accelerometer. + +2. The Nanoapp Support Library (NSL) and/or the platform's dynamic linking + module are responsible for handling the transition of control from the + nanoapp binary to the CHRE framework. This can vary by platform, but we'll + assume that control arrives in the `chreSensorConfigure()` implementation in + `platform/shared/chre_api_sensor.cc`. + +3. `EventLoopManager::validateChreApiCall()` is invoked to confirm that this + function is being called from the context of a nanoapp being executed within + the event loop (since associating the API call with a specific nanoapp is a + requirement of this API and many others, and the majority of the CHRE + framework code is only safe to execute from within the main CHRE thread), and + fetch a pointer to the current `Nanoapp` (i.e. it retrieves `mCurrentNanoapp` + set previosly by `EventLoop`). + +4. `SensorManager::setSensorRequest()` (via + `EventLoopManager::getSensorRequestManager()`) is called to process the + nanoapp’s request - we transition to common code here. + +5. The request is validated and combined with other nanoapp requests for the + same sensor to determine the effective sensor configuration that should be + requested from the platform, and the nanoapp is registered to receive + broadcast accelerometer sensor events. + +6. `SensorRequestManager` calls into `PlatformSensorManager::configureSensor()`, + which performs the necessary operations to actually configure the + accelerometer to collect data. + +7. Assuming success, the return value propagates back up to the nanoapp, and it + continues executing. + +## Passing an event to a nanoapp + +Following the example from above, let's follow the case where an accelerometer +sample has been generated and is delivered to the nanoapp for processing. + +1. Starting in platform-specific code, likely in a different thread, the + accelerometer sample is received from the underlying sensor framework - this + typically happens in a different thread than the main CHRE thread, and within + the fully platform-specific `PlatformSensorManagerBase` class. + +2. As needed, memory is allocated to store the sample while it is being + processed, and the data is converted into the CHRE format: `struct + chreSensorThreeAxisData`. + +3. `SensorRequestManager::handleSensorDataEvent()` is invoked (common code) to + distribute the data to nanoapps. + +4. `SensorRequestManager` calls into `EventLoop` to post an event containing the + sensor data to all nanoapps registered for the broadcast event type + associated with accelerometer data, and sets `sensorDataEventFree()` as the + callback invoked after the system is done processing the event. + +5. `EventLoop` adds this event to its event queue and signals the CHRE thread. + +6. Now, within the context of the CHRE thread, once the event loop pops this + event off of its queue in `EventLoop::run()`, the `nanoappHandleEvent()` + function is invoked (via `PlatformNanoapp`, as with `nanoappStart`) for each + nanoapp that should receive the event. + +7. Once the event has been processed by each nanoapp, the free callback + (`sensorDataEventFree()`), is called to release any memory or do other + necessary cleanup actions now that the event is complete. + +## Platform Abstractions + +CHRE follows the 'compile time polymorphism' paradigm, to allow for the benefits +of `virtual` functions, while minimizing code size impact on systems with tight +memory constraints. + +Each framework module as described in the previous section is represented by a +C++ class in `core/`, which serves as the top-level reference to the module and +defines and implements the common functionality. This common object is then +composed with platform-specific functionality at compile-time. Using the +`SensorRequestManager` class as an example, its role is to manage common +functionality, such as multiplexing sensor requests from all clients into a +single request made to the platform through the `PlatformSensorManager` class, +which in turn is responsible for forwarding that request to the underlying +sensor system. + +While `SensorRequestManager` is fully common code, `PlatformSensorManager` is +defined in a common header file (under `platform/include/chre/platform`), but +implemented in a platform-specific source file. In other words, it defines the +interface between common code and platform-specific code. + +`PlatformSensorManager` inherits from `PlatformSensorManagerBase`, which is +defined in a platform-specific header file, which allows for extending +`PlatformSensorManager` with platform-specific functions and data. This pattern +applies for all `Platform<Module>` classes, which must be implemented for all +platforms that support the given module. + +Selection of which `PlatformSensorManager` and `PlatformSensorManagerBase` +implementation is instantiated is controlled by the build system, by setting the +appropriate include path and source files. This includes the path used to +resolve include directives appearing in common code but referencing +platform-specific headers, like `#include +"chre/target_platform/platform_sensor_manager_base.h"`. + +To ensure compatibility across all platforms, common code is restricted in how +it interacts with platform-specific code - it must always go through a common +interface with platform-specific implementation, as described above. However, +platform-specific code is less restricted, and can refer to common code, as well +as other platform code directly. + +## Coding conventions + +This project follows the [Google-wide style guide for C++ +code](https://google.github.io/styleguide/cppguide.html), with the exception of +Android naming conventions for methods and variables. This means 2 space +indents, camelCase method names, an mPrefix on class members and so on. Style +rules that are not specified in the Android style guide are inherited from +Google. Additionally, this project uses clang-format for automatic code +formatting. + +This project uses C++11, but with two main caveats: + +1. General considerations for using C++ in an embedded environment apply. This + means avoiding language features that can impose runtime overhead, due to the + relative scarcity of memory and CPU resources, and power considerations. + Examples include RTTI, exceptions, overuse of dynamic memory allocation, etc. + Refer to existing literature on this topic including this [Technical Report + on C++ Performance](http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf) + and so on. + +2. Full support of the C++ standard library is generally not expected to be + extensive or widespread in the embedded environments where this code will + run. This means things like <thread> and <mutex> should not be used, in + favor of simple platform abstractions that can be implemented directly with + less effort (potentially using those libraries if they are known to be + available). diff --git a/doc/framework_testing.md b/doc/framework_testing.md new file mode 100644 index 00000000..01ebfcaa --- /dev/null +++ b/doc/framework_testing.md @@ -0,0 +1,58 @@ +# Testing the CHRE Framework + +[TOC] + +The CHRE framework can be tested at various levels to ensure that +components/modules components are working correctly and API contracts are being +met. Below are some examples of what the team currently does to verify new +changes. + +## Unit tests + +Currently, unit tests exist for various core components and utilities. Since +platform-specific components likely aren’t compilable/available on a host +machine, only components that are OS independent can be tested via this path. + +In order to write new tests, add a test source file under the test directory in +the appropriate subdirectory. e.g. `util/tests`. Then, add the file to the +`GOOGLETEST_SRCS` variable in the appropriate .mk file for that subdir, +`util/util.mk` for example. + +Unit tests can be built and executed using `run_tests.sh`. + +## PAL implementation tests + +PAL implementation tests verify implementations of PAL interfaces adhere to the +requirements of that interface, and are intended primarily to support +development of PAL implementations, typically done by a chip vendor partner. +Additional setup may be required to integrate with the PAL under test and supply +necessary dependencies. The source code is in the files under `pal/tests/src` +and follows the naming scheme `*_pal_impl_test.cc`. + +In order to run PAL tests, run: `run_pal_impl_tests.sh`. Note that there are +also PAL unit tests in the same directory. The unit tests are added to the +`GOOGLETEST_SRCS` target while PAL tests are added to the +`GOOGLETEST_PAL_IMPL_SRCS` target. + +## FeatureWorld nanoapps + +Located under the `apps/` directory, FeatureWorld nanoapps interact with the set +of CHRE APIs that they are named after, and can be useful during framework +development for manual verification of a feature area. For example, SensorWorld +attempts to samples from sensors and outputs to the log. It also offers a +break-it mode that randomly changes which sensors are being sampled in an +attempt to point out stress points in the framework or platform implementation. + +These apps are usually built into the CHRE framework binary as static nanoapps +to facilitate easy development. See the Deploying Nanoapps section for more +information on static nanoapps. + +## CHQTS + +The Context Hub Qualification Test Suite (CHQTS) tests perform end-to-end +validation of a CHRE implementation, by using the Java APIs in Android to load +and interact with test nanoapps which then exercise the CHRE API. While this +code is nominally integrated in another test suite, the source code is available +under `java/test/chqts/` for the Java side code and `apps/test/chqts/` for the +CHQTS-only nanoapp code and `apps/test/common/` for the nanoapp code shared by +CHQTS and other test suites. diff --git a/doc/nanoapp_clients.md b/doc/nanoapp_clients.md new file mode 100644 index 00000000..9d5af0cc --- /dev/null +++ b/doc/nanoapp_clients.md @@ -0,0 +1,68 @@ +# Interacting with Nanoapps from Client Code + +[TOC] + +Code that interacts with a nanoapp, for example within an Android app, is known +as the *client* of the nanoapp. There are two ways to interact with nanoapps +from the host (application processor): (1) Java (above the Context Hub HAL, from +the Android framework or an APK), and (2) native vendor code (beneath the +Context Hub HAL). Most clients, especially those with a UI component, should use +the Java method, unless a vendor-partition native implementation is required +(e.g. if interacting with a nanoapp is used to implement a different HAL, or if +communication with other beneath-HAL modules is required). + +Interaction between nanoapps and clients occur through a flexible message +passing interface. Refer to the Nanoapp Developer Guide for recommendations on +how to design a protocol for use with a nanoapp. + +## Java APIs + +CHRE is exposed to Android apps holding the appropriate permissions through the +[ContextHubManager](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/hardware/location/ContextHubManager.java) +and the associated +[ContextHubClient](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/hardware/location/ContextHubClient.java) +system APIs. + +To use the above APIs, your application must have access to the +`ACCESS_CONTEXT_HUB` permission, which is restricted to `signature|privileged`. +This permission must be declared in the app’s [Android +Manifest](https://developer.android.com/guide/topics/manifest/uses-permission-element), +and is only granted to APKs that are signed with the same key as the platform +(“signature” scope) or are preinstalled in the privileged apps folder *and* are +present on the [privileged permission +whitelist](https://source.android.com/devices/tech/config/perms-whitelist). + +The recommended flow for Java nanoapp client code is as follows: + +1. Retrieve the ContextHubManager object via + `Context.getSystemService(ContextHubManager.class)` - this will produce a + valid handle if the device supports `FEATURE_CONTEXT_HUB` as indicated by + `PackageManager.hasSystemFeature()` +2. Retrieve a reference to a Context Hub via + `ContextHubManager.getContextHubs()` +3. Confirm that the nanoapp is loaded and retrieve its version number by calling + `ContextHubManager.queryNanoApps()` +4. If the nanoapp was found, create a `ContextHubClient` object through + `ContextHubManager.createClient()`. This can be used to communicate with your + nanoapp, and receive notifications of system events like reset. Note that the + `createClient()` API supports two modes of operation, which define how events + and data are passed to the client: direct callback with + `ContextHubClientCallback` (requires a persistent process), or + `PendingIntent` with `ContextHubIntentEvent` (can start an app’s process when + an event occurs). To send messages to the nanoapp, use + `ContextHubClient.sendMessageToNanoApp()`. + +## Vendor Native + +Depending on the details of the platform implementation, you may also be able to +interact with CHRE directly, beneath the Context Hub HAL, by using socket IPC as +exposed by the CHRE daemon reference implementation. This approach has some +advantages, like being able to interact with system nanoapps that are not +exposed at the Java level, and it can be used with other low-level beneath-HAL +code. However, it is not suitable for use from native code within an Android +app. + +See `host/common/test/chre_test_client.cc` for an example of how to use this +interface. Note that SELinux configuration is generally required to whitelist +access to the CHRE socket. + diff --git a/doc/nanoapp_developer_guide.md b/doc/nanoapp_developer_guide.md new file mode 100644 index 00000000..b7b94918 --- /dev/null +++ b/doc/nanoapp_developer_guide.md @@ -0,0 +1,343 @@ +# Nanoapp Developer Guide + +[TOC] + +Since CHRE is an open platform, anyone can write nanoapps. However, deploying to +a device requires cooperation of the device manufacturer, because CHRE is a +security-sensitive trusted environment, similar to other low-level firmware, and +it typically has tight resource constraints. This section assumes you have a +basic understanding of what a nanoapp is (if not, see the Nanoapp Overview +section), and provides some simple instructions to help you get started with +developing your own nanoapp. + +## Getting Started + +When starting a new nanoapp, it’s helpful to start with the skeleton of an +existing nanoapp. The simplest example can be found at `apps/hello_world`. Start +by copying this folder to the location where you will develop the nanoapp - it +can be outside of the `system/chre` project, for example in a vendor-specific +Git repository in the `vendor/` folder of the Android tree. + +If you don’t plan to use this nanoapp as a *static nanoapp* (see the Nanoapp +Overview for details), remove the `hello_world.mk` file and delete the code +blocks wrapped in `#ifdef CHRE_NANOAPP_INTERNAL`. Rename the remaining files to +match your nanoapp. + +### Picking a Nanoapp ID + +Nanoapps are uniquely identified by a 64-bit number. The most significant 5 +bytes of this number are the vendor identifier, and the remaining bytes identify +the nanoapp within the vendor’s namespace. The vendor identifier is usually +devised from an ASCII representation of the vendor’s name, for example Google +uses 0x476F6F676C (“Googl”). The remaining portion of the ID is typically just +an incrementing value for each nanoapp. + +Refer to `system/chre/chre_api/include/chre_api/chre/common.h` and +`util/include/chre/util/nanoapp/app_id.h` for some examples and utilities. + +Be sure to pick a unique nanoapp ID when creating a new nanoapp. + +### Picking a Language + +CHRE guarantees support for nanoapps written in C99 or C++11, though not all +standard library functions are supported (see below for details). For a +device-specific nanoapp, additional programming languages/versions *may* be +supported, but this can impact portability. + +### Building the Nanoapp Binary + +While it’s possible to build a nanoapp with a different build system, just as it +is for the CHRE framework, it’s recommended to use the common build system +included in this project, as it makes it easy to support a variety of target +platforms. The rest of this section assumes you are using the CHRE build system +to create a non-static nanoapp. + +Update the `Makefile` in your nanoapp’s directory to: + +* Define nanoapp metadata, including: + * `NANOAPP_NAME`: sets the output filename of the binary + * `NANOAPP_ID`: 64-bit identifier, in hexadecimal format + * `NANOAPP_VERSION`: 32-bit version, in hexadecimal format (see versioning + section below) + * `NANOAPP_NAME_STRING`, `NANOAPP_VENDOR_STRING`: human-readable strings for + the name of the nanoapp and vendor, respectively + * `NANOAPP_IS_SYSTEM_NANOAPP`: 0 or 1 (see Nanoapp Overview) +* Populate `COMMON_SRCS` with the C or C++ source files to compile +* Populate `COMMON_CFLAGS` with compiler flags, like additional include paths +* Include any additional `.mk` files for vendor extensions, etc. before `app.mk` + +Refer to `build/nanoapp/app.mk` for full details. + +The nanoapp can then be built using a command like ``OPT_LEVEL=s make +<build_target> -j`nproc` `` (see the CHRE Framework Build System section for +details on build targets), which will produce build artifacts at +`out/<build_target>/<nanoapp_name>.*`. + +### Loading onto a Device + +Exact steps to load a nanoapp binary can vary by device, but for developing a +preloaded nanoapp, this typically involves the following steps: + +* Perform any needed post-processing of the nanoapp binary to permit it to be + loaded (such as signing with a development or production key) +* Write the binary to the device’s storage (for example, using `adb push`) +* Update `preloaded_nanoapps.json` or other configuration as needed, so that + CHRE knows to load the new nanoapp +* Restart CHRE to reload all nanoapps, including the new one + +## Nanoapp Versioning + +While not strictly enforced, nanoapps are recommended to follow the convention +of the CHRE framework and use [Semantic Versioning](http://semver.org). In the +case of a nanoapp, the key versioned “API” is considered the interface between +the client and nanoapp. Nanoapp versions are represented as a 32-bit integer, +where the most significant byte represents the major version, followed by one +byte for the minor version, and two bytes for the patch version. + +## Using the CHRE API + +The CHRE API is the key interface between each nanoapp and the underlying +system. Refer to the extensive API documentation in the header files at +`chre_api/include`, as well as usage of the APIs by sample nanoapps. The CHRE +API is normally included via `#include <chre.h>`. + +## Utility Libraries + +Some source and header files under `util` are specifically designed to aid in +nanoapp development, and others were initially created for use in the framework +but can be leveraged by nanoapps as well. In general, any source and header file +there that does **not** include a header from `chre/platform` (part of the +internal CHRE framework implementation details) may be used by a nanoapp, and +files within a subdirectory called `nanoapp` are specifically targeted for use +by nanoapps. + +This includes `util/include/chre/util/nanoapp/log.h` (meant to be included via +`#include “chre/util/nanoapp/log.h”`), which provides macros like `LOGD` which +can be conditionally compiled, include a configurable prefix to help identify +the sender, and suppress double promotion warnings. + +The utilities library also includes a number of container classes, which are +meant to mimic the C++ standard library, but with a lightweight, CHRE-compatible +implementation. This includes: + +* `chre::DynamicVector`: an `std::vector` analogue +* `chre::FixedSizeVector`: accessed like `std::vector`, but only uses statically + allocated memory +* `chre::ArrayQueue`: can be used as a circular buffer +* `chre::UniquePtr`: an `std::unique_ptr` analogue +* `chre::Optional`: an analogue to `std::optional` from C++17 +* `chre::Singleton`: a container for a statically allocated object with explicit + initialization and deinitialization (e.g. enables construction of a global + object to be deferred until `nanoappStart()`) + +## Interacting with the Host + +Nanoapps can interact with one or more clients on the host (applications +processor) through a flexible binary message-passing interface. For simple +interactions in cases where the lowest memory footprint is desired, using only +the built-in message type field with no additional payload, or passing +hand-rolled packed C-style structures (e.g. using Java’s ByteBuffer on the +client side) can work, though this approach can be error-prone. Using a +well-defined serialization format, such as Protocol Buffers (see the Using +NanoPB section below) or FlatBuffers, is usually a better choice. + +There are a few common tips to keep in mind when interacting with the host: + +1. Nanoapp binaries are usually updated independently from client code - watch +out for compatibility issues arising from changes to the messaging protocol, and +use a serialization format like Protocol Buffers if possible. + +2. Nanoapp messages to the host always wake it up if it’s asleep. If this is not +required, nanoapps are encouraged to batch their messages and opportunistically +send when the host wakes up for another reason (see +`chreConfigureHostSleepStateEvents()`). + +3. After calling `chreSendMessageToHostEndpoint()`, ownership of the memory +associated with the message payload is assigned to the framework. Do not modify +it until the free callback is invoked. + +4. Nanoapp messaging should be unicast, unless broadcast messaging is strictly +necessary. Design the messaging protocol such that the client initiates +communication, and save the host endpoint ID in the nanoapp to use when sending +replies. + +## Interacting with Other Nanoapps + +While most nanoapps are only concerned with providing functionality for a single +client on the host, it is possible for a nanoapp to provide services to other +nanoapps within CHRE. Similar to how nanoapps communicate with the host by +passing *messages*, nanoapps can communicate with one another by passing +*events* with arbitrary binary payload. Event IDs starting in the range +`CHRE_EVENT_FIRST_USER_VALUE` are reserved for this purpose. + +Typically a nanoapp creates a *nanoapp client library* which other nanoapps can +include, which presents a simple, expressive API, and handles the implementation +details of passing events to the target nanoapp, and interpreting incoming +messages. + +Refer to the functions defined in `chre/event.h` for more details. + +## Using TensorFlow Lite for Microcontrollers + +Many nanoapps use machine learning techniques to accomplish their functionality. +The CHRE build system has built-in support for integrating [TensorFlow Lite for +Microcontrollers](https://www.tensorflow.org/lite/microcontrollers) (TFLM) into +a nanoapp. Sync the TFLM sources, set `TFLM_PATH`, and define `USE_TFLM=true` in +your Makefile - see `apps/tflm_demo/README` for details and an example nanoapp. + +## Using Nanopb + +The CHRE build system has integrated support for using +[Nanopb](https://jpa.kapsi.fi/nanopb/) to provide support for [Protocol +Buffers](https://developers.google.com/protocol-buffers) in a nanoapp. To +integrate this into your nanoapp’s Makefile, first install and configure +dependencies: + +* Sync the Nanopb source tree (e.g. from a release on GitHub), and define the + `NANOPB_PREFIX` environment variable to its path +* Download and install the protobuf compiler `protoc` and make it available in + your `$PATH`, or set the `PROTOC` environment variable + +Then in your nanoapp’s Makefile, populate `NANOPB_SRCS` with the desired +`.proto` file(s). That’s it! Though some additional options/parameters are +available - see `build/nanopb.mk` for details. + +## Nanoapp Development Best Practices + +Even though CHRE aims to provide an environment for low-power and low-latency +contextual signal processing, these two are often conflicting goals. In +addition, CHRE is usually implemented in a resource-constrained environment with +limited memory available. + +As it requires collaboration from all nanoapps to optimize their resource usage +for CHRE to live up to its promises, some best practices are provided here as +guidance for nanoapp development. + +### Memory Efficiency + +#### Avoid dynamic heap allocations where possible + +As CHRE is designed in a resource-constrained environment, there is no guarantee +runtime memory allocation will succeed. In addition, dynamic heap allocations +make it difficult to estimate the memory usage in advance. Developers are +therefore encouraged to use static allocations where possible. + +#### Be careful of stack usage + +Unlike Linux’s default stack of 8MB that scales dynamically, CHRE only has a +fixed stack of limited size (8KB is typical). Ensure you keep any allocations to +an absolute minimum and any large allocations should go out of scope prior to +navigating deeper into a stack. + +#### Prefer in-place algorithms + +Prefer in-place algorithms over out-of-place ones where efficiency allows to +minimize additional memory requirements. + +### Power Efficiency + +#### Be opportunistic when possible + +Examples include: + +* If the host is asleep and doesn’t need to act on a nanoapp message + immediately, buffer until it wakes up for another reason. +* Make a WiFi on-demand scan request only if the WiFi scan monitor doesn’t + provide a scan result in time. + +#### Batch data at the source where possible + +By batching data at the source, it reduces the data delivery frequency and helps +keep CHRE asleep and improve power efficiency. Clients should make data requests +with the longest batch interval that still meets the latency requirement. +Examples include: + +* Make a sensor data request with the longest ``latency`` possible. +* Make an audio data request with the longest ``deliveryInterval`` possible. + +### Standard Library Usage + +CHRE implementations are only required to support a subset of the standard C and +C++ libraries, as well as language features requiring run-time support. This +list is carefully considered to ensure memory usage and implementation +complexity are minimized. Following these principles, some features are +explicitly excluded due to their memory and/or extensive OS-level dependencies, +and others because they are supplanted by more suitable CHRE-specific APIs. +While not meant to be an exhaustive list and some platforms may differ, the +following standard library features are not meant to be used by nanoapps: + +* C++ exceptions and run-time type information (RTTI) +* Standard library multi-threading support, including C++11 headers `<thread>`, + `<mutex>`, `<atomic>`, `<future>`, etc. +* C and C++ Standard Input/Output libraries +* C++ Standard Template Library (STL) +* C++ Standard Regular Expressions library +* Dynamic memory allocation (`malloc`, `calloc`, `realloc`, `free`), and + libraries that inherently use dynamic allocation, such as `std::unique_ptr` +* Localization and Unicode character support +* Date and time libraries +* Functions that modify normal program flow, including `<setjmp.h>`, + `<signal.h>`, `abort`, `std::terminate`, etc. +* Accessing the host environment, including `system`, `getenv`, etc. +* POSIX or other libraries not included in the C99 or C++11 language standards + +In many cases, equivalent functionality is available from CHRE API functions +and/or utility libraries. For example, `chreLog` may be used for debug logging, +where a more traditional program might use `printf`. + +## Debugging + +Similar to the framework debugging methods, each has its nanoapp counterpart to +support nanoapp debugging through the framework. Please see the Framework +Debugging section for reference/context. + +### Logging + +CHRE API `chreLog()` logs information into the system as part of the CHRE logs. +Normally this appears in logcat, but some platforms may route it to a different +logging system (a future version of the CHRE API is expected to make logcat +logging mandatory). + +Nanoapps are encouraged to `#include "chre/util/nanoapp/log.h"` and use the +`LOGx()` macros defined therein, which requires these additional steps: + +* Define `LOG_TAG` to a short, human-readable identifier for your nanoapp, as + this gets prepended to logs +* Define `NANOAPP_MINIMUM_LOG_LEVEL` to a `CHRE_LOG_LEVEL_\*` value in your + Makefile for compile time log level filtering - it’s recommended to use + `CHRE_LOG_LEVEL_DEBUG` for development, and `CHRE_LOG_LEVEL_INFO` for release + +See also the Framework Debugging section for more general guidance on logging in +CHRE. + +### Debug Dump + +When running on CHRE v1.4+, nanoapps can also append information to the CHRE +framework debug dump. Nanoapps interested in using this capability should call +`chreConfigureDebugDumpEvent(true)` in `nanoappStart()`, then when +`CHRE_EVENT_DEBUG_DUMP` is received in `nanoappHandleEvent()`, use +`chreDebugDumpLog()` to write human-readable output to the debug dump, which +appears in bug reports under the Context Hub HAL debug section. In the reference +CHRE framework implementation, nanoapp debug dumps have the nanoapp name and ID +automatically prepended, for example: + +``` +Nanoapp debug dumps: + + DebugDumpWorld 0x0123456789000011: + Debug event count: 2 + Total dwell time: 92 us +``` + +Refer to the associated CHRE API documentation and Framework Debugging section +for more information. + +### CHRE_ASSERT + +To help catch programming errors or other unexpected conditions, nanoapps can +use the `CHRE_ASSERT` macro provided by `#include "chre/util/nanoapp/assert.h"`. +Keep in mind that if one nanoapp encounters an assertion failure, it most likely +will cause a reset of the processor where CHRE is running, impacting other +functionality (though this can vary by platform). Therefore, assertions are only +recommended to be used during development. Define the `CHRE_ASSERTIONS_ENABLED` +variable in your Makefile to `false` to disable assertions at compile time. diff --git a/doc/nanoapp_overview.md b/doc/nanoapp_overview.md new file mode 100644 index 00000000..774356f1 --- /dev/null +++ b/doc/nanoapp_overview.md @@ -0,0 +1,199 @@ +*** aside +See also: [Nanoapp Developer Guide](/doc/nanoapp_developer_guide.md) | +[Interacting with Nanoapps](/doc/nanoapp_clients.md) +*** + +# Nanoapp Overview + +[TOC] + +Nanoapps are applications written in C or C++ which run in CHRE, to leverage +low-power hardware. Typically, nanoapps integrate with a component on the +Android side, such as a privileged APK, in order to provide complete end-to-end +functionality of the target feature. This Android-side component is referred to +as the nanoapp’s “client”. Since nanoapps are not limited to the same power +constraints that Android apps are, they can process inputs, like sensor data, +much more frequently while the device’s screen is off. + +Nanoapps are abstracted from the underlying platform details by the CHRE API, +which is standardized across all CHRE implementations. This means that a nanoapp +is *code compatible* across devices - its source code does not need to be +changed to run on different hardware, but it *may* need to be recompiled. The +CHRE API also provides binary compatibility guarantees across minor versions, so +a nanoapp does not need to be recompiled to run on a device that exposes a newer +or older version of the CHRE API. These properties help provide for the maximum +reuse of code across devices. + +Due to system security and resource constraints of the platforms that CHRE +targets, only device OEMs and their trusted partners are able to create +nanoapps. In other words, the system only runs nanoapps that possess a digital +signature that is trusted in advance by the device manufacturer, and APKs must +hold a special privileged/same-signature permission (`ACCESS_CONTEXT_HUB`) to be +able to interact with nanoapps and the Context Hub in general. However, this +does not mean that third-party APKs cannot benefit from CHRE - nanoapps can be +used to power APIs available for use by any Android app. + +## Methods for Loading a Nanoapp + +While nanoapps are nominally dynamically loadable modules, they can be loaded +into a device through a few methods, each of which has pros and cons elaborated +below. + +### Static Nanoapps + +Static nanoapps are, as the name suggests, statically compiled into the CHRE +framework binary. Static nanoapps are automatically initialized after the CHRE +framework completes its initialization during the boot process. + +Static nanoapps typically aren’t used in production, because this monolithic +approach has downsides in terms of version control, updatability, etc., but it +can be useful during CHRE development and bring-up of new devices, especially +before dynamic loading functionality is enabled. For example, the FeatureWorld +nanoapps (described later) are typically built as static nanoapps. + +Static nanoapps are typically unconditionally compiled as part of the framework +build (via `apps/apps.mk`), but then stripped out by the linker if unreferenced +(using the `--gc-sections` option, or equivalent). Static nanoapps are +referenced only if their initialization function appears in the +`kStaticNanoappList` array, which by default is empty, but can be overridden by +the device variant makefile, as in `variant/simulator/` for example. + +Some boilerplate is needed to enable nanoapp to be built as a static nanoapp - +see the code wrapped in `#ifdef CHRE_NANOAPP_INTERNAL` in +`apps/hello_world/hello_world.cc`. + +### Preloaded Nanoapps + +Preloaded nanoapps are built as a separate binary from the CHRE framework, but +included in the vendor partition of an overall device image (hence they are +“preloaded” onto the device). The binaries associated with a preloaded nanoapp +(usually a `.so` and a `.napp_header` file) are checked in to the Android tree +as a “prebuilt” binary, and integrated into the Android build system so as to +appear in the resulting device image, for example using `$(BUILD_PREBUILT)` in +Android.mk, or `prebuilt_dsp` or `prebuilt_firmware` in Android.bp. + +While the mechanism for loading prebuilt nanoapps is platform-specific, the CHRE +framework generally follows these steps at boot time: + +1. When CHRE starts up, the CHRE daemon process running on the AP reads the + configuration file `/vendor/etc/chre/preloaded_nanoapps.json`, which contains + the list of nanoapps that should be automatically loaded. + +2. For each nanoapp in the JSON file, the CHRE daemon reads the `.napp_header` + from storage, and sends a message to CHRE requesting it to load the nanoapp. + +3. The platform layer of the CHRE framework handles the requests by loading, + authenticating, linking, and starting the nanoapp. + +4. CHRE initialization proceeds (it is important for all preloaded nanoapps to + be included at the first moment list query command can be processed, to + avoid race conditions leading to clients believing that a preloaded nanoapp + is missing). + +This path is most commonly used to deploy nanoapps to production, as the entire +device software can be validated together without external dependencies, while +also preserving the ability to update nanoapps independent from other components +in the system. + +### Fully Dynamic Nanoapps + +At the binary level, a preloaded nanoapp and fully dynamic nanoapp are +identical. The key difference is where they are stored and how they are +initially loaded into CHRE, and potentially how metadata is handled. In most +cases, preloaded nanoapps will use a separate `.napp_header` file with metadata +and `.so` file for the actual binary, a fully dynamic nanoapp has the header +prepended to the binary, and carries the `.napp` file type suffix. In other +words, the command `cat my_nanoapp.napp_header my_nanoapp.so > my_nanoapp.napp` +can be used to create a fully dynamic nanoapp file from these components. + +Instead of being stored on the device filesystem, fully dynamic nanoapps can be +loaded at any time after initialization using the +`ContextHubManager.loadNanoApp()` Java API. This allows nanoapps to be +updated/delivered by an APK, outside of a full Android system update (OTA). + +This mechanism is used to dynamically load and unload test nanoapps, but can +also be used for production nanoapps. + +## Other Nanoapp Types + +Some platforms support loading nanoapps into multiple tiers of memory, for +example low-power tightly coupled memory (TCM, usually SRAM), versus a +higher-power but higher-capacity memory bank (such as DRAM). This distinction is +normally made at the build target variant level. + +CHRE also supports the concept of a *system nanoapp*, which is a nanoapp whose +purpose is to accomplish some low-level, device-specific functionality that is +purely beneath the HAL level. System nanoapps are therefore hidden from the +nanoapp list at the HAL. This property is controlled by setting the +`NANOAPP_IS_SYSTEM_NANOAPP` variable in the nanoapp Makefile. + +## Example AOSP Nanoapps + +Some basic nanoapps can be found in the `apps/` folder, which are used for test +purposes, as well as to demonstrate how to use the CHRE APIs. + +### FeatureWorld Nanoapps + +The *FeatureWorld* nanoapps each exercise a part of the CHRE API, and print +results/output to `chreLog`. An overview of a few of the key FeatureWorld +nanoapps is given below: + +* `hello_world`: While not technically a FeatureWorld nanoapp, it’s generally + the first nanoapp to be tried, and it simply outputs a log message when it + starts and ends, and upon any event received. + +* `message_world`: Exercises host messaging functionality. Typically used in + conjunction with `host/common/test/chre_test_client.cc` (see + `sendMessageToNanoapp()` in that file). + +* `sensor_world`: Enables sensors and prints the samples it receives. This + nanoapp is typically customized prior to executing, for example to control + which sensors it will enable. It also supports a “break it” mode which + stresses the system by enabling/disabling sensors frequently. + +* `host_awake_world`: Used to help validate functionality used for + opportunistically sending messages to the AP when it is awake. + +### Stress Test Nanoapps + +These nanoapps help stress test the CHRE framework. They include: + +* `audio_stress_test`: Repeatedly enables and disables an audio source, + verifying that it continues to provide data as expected. + +* `sensor_world`: Contains a “break it” mode which repeatedly enables, disables, + and reconfigures sensors. + +* `spammer`: Sends a constant stream of messages and events to stress test the + queueing system. + +* `unload_tester`: Used in conjunction with the spammer nanoapp to verify that + unloading a nanoapp with pending events/messages completes successfully. Note + that this nanoapp references internal framework functions (e.g. + `EventLoopManager::deferCallback()`) to accomplish its functionality, which is + generally only permissible for testing purposes. + +### Power Test + +The `power_test` nanoapp is intended to be used in conjunction with special +hardware that directly measures the power usage of the system and/or its +components. This nanoapp is intended to be used with its host-side client, +`chre_power_test_client`, to create some activity at the CHRE API level which +can then be measured. For example, running `./chre_power_test_client wifi enable +5000000000` will configure the `power_test` nanoapp to request a WiFi scan every +5 seconds - the power monitoring equipment can then be used to determine the +power cost of performing a WiFi scan from CHRE. Typically this is done after +unloading all other nanoapps in the system (which can be done via +`./chre_power_test_client unloadall`), and disabling all other functionality, to +get a clean power trace of purely the functionality exercised by the +`power_test` nanoapp. + +Refer to `chre_power_test_client.cc` for more details, including a full listing +of all supported commands. + +### Nanoapps Used with Java-based Test Suites + +Nanoapps under `apps/test` are associated with a test suite, for example Context +Hub Qualification Test Suite (CHQTS), which is used to test that a given device +upholds the requirements of the CHRE API. Much of the host-side Java code +associated with these nanoapps can be found in the `java/` folder. diff --git a/doc/porting_guide.md b/doc/porting_guide.md new file mode 100644 index 00000000..72d3b1d9 --- /dev/null +++ b/doc/porting_guide.md @@ -0,0 +1,253 @@ +# CHRE Framework Porting Guide + +[TOC] + +CHRE achieves portability and extensibility across platforms by defining +interfaces that the platform needs to implement. These interfaces provide +dependencies for the common CHRE code that are necessarily platform-specific. +Additionally, platform code calls into common code to ferry events from +underlying subsystems to nanoapps. + +This section gives an overview of the steps one should take to add support for a +new platform in the CHRE reference implementation. + +## Directory Structure + +CHRE platform code can be broadly categorized as follows. + +### Platform Interfaces + +Files under `platform/include` serve as the interface between common code in +`core/` and other platform-specific code in `platform/<platform_name>`. These +files are considered common and should not be modified for the sake of +supporting an individual platform. + +### Shared Platform Code + +Located in `platform/shared/`, the code here is part of the platform layer’s +responsibilities, but is not necessarily specific to only one platform. In other +words, this code is likely to be re-used by multiple platforms, but it is not +strictly necessary for a given platform to use it. + +### Platform-specific Code + +Files under `platform/<platform_name>` are specific to the underlying software +of a given platform, for example the APIs which are used to access functionality +like sensors, the operating system, etc. To permit code reuse, the CHRE platform +layer for a given device may be composed of files from multiple +`<platform_name>` folders, for example if the same sensor framework is supported +across multiple OSes, there may be one folder that provides the components that +are specific to just the OS. + +Platform-specific code can be further subdivided into: + +* **Source files**: normally included at + `platform/<platform_name>/<file_name>.cc`, but may appear in a subdirectory + +* **Headers which are includable by common code**: these are placed at + `platform/<platform_name>/include/chre/target_platform/<file_name>.h`, and are + included by *Platform Interfaces* found in `platform/include` and provide + inline or base class definitions, such as `mutex_base_impl.h` and + `platform_sensor_base.h` respectively, or required macros + +* **Fully platform-specific headers**: these typically appear at + `platform/<platform_name>/include/chre/platform/<platform_name/<file_name>.h` + and may only be included by other platform-specific code + +## Open Sourcing + +Partners who add support for a new platform are recommended to upstream their +code to +[AOSP](https://source.android.com/setup/contribute#contribute-to-the-code). +This helps ensure that details of your platform are considered by the team that +maintains the core framework, so any changes that break compilation are +addressed in a timely fashion, and enables you to receive useful code review +feedback to improve the quality of your CHRE implementation. Please reach out +via your TAM to help organize this effort. + +If some parts of a platform’s CHRE implementation must be kept closed source, +then it is recommended to be kept in a separate Git project (under vendor/ in +the Android tree). This vendor-private code can be integrated with the main CHRE +build system through the `CHRE_VARIANT_MK_INCLUDES` variable. See the build +system documentation for more details. + +## Recommended Steps for Porting CHRE + +When starting to add support for a new platform in the CHRE framework, it’s +recommended to break the task into manageable chunks, to progressively add more +functionality until the full desired feature set is achieved. An existing +platform implementation can be referenced to create empty stubs, and then +proceed to add implementations piece by piece, testing along the way. + +CHRE provides various test nanoapps in `apps/` that exercise a particular +feature that the platform provides. These are selectively compiled into the +firmware statically via a `static_nanoapps.cc` source file. + +With this in mind, it is recommended to follow this general approach: + +1. Create a new platform with only empty stubs, with optional features (like + `CHRE_GNSS_SUPPORT_ENABLED`) disabled at build-time + +2. Work on updating the build system to add a new build target and achieve + successful compilation and linking (see the build system documentation for + details) + +3. Implement base primitives from `platform/include`, including support for + mutexes, condition variables, atomics, time, timers, memory allocation, and + logging + +4. Add initialization code to start the CHRE thread + +5. Add static nanoapp initialization support (usually this is just a copy/paste + from another platform) + +6. Confirm that the ‘hello world’ nanoapp produces the expected log message (if + it does, huzzah!) + +7. Complete any remaining primitives, like assert + +8. Implement host link and the daemon/HAL on the host (AP) side, and validate it + with a combination of the message world nanoapp and the host-side test code + found at `host/common/test/chre_test_client.cc` + +At this stage, the core functionality has been enabled, and further steps should +include enabling dynamic loading (described in its own section below), and the +desired optional feature areas, like sensors (potentially via their respective +PALs, described in the next section). + +## Implementing the Context Hub HAL + +The Context Hub HAL (found in the Android tree under +`hardware/interfaces/contexthub`) defines the interface between Android and the +underlying CHRE implementation, but as CHRE is implemented on a different +processor from the HAL, the HAL is mostly responsible for relaying messages to +CHRE. This project includes an implementation of the Context Hub HAL under +`host/hal_generic` which pairs with the CHRE framework reference implementation. +It converts between HAL API calls and serialized flatbuffers messages, using the +host messaging protocol defined under `platform/shared` (platform +implementations are able to choose a different protocol if desired, but would +require a new HAL implementation), and passes the messages to and from the CHRE +daemon over a socket. The CHRE daemon is in turn responsible for communicating +directly with CHRE, including common functionality like relaying messages to and +from nanoapps, as well as device-specific functionality as needed. Some examples +of CHRE functionality that are typically implemented with support from the CHRE +daemon include: + +* Loading preloaded nanoapps at startup +* Passing log messages from CHRE into Android logcat +* Determining the offset between `chreGetTime()` and Android’s + `SystemClock.elapsedRealtimeNanos()` for use with + `chreGetEstimatedHostTimeOffset()` +* Coordination with the SoundTrigger HAL for audio functionality +* Exposing CHRE functionality to other vendor-specific components (e.g. via + `chre::SocketClient`) + +When adding support for a new platform, a new HAL implementation and/or daemon +implementation on the host side may be required. Refer to code in the `host/` +directory for examples. + +## Implementing Optional Feature Areas (e.g. PALs) + +CHRE provides groups of functionality called *feature areas* which are +considered optional from the perspective of the CHRE API, but may be required to +support a desired nanoapp. CHRE feature areas include sensors, GNSS, audio, and +others. There are two ways by which this functionality can be exposed to the +common CHRE framework code: via the `Platform<Module>` C++ classes, or the C PAL +(Platform Abstraction Layer) APIs. It may not be necessary to implement all of +the available feature areas, and they can instead be disabled if they won’t be +implemented. + +The Platform C++ Classes and PAL APIs have extensive documentation in their +header files, including details on requirements. Please refer to the headers for +precise implementation details. + +### Platform C++ Classes vs. PAL APIs + +Each feature area includes one or more `Platform<Module>` classes which the +common framework code directly interacts with. These classes may be directly +implemented to provide the given functionality, or the shim to the PAL APIs +included in the `shared` platform directory may be used. PALs provide a C API +which is suitable for use as a binary interface, for example between two dynamic +modules/libraries, and it also allows for the main CHRE to platform-specific +translation to be implemented in C, which may be preferable in some cases. + +Note that the PAL APIs are binary-stable, in that it’s possible for the CHRE +framework to work with a module that implements a different minor version of the +PAL API, full backwards compatibility (newer CHRE framework to older PAL) is not +guaranteed, and may not be possible due to behavioral changes in the CHRE API. +While it is possible for a PAL implementation to simultaneously support multiple +versions of the PAL API, it is generally recommended to ensure the PAL API +version matches between the framework and PAL module, unless the source control +benefits of a common PAL binary are desired. + +This level of compatibility is not provided for the C++ `Platform<Module>` +classes, as the CHRE framework may introduce changes that break compilation. If +a platform implementation is included in AOSP, then it is possible for the +potential impact to be evaluated and addressed early. + +### Disabling Feature Areas + +If a feature area is not supported, setting the make variable +`CHRE_<name>_SUPPORT_ENABLED` to false in the variant makefile will avoid +inclusion of common code for that feature area. Note that it must still be +possible for the associated CHRE APIs to be called by nanoapps without crashing +- these functions must return an appropriate response indicating the lack of + support (refer to `platform/shared/chre_api_<name>.cc` for examples). + +### Implementing Platform C++ Classes + +As described in the CHRE Framework Overview section, CHRE abstracts common code +from platform-specific code at compile time by inheriting through +`Platform<Module>` and `Platform<Module>Base` classes. Platform-specific code +may retrieve a reference to other objects in CHRE via +`EventLoopManagerSingleton::get()`, which returns a pointer to the +`EventLoopManager` object which contains all core system state. Refer to the +`Platform<Module>` header file found in `platform/include`, and implementation +examples from other platforms for further details. + +### Implementing PALs + +PAL implementations must only use the callback and system APIs provided in +`open()` to call into the CHRE framework, as the other functions in the CHRE +framework do not have a stable API. + +If a PAL implementation is provided as a dynamic module in binary form, it can +be linked into the CHRE framework at build time by adding it to +`TARGET_SO_LATE_LIBS` in the build variant’s makefile - see the build system +documentation for more details. + +### PAL Verification + +There are several ways to test the PAL implementation beyond manual testing. +Some of them are listed below in increasing order of the amount of checks run by +the tests. + +1. Use the FeatureWorld apps provided under the `apps` directory to exercise +the various PAL APIs and verify the CHRE API requirements are being met + +2. Assuming the platform PAL implementations utilize CHPP and can communicate +from the host machine to the target chipset, execute `run_pal_impl_tests.sh` to +run basic sanity checks on the PAL + +3. Execute tests (see Testing section for details) + +## Dynamic Loading Support + +CHRE requires support for runtime loading and unloading of nanoapp binaries. +There are several advantages to this approach: + +* Decouples nanoapp binaries from the underlying system - can maintain and + deploy a single nanoapp binary across multiple devices, even if they support + different versions of Android or the CHRE API + +* Makes it possible to update nanoapps without requiring a system reboot, + particularly on platforms where CHRE runs as part of a statically compiled + firmware + +* Enables advanced capabilities, like staged rollouts and targeted A/B testing + +While dynamic loading is a responsibility of the platform implementation and may +already be a part of the underlying OS/system capabilities, the CHRE team is +working on a reference implementation for a future release. Please reach out via +your TAM if you are interested in integrating this reference code prior to its +public release. diff --git a/doc/vendor_extensions.md b/doc/vendor_extensions.md new file mode 100644 index 00000000..68c21367 --- /dev/null +++ b/doc/vendor_extensions.md @@ -0,0 +1,135 @@ +# 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. diff --git a/navbar.md b/navbar.md new file mode 100644 index 00000000..82d0d3f6 --- /dev/null +++ b/navbar.md @@ -0,0 +1,3 @@ +* [Home](/README.md) +* [Framework Docs](/doc/framework_overview.md) +* [Nanoapp Docs](/doc/nanoapp_overview.md) |