# 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/`.  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/` 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
`` 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//.cc`, but may appear in a subdirectory

* **Headers which are includable by common code**: these are placed at
  `platform//include/chre/target_platform/.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//include/chre/platform/.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` 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` 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`
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__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_.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` and `PlatformBase` 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` 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 consistency 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.