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.
CHRE platform code can be broadly categorized as follows.
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.
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/<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
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
Partners who add support for a new platform are recommended to upstream their code to AOSP. 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.
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:
Create a new platform with only empty stubs, with optional features (like
CHRE_GNSS_SUPPORT_ENABLED) disabled at build-time
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)
Implement base primitives from
platform/include, including support for mutexes, condition variables, atomics, time, timers, memory allocation, and logging
Add initialization code to start the CHRE thread
Add static nanoapp initialization support (usually this is just a copy/paste from another platform)
Confirm that the ‘hello world’ nanoapp produces the expected log message (if it does, huzzah!)
Complete any remaining primitives, like assert
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
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).
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:
SystemClock.elapsedRealtimeNanos()for use with
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.
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.
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.
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
As described in the CHRE Framework Overview section, CHRE abstracts common code from platform-specific code at compile time by inheriting through
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.
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.
While there may be minor differences, most CHRE PAL API implementations follow the following pattern:
CHRE provides a standardized structure containing various interfaces for calling into the vendor's closed source code in pal/.h. These functions must be implemented by the platform, and provided to CHRE when a call to chrePalGetApi function is called. Functions to be implemented are of two broad categories:
CHRE provides feature specific callbacks (see 2. below) to the PAL by invoking the _open()_ function in the Module API provided above. The structures returned by this function call must be stored somewhere by the PAL, and used as necessary to call into the CHRE core. Typically, one or more of these callbacks need to be invoked in reponse to a request from CHRE using the Module API provided above. The _close()_ function, when invoked by CHRE, indicates that CHRE is shutting down. It is now the PAL's responsibility to perform cleanup, cancel active requests, and not invoke any CHRE callbacks from this point on.
These are interfaces in the PAL API that are module specific, and used by CHRE to call into the vendor PAL code.
CHRE provides a set of module specific callbacks by invoking the open() access function provided by the Module API. It then starts performing control or data requests as needed, by invoking the request functions in the Module API. The PAL is expected to process these requests, and invoke the appropriate CHRE provided callback in response. Note that some callbacks might require memory ownership to be held until explicitly released. For example, upon an audio request from CHRE via a call to the
requestAudioDataEvent audio PAL API, the platform might invoke the
audioDataEventCallback when the audio data is ready and packaged approapriately into a
chreAudioDataEvent structure. The platform must ensure that the memory associated with this data structure remains in context until CHRE explicitly releases it via a call to
Please refer to the appropriate PAL documentation to ensure that such dependencies are understood early in the development phase.
Please refer to the various reference implementations that are available for CHRE PALS for the Linux platform. Note that this implementation is meant to highlight the usage of the PAL APIs and callbacks, and might not necessarily port directly over into a resource constrained embedded context.
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.
Use the FeatureWorld apps provided under the
apps directory to exercise the various PAL APIs and verify the CHRE API requirements are being met
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 consistency checks on the PAL
Execute tests (see Testing section for details)
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.
Once you have implemented the necessary pieces described previously, you are now ready to add the Context Hub support on the device! Here are the necessary steps to do this:
Add the build target of the Context Hub HAL implementation to your device .mk file. For example, if the default generic Context Hub HAL is being used, you can add the following:
PRODUCT_PACKAGES += \ android.hardware.contexthub-service.generic
Currently, the generic Context Hub HAL relies on the CHRE daemon to communicate with CHRE. If you are using one of our existing platforms, you can add one of the following CHRE daemon build targets to your PRODUCT_PACKAGES as you did the generic HAL above.
Otherwise, you can look at those target definitions to define a new one for your specific platform.
Resolve any missing SElinux violations by using the relevant tools such as audit2allow, and updating the SElinux policies for your device. You may follow the directions in the official Android page for additional guidance.
Add the following in your device.mk file:
PRODUCT_COPY_FILES += \ frameworks/native/data/etc/android.hardware.context_hub.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.context_hub.xml
The above change will enable the Context Hub Service on the device, and expects that the Context Hub HAL comes up. If (1) and (2) are not performed, the device may fail to boot to the Android home screen.