Merge 25Q1 (ab/12770256) to aosp-main-future

Bug: 385190204
Merged-In: Ie2744a58c02b2ac5513583daa324e7a9891a26b0
Change-Id: I1535dc493f7ea63f594540ea33c857e28ce2d39c
diff --git a/Android.bp b/Android.bp
index 78a78ca..49fe6f1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,6 +56,72 @@
     ],
 }
 
+filegroup {
+    name: "contexthub_hal_common_srcs",
+    srcs: [
+        "host/common/file_stream.cc",
+        "host/common/fragmented_load_transaction.cc",
+        "host/common/host_protocol_host.cc",
+        "host/common/log.cc",
+        "host/hal_generic/common/context_hub_v4_impl.cc",
+        "host/hal_generic/common/message_hub_manager.cc",
+        "platform/shared/host_protocol_common.cc",
+    ],
+}
+
+filegroup {
+    name: "contexthub_hal_client_srcs",
+    srcs: [
+        "host/common/hal_client.cc",
+        "host/common/pigweed/hal_channel_output.cc",
+        "host/common/pigweed/hal_rpc_client.cc",
+        "host/common/socket_client.cc",
+    ],
+}
+
+cc_defaults {
+    name: "contexthub_hal_defaults",
+    vendor: true,
+    srcs: [":contexthub_hal_common_srcs"],
+    include_dirs: [
+        "system/chre/host/common/include/",
+        "system/chre/host/hal_generic/aidl/",
+        "system/chre/host/hal_generic/common/",
+        "system/chre/platform/shared/include/",
+        "system/chre/util/include/",
+    ],
+    header_libs: [
+        "chre_api",
+        "chre_flatbuffers",
+    ],
+    shared_libs: [
+        "android.frameworks.stats-V2-ndk",
+        "android.hardware.contexthub-V4-ndk",
+        "chre_atoms_log",
+        "chremetrics-cpp",
+        "libaconfig_storage_read_api_cc",
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "libjsoncpp",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libutils",
+        "server_configurable_flags",
+    ],
+    static_libs: [
+        "chre_flags_c_lib",
+        "chre_metrics_reporter",
+        "event_logger",
+    ],
+    cflags: [
+        "-DCHRE_IS_HOST_BUILD",
+        "-DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4000", // Needed to import CHRE APIs
+        "-Wall",
+        "-Werror",
+    ],
+}
+
 cc_library_static {
     name: "chre_client",
     vendor_available: true,
@@ -469,7 +535,7 @@
         "util/include",
     ],
     shared_libs: [
-        "android.hardware.contexthub-V3-ndk",
+        "android.hardware.contexthub-V4-ndk",
         "libcutils",
         "libutils",
     ],
@@ -528,7 +594,7 @@
         "util/include/",
     ],
     static_libs: [
-        "android.hardware.contexthub-V3-ndk",
+        "android.hardware.contexthub-V4-ndk",
         "chre_flags_c_lib",
         "chre_host_common",
         "event_logger",
@@ -537,7 +603,7 @@
     ],
     shared_libs: [
         "android.frameworks.stats-V2-ndk",
-        "android.hardware.contexthub-V3-ndk",
+        "android.hardware.contexthub-V4-ndk",
         "chre_atoms_log",
         "libaconfig_storage_read_api_cc",
         "libbase",
@@ -715,20 +781,17 @@
         "platform/shared/pw_trace/include",
         "util/include",
     ],
-    cflags: [
-        "-DCHRE_ASSERTIONS_ENABLED=true",
-        "-DCHRE_BLE_SUPPORT_ENABLED=true",
-        "-DCHRE_FILENAME=__FILE__",
-        "-DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4096",
-        "-DCHRE_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_DEBUG",
-        "-DGTEST",
-    ],
     header_libs: [
         "chre_flatbuffers",
     ],
     static_libs: [
         "chre_linux",
         "libgmock",
+        "pw_allocator",
+        "pw_containers",
+    ],
+    defaults: [
+        "chre_linux_cflags",
     ],
     sanitize: {
         address: true,
@@ -749,27 +812,55 @@
 // Lib dependencies for apps and libs using PW_RPC with nanopb.
 cc_defaults {
     name: "pw_rpc_nanopb_lib_dependencies",
-    defaults: [
-        "pw_android_common_backends",
-    ],
     static_libs: [
+        "pw_assert_log",
+        "pw_chrono_stl",
         "pw_containers",
+        "pw_log_android",
         "pw_protobuf",
         "pw_rpc_chre",
         "pw_rpc_nanopb_chre",
         "pw_status",
         "pw_stream",
+        "pw_sync_stl",
+        "pw_thread_stl",
         "pw_varint",
     ],
+    export_static_lib_headers: [
+        "pw_assert_log",
+        "pw_chrono_stl",
+        "pw_log_android",
+        "pw_sync_stl",
+        "pw_thread_stl",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
 }
 
 cc_library_static {
     name: "pw_rpc_chre",
     defaults: [
-        "pw_android_common_backends",
         "pw_rpc_cflags_chre",
         "pw_rpc_defaults",
     ],
+    static_libs: [
+        "pw_assert_log",
+        "pw_chrono_stl",
+        "pw_log_android",
+        "pw_sync_stl",
+        "pw_thread_stl",
+    ],
+    export_static_lib_headers: [
+        "pw_assert_log",
+        "pw_chrono_stl",
+        "pw_log_android",
+        "pw_sync_stl",
+        "pw_thread_stl",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
     host_supported: true,
     vendor_available: true,
 }
@@ -857,12 +948,19 @@
     ],
     local_include_dirs: [
         "platform/shared",
+        "platform/shared/public_platform_ble_pal",
+        "platform/shared/public_platform_debug_dump_manager",
+        "platform/shared/public_platform_gnss_pal",
+        "platform/shared/public_platform_wifi_pal",
+        "platform/shared/public_platform_wwan_pal",
         "test/simulation/inc",
     ],
     static_libs: [
         "chre_linux",
         "chre_pal_linux",
         "libprotobuf-c-nano",
+        "pw_allocator",
+        "pw_containers",
     ],
     defaults: [
         "chre_linux_cflags",
@@ -882,6 +980,7 @@
         "core/ble_request.cc",
         "core/ble_request_manager.cc",
         "core/ble_request_multiplexer.cc",
+        "core/chre_message_hub_manager.cc",
         "core/debug_dump_manager.cc",
         "core/event.cc",
         "core/event_loop.cc",
@@ -960,6 +1059,11 @@
         "platform/linux/include",
         "platform/shared/audio_pal/include",
         "platform/shared/include",
+        "platform/shared/public_platform_ble_pal",
+        "platform/shared/public_platform_debug_dump_manager",
+        "platform/shared/public_platform_gnss_pal",
+        "platform/shared/public_platform_wifi_pal",
+        "platform/shared/public_platform_wwan_pal",
         "platform/shared/sensor_pal/include",
         "util/include",
     ],
@@ -974,6 +1078,7 @@
     static_libs: [
         "libgmock",
         "libgtest",
+        "pw_allocator",
         "pw_rpc_chre",
     ],
     host_supported: true,
@@ -989,8 +1094,10 @@
         "-DCHRE_FIRST_SUPPORTED_API_VERSION=CHRE_API_VERSION_1_1",
         "-DCHRE_GNSS_SUPPORT_ENABLED",
         "-DCHRE_LARGE_PAYLOAD_MAX_SIZE=32000",
+        "-DCHRE_MESSAGE_ROUTER_SUPPORT_ENABLED",
         "-DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4096",
         "-DCHRE_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_DEBUG",
+        "-DCHRE_PLATFORM_ID=1",
         "-DCHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED",
         "-DCHRE_SENSORS_SUPPORT_ENABLED",
         "-DCHRE_TEST_ASYNC_RESULT_TIMEOUT_NS=300000000",
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..c43fb9d
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,23 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+add_subdirectory(chre_api)
+add_subdirectory(core)
+add_subdirectory(pal)
+add_subdirectory(platform)
+add_subdirectory(util)
+add_subdirectory(variant)
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+if(NOT chre_third_party_flatbuffers_LIBRARY)
+  pw_add_error_target(chre_third_party.flatbuffers
+    MESSAGE
+      "Attempted to build chre_third_party.flatbuffers without configuring it "
+      "via chre_third_party_flatbuffers_LIBRARY."
+  )
+else()
+  pw_add_library(chre_third_party.flatbuffers INTERFACE
+    PUBLIC_DEPS
+      "${chre_third_party_flatbuffers_LIBRARY}"
+  )
+endif()
diff --git a/apps/ble_world/ble_world.cc b/apps/ble_world/ble_world.cc
index 9251d87..19b1df5 100644
--- a/apps/ble_world/ble_world.cc
+++ b/apps/ble_world/ble_world.cc
@@ -23,6 +23,7 @@
 
 #define BLE_FILTER_TYPE_SERVICE_DATA 0
 #define BLE_FILTER_TYPE_MANUFACTURER_DATA 1
+#define BLE_FILTER_TYPE_BROADCASTER_ADDRESS 2
 
 /**
  * @file
@@ -42,8 +43,9 @@
  *
  * The BLE scanning test can also be configured by filter type. By default, the
  * test will filter by service data, but it can be modified to filter by
- * manufacturer data by setting the BLE_FILTER_TYPE flag to
- * BLE_FILTER_TYPE_MANUFACTURER_DATA. It is recommended to use an app that can
+ * manufacturer data or broadcaster address by setting the BLE_FILTER_TYPE flag
+ * to either BLE_FILTER_TYPE_MANUFACTURER_DATA or
+ * BLE_FILTER_TYPE_BROADCASTER_ADDRESS. It is recommended to use an app that can
  * create advertisers corresponding to the filters to do the tests.
  */
 
@@ -52,6 +54,7 @@
 namespace {
 #endif  // CHRE_NANOAPP_INTERNAL
 
+using chre::ble_constants::kNumBroadcasterFilters;
 using chre::ble_constants::kNumManufacturerDataFilters;
 using chre::ble_constants::kNumScanFilters;
 
@@ -96,6 +99,12 @@
     LOGE("BLE manufacturer data filters are not supported");
     return false;
   }
+#elif BLE_FILTER_TYPE == BLE_FILTER_TYPE_BROADCASTER_ADDRESS
+  if ((filterCapabilities & CHRE_BLE_FILTER_CAPABILITIES_BROADCASTER_ADDRESS) ==
+      0) {
+    LOGE("BLE broadcaster address filters are not supported");
+    return false;
+  }
 #else
   if ((filterCapabilities & CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA) == 0) {
     LOGE("BLE service data filters are not supported");
@@ -111,10 +120,18 @@
   chreBleGenericFilter genericFilters[kNumManufacturerDataFilters];
   chre::createBleManufacturerDataFilter(kNumManufacturerDataFilters,
                                         genericFilters, filter);
+#elif BLE_FILTER_TYPE == BLE_FILTER_TYPE_BROADCASTER_ADDRESS
+  chreBleBroadcasterAddressFilter broadcasterFilters[kNumBroadcasterFilters];
+  if (!chre::createBleScanFilterForAdvertiser(filter, broadcasterFilters,
+                                              kNumBroadcasterFilters)) {
+    LOGE("Failed to create BLE scan filters for known beacons and advertiser");
+  }
 #else
   chreBleGenericFilter genericFilters[kNumScanFilters];
-  chre::createBleScanFilterForKnownBeaconsV1_9(filter, genericFilters,
-                                               kNumScanFilters);
+  if (!chre::createBleScanFilterForKnownBeaconsV1_9(filter, genericFilters,
+                                                    kNumScanFilters)) {
+    LOGE("Failed to create BLE scan filters for known beacons");
+  }
 #endif
   return chreBleStartScanAsyncV1_9(CHRE_BLE_SCAN_MODE_BACKGROUND,
                                    gBleBatchDurationMs, &filter, &kScanCookie);
@@ -181,26 +198,27 @@
   return static_cast<uint16_t>(data[0] + (data[1] << 8));
 }
 
-void parseAdData(const uint8_t *data, uint16_t size) {
-  for (uint16_t i = 0; i < size;) {
-    // First byte has the dvertisement data length.
-    uint16_t adDataLength = data[i];
+void parseReport(const chreBleAdvertisingReport *report) {
+  for (uint16_t i = 0; i < report->dataLength;) {
+    // First byte has the advertisement data length.
+    uint16_t adDataLength = report->data[i];
     // Early termination with zero length advertisement.
     if (adDataLength == 0) break;
+
     // Log 2 byte UUIDs for service data or manufacturer data AD types.
     if (adDataLength < kUuidLengthInBytes) {
       i += adDataLength + 1;
       continue;
     }
-    uint8_t adDataType = data[++i];
+    uint8_t adDataType = report->data[++i];
     switch (adDataType) {
       case kDataTypeServiceData:
         LOGD("Service Data UUID: %" PRIx16,
-             getUuidInLittleEndian(&data[i + 1]));
+             getUuidInLittleEndian(&report->data[i + 1]));
         break;
       case kDataTypeManufacturerData:
         LOGD("Manufacturer Data UUID: %" PRIx16,
-             getUuidInLittleEndian(&data[i + 1]));
+             getUuidInLittleEndian(&report->data[i + 1]));
         break;
       default:
         break;
@@ -208,6 +226,14 @@
     // Moves to next advertisement.
     i += adDataLength;
   }
+  LOGD("application address type 0x%" PRIx8, report->addressType);
+  LOGD("address=%02X:%02X:%02X:%02X:%02X:%02X", report->address[0],
+       report->address[1], report->address[2], report->address[3],
+       report->address[4], report->address[5]);
+  LOGD("direct address=%02X:%02X:%02X:%02X:%02X:%02X", report->directAddress[0],
+       report->directAddress[1], report->directAddress[2],
+       report->directAddress[3], report->directAddress[4],
+       report->directAddress[5]);
 }
 
 void handleAsyncResultEvent(const chreAsyncResult *result) {
@@ -229,7 +255,7 @@
          event->reports[i].eventTypeAndDataStatus);
     LOGD("Timestamp: %" PRIu64 " ms",
          event->reports[i].timestamp / chre::kOneMillisecondInNanoseconds);
-    parseAdData(event->reports[i].data, event->reports[i].dataLength);
+    parseReport(&event->reports[i]);
   }
 }
 
diff --git a/apps/message_world/message_world.cc b/apps/message_world/message_world.cc
index 17c910e..b1495e7 100644
--- a/apps/message_world/message_world.cc
+++ b/apps/message_world/message_world.cc
@@ -16,6 +16,7 @@
 
 #include <cinttypes>
 
+#include "chre/util/macros.h"
 #include "chre/util/nanoapp/log.h"
 #include "chre_api/chre.h"
 
@@ -28,17 +29,21 @@
 
 namespace {
 
-constexpr uint32_t kMessageType = 1234;
-uint8_t gMessageData[CHRE_MESSAGE_TO_HOST_MAX_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8};
+enum MessageType { kDefault = 1, kCustomReplyMessageSize = 2 };
+
+#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
+constexpr uint32_t gMaxReplyMessageSize = CHRE_LARGE_PAYLOAD_MAX_SIZE;
+#else
+constexpr uint32_t gMaxReplyMessageSize = CHRE_MESSAGE_TO_HOST_MAX_SIZE;
+#endif
+
+uint8_t gMessageData[gMaxReplyMessageSize] = {};
 
 void messageFreeCallback(void *message, size_t messageSize) {
-  LOGI(
-      "Got message free callback for message @ %p (match? %d) size %zu (match?"
-      " %d)",
-      message, (message == gMessageData), messageSize,
-      (messageSize == sizeof(gMessageData)));
-  if (!chreSendEvent(CHRE_EVENT_FIRST_USER_VALUE, nullptr, nullptr,
-                     chreGetInstanceId())) {
+  LOGI("Got message free callback for message @ %p (%s) size %zu", message,
+       (message == gMessageData) ? "matched" : "unmatched", messageSize);
+  if (!chreSendEvent(CHRE_EVENT_FIRST_USER_VALUE, /* eventData= */ nullptr,
+                     /* freeCallback= */ nullptr, chreGetInstanceId())) {
     LOGE("Failed to send event");
   }
 }
@@ -47,31 +52,43 @@
 
 bool nanoappStart() {
   LOGI("App started as instance %" PRIu32, chreGetInstanceId());
-
+  // initialize gMessageData
+  for (uint32_t i = 0; i < gMaxReplyMessageSize; ++i) {
+    gMessageData[i] = i % 10;
+  }
   bool success = chreSendMessageToHostEndpoint(
-      gMessageData, sizeof(gMessageData), kMessageType,
+      gMessageData, sizeof(gMessageData), MessageType::kDefault,
       CHRE_HOST_ENDPOINT_BROADCAST, messageFreeCallback);
-  LOGI("Sent message to host from start callback, result %d", success);
+  LOGI("Sent message of size %zu to host from start callback: %s",
+       sizeof(gMessageData), success ? "success" : "failure");
   return true;
 }
 
 void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
                         const void *eventData) {
-  if (eventType == CHRE_EVENT_MESSAGE_FROM_HOST) {
-    auto *msg = static_cast<const chreMessageFromHostData *>(eventData);
-    LOGI("Got message from host with type %" PRIu32 " size %" PRIu32
-         " data @ %p hostEndpoint 0x%" PRIx16,
-         msg->messageType, msg->messageSize, msg->message, msg->hostEndpoint);
-    if (senderInstanceId != CHRE_INSTANCE_ID) {
-      LOGE("Message from host came from unexpected instance ID %" PRIu32,
-           senderInstanceId);
-    }
-
-    bool success = chreSendMessageToHostEndpoint(
-        gMessageData, sizeof(gMessageData), kMessageType,
-        CHRE_HOST_ENDPOINT_BROADCAST, messageFreeCallback);
-    LOGI("Result of sending reply: %d", success);
+  if (eventType != CHRE_EVENT_MESSAGE_FROM_HOST) {
+    return;
   }
+  auto *msg = static_cast<const chreMessageFromHostData *>(eventData);
+  LOGI("Got message from host with type %" PRIu32 " size %" PRIu32
+       " data @ %p hostEndpoint 0x%" PRIx16,
+       msg->messageType, msg->messageSize, msg->message, msg->hostEndpoint);
+  if (senderInstanceId != CHRE_INSTANCE_ID) {
+    LOGE("Message from host came from unexpected instance ID %" PRIu32,
+         senderInstanceId);
+  }
+
+  uint32_t messageSize = gMaxReplyMessageSize;
+  if (msg->messageType == MessageType::kCustomReplyMessageSize) {
+    messageSize =
+        MIN(messageSize, *(static_cast<const uint32_t *>(msg->message)));
+  }
+
+  bool success = chreSendMessageToHostEndpoint(
+      gMessageData, messageSize, MessageType::kDefault, msg->hostEndpoint,
+      messageFreeCallback);
+  LOGI("Result of sending reply (size=%" PRIu32 "): %s", messageSize,
+       success ? "success" : "failure");
 }
 
 void nanoappEnd() {
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.cc
index e99c4ea..00dd161 100644
--- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.cc
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.cc
@@ -114,9 +114,7 @@
       HandleMatchAdvReports(adv_reports_cache_);
       break;
     case CHRE_EVENT_TIMER:
-      if (event_data == &ble_scan_keep_alive_timer_id) {
-        tracker_storage_.Refresh(tracker_filter_.GetBatchConfig());
-      }
+      HandleTimerEvent(event_data);
       break;
     case CHRE_EVENT_HOST_AWAKE:
       HandleHostAwakeEvent();
@@ -213,7 +211,8 @@
 }
 
 void AppManager::UpdateBleScanState() {
-  if (!filter_.IsEmpty() || !tracker_filter_.IsEmpty() ||
+  if (!filter_.IsEmpty() ||
+      (!tracker_filter_.IsEmpty() && tracker_filter_.IsActive()) ||
       !filter_extension_.IsEmpty()) {
     ble_scanner_.Restart();
   } else {
@@ -631,12 +630,42 @@
       chre::kOneMillisecondInNanoseconds;
   if (current_time - last_tracker_report_flush_time_nanosec_ >=
       flush_threshold_nanosec) {
-    LOGI("Flush tracker reports by host awake event.");
+    LOGD("Flush tracker reports by host awake event.");
     SendTrackerReportsToHost(tracker_storage_.GetBatchReports());
     tracker_storage_.Clear();
   }
 }
 
+void AppManager::HandleTimerEvent(const void *event_data) {
+  if (event_data == &ble_scan_keep_alive_timer_id) {
+    tracker_storage_.Refresh(tracker_filter_.GetBatchConfig());
+  } else if (event_data ==
+             tracker_filter_.GetActiveIntervalTimer().GetTimerId()) {
+    // When receive the active interval timer event, set the active state for
+    // tracker scan filter, start the oneshot active window timer, and set the
+    // tracker scan filters from the BLE scanner. Then update the BLE scan state
+    // so that the tracker scan can start properly. The tracker scan will stop
+    // when receive the oneshot active window timer event.
+    tracker_filter_.SetActiveState();
+    tracker_filter_.GetActiveWindowTimer().StartTimer();
+    ble_scanner_.SetTrackerFilters();
+    UpdateBleScanState();
+  } else if (event_data ==
+             tracker_filter_.GetActiveWindowTimer().GetTimerId()) {
+    // When receive the active window timer event, clear the active state for
+    // tracker scan filter, clear the tracker scan filters from the BLE scanner,
+    // and update the BLE scan state so that the tracker scan can stop properly.
+    // The tracker scan will restart when receive the next active interval timer
+    // event. If the tracker filter is empty, no action is needed as the tracker
+    // scan has completely stopped at this point.
+    if (!tracker_filter_.IsEmpty()) {
+      tracker_filter_.ClearActiveState();
+      ble_scanner_.ClearTrackerFilters();
+      UpdateBleScanState();
+    }
+  }
+}
+
 void AppManager::OnTrackerStorageFullEvent() {
   SendTrackerStorageFullEventToHost();
 }
@@ -651,6 +680,12 @@
     return false;
   }
   ble_scanner_.UpdateTrackerFilters(generic_filters);
+  // Set or clear tracker scan filter state before updating BLE scan state.
+  if (tracker_filter_.IsEmpty()) {
+    ble_scanner_.ClearTrackerFilters();
+  } else {
+    ble_scanner_.SetTrackerFilters();
+  }
   UpdateBleScanState();
   // Send tracker reports to host before clearing the tracker storage if the
   // host stops the tracker filter.
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.h
index 2d74be8..bd324a8 100644
--- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.h
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/app_manager.h
@@ -125,6 +125,9 @@
   // Handles host awake event.
   void HandleHostAwakeEvent();
 
+  // Handles timer event.
+  void HandleTimerEvent(const void *event_data);
+
   // Handles tracker filter config request from the host.
   bool HandleExtTrackerFilterConfig(
       const chreHostEndpointInfo &host_info,
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.cc
index 3727a17..27db465 100644
--- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.cc
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.cc
@@ -213,9 +213,11 @@
       generic_filters.push_back(kDefaultGenericFilters[i]);
     }
   }
-  for (auto &tracker_filter : tracker_filters_) {
-    if (!ContainsFilter(generic_filters, tracker_filter)) {
-      generic_filters.push_back(tracker_filter);
+  if (is_tracker_filter_enabled_) {
+    for (auto &tracker_filter : tracker_filters_) {
+      if (!ContainsFilter(generic_filters, tracker_filter)) {
+        generic_filters.push_back(tracker_filter);
+      }
     }
   }
   for (auto &oem_generic_filters : generic_filters_list_) {
@@ -253,7 +255,9 @@
   } else {
     LOGE("Failed to stop BLE scan");
   }
-  StopKeepAliveTimer();
+  if (tracker_filters_.empty()) {
+    StopKeepAliveTimer();
+  }
 }
 
 bool BleScanner::UpdateFilters(
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.h
index 7418ef1..af2f45f 100644
--- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.h
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/ble_scanner.h
@@ -115,6 +115,16 @@
     is_default_generic_filter_enabled_ = false;
   }
 
+  // Sets tracker filters.
+  void SetTrackerFilters() {
+    is_tracker_filter_enabled_ = true;
+  }
+
+  // Clears tracker filters.
+  void ClearTrackerFilters() {
+    is_tracker_filter_enabled_ = false;
+  }
+
   // Returns whether the filter list contains the given filter.
   bool ContainsFilter(const chre::DynamicVector<chreBleGenericFilter> &filters,
                       const chreBleGenericFilter &src);
@@ -146,6 +156,9 @@
   // Whether default generic filter is enabled.
   bool is_default_generic_filter_enabled_ = false;
 
+  // Whether tracker filter is enabled.
+  bool is_tracker_filter_enabled_ = false;
+
   // Current report delay for BLE batch scan
   uint32_t report_delay_ms_ = 0;
 
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.proto b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.proto
index 873388a..b0d9bd1 100644
--- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.proto
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.proto
@@ -115,6 +115,19 @@
     // disables the opportunistic flush.
     optional uint32 opportunistic_flush_threshold_time_ms = 8
         [default = 4294967295];
+
+    // Active interval for tracker scan filter. The tracker scan filter is
+    // enabled at the beginning of the active interval and disabled at the end
+    // of the active window. This creates a toggle effect for the tracker scan
+    // filter and reduces the BLE scan power consumption. If the interval and
+    // window are not set by host, the default values are 0, and the tracker
+    // scan filter is always enabled.
+    // The active interval must be greater than the active window so that the
+    // scan toggling can function properly.
+    optional uint32 active_interval_ms = 9 [default = 0];
+
+    // Active window for tracker scan filter.
+    optional uint32 active_window_ms = 10 [default = 0];
   }
 
   message FlushTrackerReports {}
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/timer.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/timer.cc
new file mode 100644
index 0000000..ee857ed
--- /dev/null
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/timer.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "location/lbs/contexthub/nanoapps/nearby/timer.h"
+
+#include <chre.h>
+
+#include "third_party/contexthub/chre/util/include/chre/util/nanoapp/log.h"
+#include "third_party/contexthub/chre/util/include/chre/util/time.h"
+
+#define LOG_TAG "[NEARBY][TIMER]"
+
+namespace nearby {
+
+bool Timer::StartTimer() {
+  if (duration_ms_ == 0) {
+    LOGD("Timer is not started. Timer duration is 0.");
+    return false;
+  }
+  if (!is_one_shot_ && timer_id_ != CHRE_TIMER_INVALID) {
+    chreTimerCancel(timer_id_);
+    timer_id_ = CHRE_TIMER_INVALID;
+  }
+  timer_id_ = chreTimerSet(duration_ms_ * chre::kOneMillisecondInNanoseconds,
+                           &timer_id_, /*oneShot=*/is_one_shot_);
+  if (timer_id_ == CHRE_TIMER_INVALID) {
+    LOGE("Error in configuring timer.");
+    return false;
+  }
+  return true;
+}
+
+bool Timer::StopTimer() {
+  if (timer_id_ == CHRE_TIMER_INVALID) {
+    LOGD("Timer is already stopped.");
+    return false;
+  }
+  if (!chreTimerCancel(timer_id_)) {
+    LOGW(
+        "Error in stopping timer. For a one-shot timer, it may have just "
+        "fired or expired.");
+    return false;
+  }
+  timer_id_ = CHRE_TIMER_INVALID;
+  return true;
+}
+
+}  // namespace nearby
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/timer.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/timer.h
new file mode 100644
index 0000000..600ed9f
--- /dev/null
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/timer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOCATION_LBS_CONTEXTHUB_NANOAPPS_NEARBY_TIMER_H_
+#define LOCATION_LBS_CONTEXTHUB_NANOAPPS_NEARBY_TIMER_H_
+
+#include <cstdint>
+
+#include "chre_api/chre.h"
+
+namespace nearby {
+
+class Timer {
+ public:
+  // Constructs timer.
+  explicit Timer(bool is_one_shot) : is_one_shot_(is_one_shot) {}
+
+  // Sets timer duration in milliseconds.
+  void SetDurationMs(uint32_t duration_ms) {
+    duration_ms_ = duration_ms;
+  }
+
+  // Starts timer.
+  bool StartTimer();
+
+  // Stops timer.
+  bool StopTimer();
+
+  // Returns timer id.
+  const uint32_t *GetTimerId() {
+    return &timer_id_;
+  }
+
+ private:
+  uint32_t timer_id_ = CHRE_TIMER_INVALID;
+  uint32_t duration_ms_ = 0;
+  bool is_one_shot_;
+};
+
+}  // namespace nearby
+
+#endif  // LOCATION_LBS_CONTEXTHUB_NANOAPPS_NEARBY_TIMER_H_
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_filter.cc b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_filter.cc
index e072b7a..3dbf1fd 100644
--- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_filter.cc
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_filter.cc
@@ -1,5 +1,6 @@
 #include "location/lbs/contexthub/nanoapps/nearby/tracker_filter.h"
 
+#include <inttypes.h>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
@@ -51,6 +52,10 @@
   }
   scan_filter_config_.hardware_filters = std::move(hardware_filters);
   scan_filter_config_.rssi_threshold = filter_config.rssi_threshold;
+  scan_filter_config_.active_interval_ms = filter_config.active_interval_ms;
+  scan_filter_config_.active_window_ms = filter_config.active_window_ms;
+  ConfigureActiveState();
+  ConfigureScanControlTimers();
   // Sets batch configuration
   batch_config_.sample_interval_ms = filter_config.sample_interval_ms;
   batch_config_.max_tracker_count = filter_config.max_tracker_count;
@@ -62,6 +67,42 @@
       filter_config.opportunistic_flush_threshold_time_ms;
 }
 
+void TrackerFilter::ConfigureActiveState() {
+  if (!scan_filter_config_.hardware_filters.empty()) {
+    SetActiveState();
+  } else {
+    ClearActiveState();
+  }
+}
+
+void TrackerFilter::ConfigureScanControlTimers() {
+  // The timer based scan is only enabled when the hardware scan filters are not
+  // empty and the active window and interval are valid. The active interval
+  // must be greater than the active window so that the timer based scan can
+  // function properly.
+  if (!scan_filter_config_.hardware_filters.empty() &&
+      scan_filter_config_.active_window_ms > 0) {
+    if (scan_filter_config_.active_interval_ms <=
+        scan_filter_config_.active_window_ms) {
+      LOGE("Invalid active interval %" PRIu32
+           " ms, must be greater than active window %" PRIu32 " ms.",
+           scan_filter_config_.active_interval_ms,
+           scan_filter_config_.active_window_ms);
+      return;
+    }
+    // Sets active interval and window timer duration.
+    active_interval_timer_.SetDurationMs(
+        scan_filter_config_.active_interval_ms);
+    active_window_timer_.SetDurationMs(scan_filter_config_.active_window_ms);
+    // Starts active interval and window timers.
+    if (active_interval_timer_.StartTimer()) {
+      active_window_timer_.StartTimer();
+    }
+  } else if (scan_filter_config_.hardware_filters.empty()) {
+    active_interval_timer_.StopTimer();
+  }
+}
+
 void TrackerFilter::MatchAndSave(
     const chre::DynamicVector<chreBleAdvertisingReport> &ble_adv_reports,
     TrackerStorage &tracker_storage) {
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_filter.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_filter.h
index ec30279..ef07856 100644
--- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_filter.h
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_filter.h
@@ -1,11 +1,14 @@
 #ifndef LOCATION_LBS_CONTEXTHUB_NANOAPPS_NEARBY_TRACKER_FILTER_H_
 #define LOCATION_LBS_CONTEXTHUB_NANOAPPS_NEARBY_TRACKER_FILTER_H_
 
+#include <stdbool.h>
+
 #include <cstdint>
 
 #include "chre_api/chre.h"
 #include "location/lbs/contexthub/nanoapps/nearby/byte_array.h"
 #include "location/lbs/contexthub/nanoapps/nearby/proto/nearby_extension.nanopb.h"
+#include "location/lbs/contexthub/nanoapps/nearby/timer.h"
 #include "location/lbs/contexthub/nanoapps/nearby/tracker_storage.h"
 #include "third_party/contexthub/chre/util/include/chre/util/dynamic_vector.h"
 
@@ -14,6 +17,15 @@
 struct TrackerScanFilterConfig {
   chre::DynamicVector<chreBleGenericFilter> hardware_filters;
   int8_t rssi_threshold = CHRE_BLE_RSSI_THRESHOLD_NONE;
+  // Active interval for tracker scan filter. The tracker scan filter is enabled
+  // at the beginning of the active interval and disabled at the end of the
+  // active window. This creates a toggle effect for the tracker scan filter and
+  // reduces the BLE scan power consumption. If the interval and window are not
+  // set by host, the default values are 0, and the tracker scan filter is
+  // always enabled.
+  uint32_t active_interval_ms;
+  // Active window for tracker scan filter.
+  uint32_t active_window_ms;
 };
 
 class TrackerFilter {
@@ -60,10 +72,47 @@
   static bool EncodeTrackerReport(TrackerReport &tracker_report,
                                   ByteArray data_buf, size_t *encoded_size);
 
+  // Sets tracker scan filter active state.
+  void SetActiveState() {
+    is_active_ = true;
+  }
+
+  // Clears tracker scan filter active state.
+  void ClearActiveState() {
+    is_active_ = false;
+  }
+
+  // Returns whether tracker scan filter is active.
+  bool IsActive() {
+    return is_active_;
+  }
+
+  Timer &GetActiveIntervalTimer() {
+    return active_interval_timer_;
+  }
+
+  Timer &GetActiveWindowTimer() {
+    return active_window_timer_;
+  }
+
  private:
   TrackerScanFilterConfig scan_filter_config_;
   TrackerBatchConfig batch_config_;
   chreHostEndpointInfo host_info_;
+  // whether the tracker scan filter is active.
+  bool is_active_ = false;
+
+  // Configures tracker scan filter active state.
+  void ConfigureActiveState();
+
+  // Configures tracker scan filter control timers when updating scan filter and
+  // batch configurations.
+  void ConfigureScanControlTimers();
+
+  // Timer for tracker scan filter active interval.
+  Timer active_interval_timer_ = Timer(false);
+  // Timer for tracker scan filter active window.
+  Timer active_window_timer_ = Timer(true);
 };
 
 }  // namespace nearby
diff --git a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_storage.h b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_storage.h
index 73a03b2..75eb103 100644
--- a/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_storage.h
+++ b/apps/nearby/location/lbs/contexthub/nanoapps/nearby/tracker_storage.h
@@ -22,18 +22,18 @@
 
 struct TrackerBatchConfig {
   // Minimum sampling interval to update tracker history.
-  uint32_t sample_interval_ms;
+  uint32_t sample_interval_ms = {60000};
   // Maximum number of tracker reports that can be stored in storage.
-  uint32_t max_tracker_count;
+  uint32_t max_tracker_count = {30};
   // Notification threshold of the number of tracker reports, which should be
   // equal to or smaller than max_tracker_count.
-  uint32_t notify_threshold_tracker_count;
+  uint32_t notify_threshold_tracker_count = {28};
   // Maximum number of tracker histories that can be stored in tracker report.
-  uint32_t max_history_count;
+  uint32_t max_history_count = {20};
   // Timeout for tracker history to be considered lost.
-  uint32_t lost_timeout_ms;
+  uint32_t lost_timeout_ms = {60000};
   // Time based threshold for opportunistic flush of tracker reports.
-  uint32_t opportunistic_flush_threshold_time_ms;
+  uint32_t opportunistic_flush_threshold_time_ms = {4294967295};
 };
 
 enum class TrackerState {
diff --git a/apps/sensor_test/Makefile b/apps/sensor_test/Makefile
new file mode 100644
index 0000000..d402f68
--- /dev/null
+++ b/apps/sensor_test/Makefile
@@ -0,0 +1,41 @@
+#
+# Sensor World Nanoapp Makefile
+#
+
+# Environment Checks ###########################################################
+
+ifeq ($(CHRE_PREFIX),)
+ifneq ($(ANDROID_BUILD_TOP),)
+CHRE_PREFIX = $(ANDROID_BUILD_TOP)/system/chre
+else
+$(error "You must run 'lunch' to setup ANDROID_BUILD_TOP, or explicitly define \
+         the CHRE_PREFIX environment variable to point to the CHRE root \
+	 directory.")
+endif
+endif
+
+# Nanoapp Configuration ########################################################
+
+NANOAPP_NAME = sensor_test
+NANOAPP_ID = 0x0123456789000016
+NANOAPP_VERSION = 0x00000001
+
+NANOAPP_NAME_STRING = \"Sensor\ Test\"
+
+# Common Compiler Flags ########################################################
+
+# Include paths.
+COMMON_CFLAGS += -I.
+COMMON_CFLAGS += -I$(CHRE_PREFIX)/util/include
+
+# Defines.
+COMMON_CFLAGS += -DNANOAPP_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_DEBUG
+COMMON_CFLAGS += -DCHRE_ASSERTIONS_ENABLED
+
+# Common Source Files ##########################################################
+
+COMMON_SRCS += sensor_test.cc
+
+# Makefile Includes ############################################################
+
+include $(CHRE_PREFIX)/build/nanoapp/app.mk
diff --git a/apps/sensor_test/sensor_test.cc b/apps/sensor_test/sensor_test.cc
new file mode 100644
index 0000000..0aa612e
--- /dev/null
+++ b/apps/sensor_test/sensor_test.cc
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cinttypes>
+
+#include "chre/util/macros.h"
+#include "chre/util/nanoapp/log.h"
+#include "chre/util/time.h"
+#include "chre_api/chre.h"
+
+#define LOG_TAG "[SensorTest]"
+
+#ifdef CHRE_NANOAPP_INTERNAL
+namespace chre {
+namespace {
+#endif  // CHRE_NANOAPP_INTERNAL
+
+using chre::kOneMillisecondInNanoseconds;
+using chre::Milliseconds;
+using chre::Seconds;
+
+namespace {
+
+//! Enable BreakIt test mode.
+// In BreakIt test mode, a timer will be set periodically to randomly
+// enable/disable each sensor.
+constexpr bool kBreakIt = false;
+constexpr Milliseconds kBreakItPeriod = Milliseconds(2000);
+uint32_t gBreakItTimerHandle;
+
+//! Enable chreSensorFlushAsync test
+// When enabled, SensorTest will set a timer to invoke
+// chreSensorFlushAsync(sensors[kFlushSensorIndex].handle)
+// halfway through sensors[kFlushSensorIndex].latency
+//
+// If CHRE_EVENT_SENSOR_FLUSH_COMPLETE is not received before
+// kFlushItTimeout expires, an error message will be logged.
+constexpr bool kFlushIt = true;
+constexpr uint32_t kFlushCookie = 0xdeadbeef;
+constexpr uint32_t kFlushSensorIndex = 0;  // CHRE_SENSOR_TYPE_ACCELEROMETER
+uint32_t gFlushItTimerHandle;
+
+constexpr Milliseconds kFlushItTimeout = Milliseconds(5000);
+uint32_t gFlushItTimeoutTimerHandle;
+
+//! Whether to enable sensor event logging or not.
+constexpr bool kEnableSensorEventLogging = true;
+
+//! Enable/disable all sensors by default.
+// This allows disabling all sensens by default and enabling only targeted
+// sensors for testing by locally overriding 'enable' field in SensorState.
+// Note that enabling BreakIt test disables all sensors at init by default.
+constexpr bool kEnableDefault = !kBreakIt;
+
+struct SensorState {
+  const uint8_t type;
+  const uint8_t sensorIndex;
+  uint32_t handle;
+  bool isInitialized;
+  bool enable;
+  uint64_t interval;  // nsec
+  uint64_t latency;   // nsec
+  chreSensorInfo info;
+};
+
+SensorState sensors[] = {
+    {
+        .type = CHRE_SENSOR_TYPE_ACCELEROMETER,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(80).toRawNanoseconds(),
+        .latency = Seconds(4).toRawNanoseconds(),
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = false,  // InstantMotion is triggered by Prox
+        .interval = CHRE_SENSOR_INTERVAL_DEFAULT,
+        .latency = CHRE_SENSOR_LATENCY_DEFAULT,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_STATIONARY_DETECT,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = false,  // StationaryDetect is triggered by Prox
+        .interval = CHRE_SENSOR_INTERVAL_DEFAULT,
+        .latency = CHRE_SENSOR_LATENCY_DEFAULT,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_GYROSCOPE,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(80).toRawNanoseconds(),
+        .latency = Seconds(4).toRawNanoseconds(),
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(80).toRawNanoseconds(),
+        .latency = Seconds(4).toRawNanoseconds(),
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_PRESSURE,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(200).toRawNanoseconds(),
+        .latency = Seconds(4).toRawNanoseconds(),
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_LIGHT,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(200).toRawNanoseconds(),
+        .latency = 0,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_PROXIMITY,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(200).toRawNanoseconds(),
+        .latency = 0,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_STEP_DETECT,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = CHRE_SENSOR_INTERVAL_DEFAULT,
+        .latency = CHRE_SENSOR_LATENCY_ASAP,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_STEP_COUNTER,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = CHRE_SENSOR_INTERVAL_DEFAULT,
+        .latency = CHRE_SENSOR_LATENCY_ASAP,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Seconds(2).toRawNanoseconds(),
+        .latency = 0,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Seconds(2).toRawNanoseconds(),
+        .latency = 0,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Seconds(2).toRawNanoseconds(),
+        .latency = 0,
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(80).toRawNanoseconds(),
+        .latency = Seconds(4).toRawNanoseconds(),
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(80).toRawNanoseconds(),
+        .latency = Seconds(4).toRawNanoseconds(),
+        .info = {},
+    },
+    {
+        .type = CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD,
+        .sensorIndex = 0,
+        .handle = 0,
+        .isInitialized = false,
+        .enable = kEnableDefault,
+        .interval = Milliseconds(80).toRawNanoseconds(),
+        .latency = Seconds(4).toRawNanoseconds(),
+        .info = {},
+    },
+};
+
+// Conditional logging macro
+#define CLOGI(fmt, ...)              \
+  do {                               \
+    if (kEnableSensorEventLogging) { \
+      LOGI(fmt, ##__VA_ARGS__);      \
+    }                                \
+  } while (0);
+
+// Helpers for testing InstantMotion and StationaryDetect
+enum class MotionMode {
+  Instant,
+  Stationary,
+};
+
+// Storage to help access InstantMotion and StationaryDetect sensor handle and
+// info
+size_t motionSensorIndices[2];
+MotionMode motionMode = MotionMode::Instant;
+
+size_t getMotionSensorIndex() {
+  motionMode = (motionMode == MotionMode::Instant) ? MotionMode::Stationary
+                                                   : MotionMode::Instant;
+  return motionSensorIndices[static_cast<size_t>(motionMode)];
+}
+
+//! Used to loop through all sensors to query sensor sampling status.
+size_t statusIndex = 0;
+
+// Obtains 16-bit pseudo-random numbers.
+uint16_t getNextLfsrState() {
+  // 15-bit LFSR with feedback polynomial x^15 + x^14 + 1 gives us a
+  // pseudo-random sequence over all 32767 possible values
+  static uint16_t lfsr = 0x1337;
+  uint16_t nextBit = ((lfsr << 14) ^ (lfsr << 13)) & 0x4000;
+  lfsr = nextBit | (lfsr >> 1);
+
+  return lfsr;
+}
+
+const char *getSensorName(uint32_t sensorHandle) {
+  for (size_t i = 0; i < ARRAY_SIZE(sensors); i++) {
+    if (sensors[i].handle == sensorHandle) {
+      return sensors[i].info.sensorName;
+    }
+  }
+  return nullptr;
+}
+
+void handleTimerEvent(const uint32_t *ev) {
+  if (*ev == gFlushItTimerHandle) {
+    LOGI("FlushIt Timer Fired");
+    if (chreSensorFlushAsync(sensors[kFlushSensorIndex].handle,
+                             &kFlushCookie)) {
+      gFlushItTimeoutTimerHandle =
+          chreTimerSet(kFlushItTimeout.toRawNanoseconds(),
+                       &gFlushItTimeoutTimerHandle, true /* oneShot */);
+    } else {
+      LOGE("chreSensorFlushAsync failed");
+    }
+
+  } else if (*ev == gFlushItTimeoutTimerHandle) {
+    LOGE("chreSensorFlushAsync Timeout");
+
+  } else if (*ev == gBreakItTimerHandle) {
+    for (size_t i = 0; i < ARRAY_SIZE(sensors); i++) {
+      SensorState &sensor = sensors[i];
+
+      bool enable = getNextLfsrState() & 0x1;
+      if (sensor.isInitialized && sensor.enable != enable) {
+        sensor.enable = enable;
+
+        bool status;
+        if (!enable) {
+          status = chreSensorConfigureModeOnly(sensor.handle,
+                                               CHRE_SENSOR_CONFIGURE_MODE_DONE);
+        } else {
+          enum chreSensorConfigureMode mode =
+              sensor.info.isOneShot ? CHRE_SENSOR_CONFIGURE_MODE_ONE_SHOT
+                                    : CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS;
+          status = chreSensorConfigure(sensor.handle, mode, sensor.interval,
+                                       sensor.latency);
+        }
+
+        LOGI("Configure [enable %d, status %d]: %s", enable, status,
+             sensor.info.sensorName);
+      }
+    }
+
+    gBreakItTimerHandle =
+        chreTimerSet(kBreakItPeriod.toRawNanoseconds(), &gBreakItTimerHandle,
+                     true /* oneShot */);
+  }
+}
+
+void handleThreeAxisEvent(const chreSensorThreeAxisData *ev,
+                          uint16_t eventType) {
+  const auto header = ev->header;
+  const auto *data = ev->readings;
+  const auto accuracy = header.accuracy;
+  uint64_t sampleTime = header.baseTimestamp;
+  uint64_t chreTime = chreGetTime();
+
+  float x = 0, y = 0, z = 0;
+  for (size_t i = 0; i < header.readingCount; i++) {
+    x += data[i].v[0];
+    y += data[i].v[1];
+    z += data[i].v[2];
+    sampleTime += data[i].timestampDelta;
+  }
+  x /= header.readingCount;
+  y /= header.readingCount;
+  z /= header.readingCount;
+
+  CLOGI("%s, %d samples: %f %f %f, accuracy: %u, t=%" PRIu64 " ms",
+        getSensorName(header.sensorHandle), header.readingCount,
+        static_cast<double>(x), static_cast<double>(y), static_cast<double>(z),
+        accuracy, header.baseTimestamp / kOneMillisecondInNanoseconds);
+
+  if (eventType == CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA) {
+    CLOGI("UncalGyro time: first %" PRIu64 " last %" PRIu64 " chre %" PRIu64
+          " delta [%" PRId64 ", %" PRId64 "]ms",
+          header.baseTimestamp, sampleTime, chreTime,
+          static_cast<int64_t>(header.baseTimestamp - chreTime) /
+              static_cast<int64_t>(kOneMillisecondInNanoseconds),
+          static_cast<int64_t>(sampleTime - chreTime) /
+              static_cast<int64_t>(kOneMillisecondInNanoseconds));
+  }
+}
+
+void handleFloatEvent(const chreSensorFloatData *ev) {
+  const auto header = ev->header;
+
+  float v = 0;
+  for (size_t i = 0; i < header.readingCount; i++) {
+    v += ev->readings[i].value;
+  }
+  v /= header.readingCount;
+
+  CLOGI("%s, %d samples: %f, accuracy = %u, t=%" PRIu64 " ms",
+        getSensorName(header.sensorHandle), header.readingCount,
+        static_cast<double>(v), header.accuracy,
+        header.baseTimestamp / kOneMillisecondInNanoseconds);
+}
+
+void handleProximityEvent(const chreSensorByteData *ev) {
+  const auto header = ev->header;
+  const auto reading = ev->readings[0];
+  uint64_t sampleTime = header.baseTimestamp;
+  uint64_t chreTime = chreGetTime();
+
+  CLOGI("%s, %d samples: isNear %d, invalid %d, accuracy: %u",
+        getSensorName(header.sensorHandle), header.readingCount, reading.isNear,
+        reading.invalid, header.accuracy);
+
+  CLOGI("Prox time: sample %" PRIu64 " chre %" PRIu64 " delta %" PRId64 "ms",
+        header.baseTimestamp, chreTime,
+        static_cast<int64_t>(sampleTime - chreTime) / 1000000);
+
+  // Enable InstantMotion and StationaryDetect alternatively on near->far.
+  if (reading.isNear == 0 && !kBreakIt) {
+    size_t motionSensorIndex = getMotionSensorIndex();
+    bool status = chreSensorConfigure(
+        sensors[motionSensorIndex].handle, CHRE_SENSOR_CONFIGURE_MODE_ONE_SHOT,
+        CHRE_SENSOR_INTERVAL_DEFAULT, CHRE_SENSOR_LATENCY_DEFAULT);
+    LOGI("Requested %s: %s", sensors[motionSensorIndex].info.sensorName,
+         status ? "success" : "failure");
+  }
+
+  // Exercise chreGetSensorSamplingStatus on one sensor on near->far.
+  if (sensors[statusIndex].isInitialized && reading.isNear == 0) {
+    struct chreSensorSamplingStatus status;
+    bool success =
+        chreGetSensorSamplingStatus(sensors[statusIndex].handle, &status);
+    LOGI("%s success %d: enabled %d interval %" PRIu64 " latency %" PRIu64,
+         sensors[statusIndex].info.sensorName, success, status.enabled,
+         status.interval, status.latency);
+  }
+  statusIndex = (statusIndex + 1) % ARRAY_SIZE(sensors);
+}
+
+}  // namespace
+
+bool nanoappStart() {
+  LOGI("App started on platform ID %" PRIx64, chreGetPlatformId());
+
+  for (size_t i = 0; i < ARRAY_SIZE(sensors); i++) {
+    SensorState &sensor = sensors[i];
+    sensor.isInitialized =
+        chreSensorFind(sensor.type, sensor.sensorIndex, &sensor.handle);
+    LOGI("Sensor %zu initialized: %s with handle %" PRIu32, i,
+         sensor.isInitialized ? "true" : "false", sensor.handle);
+
+    if (sensor.type == CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT) {
+      motionSensorIndices[static_cast<size_t>(MotionMode::Instant)] = i;
+    } else if (sensor.type == CHRE_SENSOR_TYPE_STATIONARY_DETECT) {
+      motionSensorIndices[static_cast<size_t>(MotionMode::Stationary)] = i;
+    }
+
+    if (sensor.isInitialized) {
+      // Get sensor info
+      chreSensorInfo &info = sensor.info;
+      bool infoStatus = chreGetSensorInfo(sensor.handle, &info);
+      if (infoStatus) {
+        LOGI("SensorInfo: %s, Type=%" PRIu8
+             " OnChange=%d OneShot=%d Passive=%d "
+             "minInterval=%" PRIu64 "nsec",
+             info.sensorName, info.sensorType, info.isOnChange, info.isOneShot,
+             info.supportsPassiveMode, info.minInterval);
+      } else {
+        LOGE("chreGetSensorInfo failed");
+      }
+
+      // Subscribe to sensors
+      if (sensor.enable) {
+        float odrHz = 1e9f / static_cast<float>(sensor.interval);
+        float latencySec = static_cast<float>(sensor.latency) / 1e9f;
+        bool status = chreSensorConfigure(sensor.handle,
+                                          CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS,
+                                          sensor.interval, sensor.latency);
+        LOGI("Requested data: odr %f Hz, latency %f sec, %s",
+             static_cast<double>(odrHz), static_cast<double>(latencySec),
+             status ? "success" : "failure");
+      }
+    }
+  }
+
+  // Set timer for BreakIt test.
+  if (kBreakIt) {
+    gBreakItTimerHandle =
+        chreTimerSet(kBreakItPeriod.toRawNanoseconds(), &gBreakItTimerHandle,
+                     true /* oneShot */);
+  }
+
+  if (kFlushIt) {
+    // Trigger a flush half way through the target sensor latency
+    gFlushItTimerHandle =
+        chreTimerSet(sensors[kFlushSensorIndex].latency / 2,
+                     &gFlushItTimerHandle, true /* oneShot */);
+  }
+
+  return true;
+}
+
+void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
+                        const void *eventData) {
+  UNUSED_VAR(senderInstanceId);
+
+  switch (eventType) {
+    case CHRE_EVENT_SENSOR_ACCELEROMETER_DATA:
+    case CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA:
+    case CHRE_EVENT_SENSOR_GYROSCOPE_DATA:
+    case CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA:
+    case CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_DATA:
+    case CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA:
+      handleThreeAxisEvent(
+          static_cast<const chreSensorThreeAxisData *>(eventData), eventType);
+      break;
+
+    case CHRE_EVENT_SENSOR_PRESSURE_DATA:
+    case CHRE_EVENT_SENSOR_LIGHT_DATA:
+    case CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA:
+    case CHRE_EVENT_SENSOR_GYROSCOPE_TEMPERATURE_DATA:
+    case CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_TEMPERATURE_DATA:
+      handleFloatEvent(static_cast<const chreSensorFloatData *>(eventData));
+      break;
+
+    case CHRE_EVENT_SENSOR_PROXIMITY_DATA:
+      handleProximityEvent(static_cast<const chreSensorByteData *>(eventData));
+      break;
+
+    case CHRE_EVENT_SENSOR_INSTANT_MOTION_DETECT_DATA:
+    case CHRE_EVENT_SENSOR_STATIONARY_DETECT_DATA:
+    case CHRE_EVENT_SENSOR_STEP_DETECT_DATA: {
+      const auto *ev = static_cast<const chreSensorOccurrenceData *>(eventData);
+      const auto header = ev->header;
+
+      CLOGI("%s, %d samples, accuracy: %u", getSensorName(header.sensorHandle),
+            header.readingCount, header.accuracy);
+      break;
+    }
+
+    case CHRE_EVENT_SENSOR_STEP_COUNTER_DATA: {
+      const auto *ev = static_cast<const chreSensorUint64Data *>(eventData);
+      const auto header = ev->header;
+      const uint64_t reading = ev->readings[header.readingCount - 1].value;
+
+      CLOGI("%s, %" PRIu16 " samples: latest %" PRIu64,
+            getSensorName(header.sensorHandle), header.readingCount, reading);
+      break;
+    }
+
+    case CHRE_EVENT_SENSOR_SAMPLING_CHANGE: {
+      const auto *ev =
+          static_cast<const chreSensorSamplingStatusEvent *>(eventData);
+
+      CLOGI("Sampling Change: handle %" PRIu32 ", status: interval %" PRIu64
+            " latency %" PRIu64 " enabled %d",
+            ev->sensorHandle, ev->status.interval, ev->status.latency,
+            ev->status.enabled);
+      break;
+    }
+
+    case CHRE_EVENT_TIMER:
+      if (kBreakIt || kFlushIt) {
+        handleTimerEvent(static_cast<const uint32_t *>(eventData));
+      } else {
+        LOGE("Timer event received with kBreakIt and kFlushIt disabled");
+      }
+      break;
+
+    case CHRE_EVENT_SENSOR_FLUSH_COMPLETE: {
+      const auto *ev =
+          static_cast<const chreSensorFlushCompleteEvent *>(eventData);
+      chreTimerCancel(gFlushItTimeoutTimerHandle);
+
+      LOGI("Flush Complete: handle %" PRIu32 ", errorCode: %d",
+           ev->sensorHandle, ev->errorCode);
+      break;
+    }
+
+    default:
+      LOGW("Unhandled event %d", eventType);
+      break;
+  }
+}
+
+void nanoappEnd() {
+  LOGI("Stopped");
+}
+
+#ifdef CHRE_NANOAPP_INTERNAL
+}  // anonymous namespace
+}  // namespace chre
+
+#include "chre/platform/static_nanoapp_init.h"
+#include "chre/util/nanoapp/app_id.h"
+#include "chre/util/system/napp_permissions.h"
+
+CHRE_STATIC_NANOAPP_INIT(SensorWorld, chre::kSensorWorldAppId, 0,
+                         chre::NanoappPermissions::CHRE_PERMS_NONE);
+#endif  // CHRE_NANOAPP_INTERNAL
diff --git a/apps/sensor_test/sensor_test.mk b/apps/sensor_test/sensor_test.mk
new file mode 100644
index 0000000..6b6f44e
--- /dev/null
+++ b/apps/sensor_test/sensor_test.mk
@@ -0,0 +1,12 @@
+#
+# Sensor World Makefile
+#
+
+# Common Compiler Flags ########################################################
+
+# Include paths.
+COMMON_CFLAGS += -Iapps/sensor_test/include
+
+# Common Source Files ##########################################################
+
+COMMON_SRCS += apps/sensor_test/sensor_test.cc
diff --git a/apps/test/chqts/src/general_test/basic_audio_test.cc b/apps/test/chqts/src/general_test/basic_audio_test.cc
index 8076598..6c2b7a0 100644
--- a/apps/test/chqts/src/general_test/basic_audio_test.cc
+++ b/apps/test/chqts/src/general_test/basic_audio_test.cc
@@ -261,10 +261,7 @@
   struct chreAudioSource source;
   constexpr uint32_t kRequiredAudioHandle = 0;
   // If the DUT supports CHRE audio, then audio handle 0 is required to be
-  // valid. There is the risk that the chreAudioGetSource function might
-  // legitimately fail however - we should replace this function when CHRE
-  // audio capabilities in b/185155280 are implemented.
-  // TODO (b/185155280): fix this query
+  // valid.
   return chreAudioGetSource(kRequiredAudioHandle, &source);
 }
 }  // anonymous namespace
diff --git a/apps/test/chqts/src/general_test/basic_ble_test.cc b/apps/test/chqts/src/general_test/basic_ble_test.cc
index 4d01722..e27432f 100644
--- a/apps/test/chqts/src/general_test/basic_ble_test.cc
+++ b/apps/test/chqts/src/general_test/basic_ble_test.cc
@@ -15,13 +15,14 @@
  */
 
 #include <general_test/basic_ble_test.h>
-
 #include <shared/send_message.h>
 
 #include "chre/util/nanoapp/ble.h"
+#include "chre/util/nanoapp/log.h"
 #include "chre/util/time.h"
 #include "chre_api/chre.h"
 
+#define LOG_TAG "[GeneralTest][Ble]"
 /*
  * Test to check expected functionality of the CHRE BLE APIs.
  */
@@ -83,8 +84,15 @@
 }
 
 void BasicBleTest::handleBleAsyncResult(const chreAsyncResult *result) {
-  if (result == nullptr || !result->success) {
+  if (result == nullptr) {
+    sendFatalFailureToHost("Received null BLE async result");
+    return;
+  }
+  if (!result->success) {
+    LOGE("Received unsuccessful BLE async result, error code %" PRIu8,
+         result->errorCode);
     sendFatalFailureToHost("Received unsuccessful BLE async result");
+    return;
   }
 
   switch (result->requestType) {
diff --git a/apps/test/common/chre_api_test/rpc/chre_api_test.pwpb_options b/apps/test/common/chre_api_test/rpc/chre_api_test.pwpb_options
new file mode 100644
index 0000000..6cc7b0b
--- /dev/null
+++ b/apps/test/common/chre_api_test/rpc/chre_api_test.pwpb_options
@@ -0,0 +1,12 @@
+chre.rpc.ChreGetSensorInfoOutput.sensorName max_size:100
+chre.rpc.ChreAudioGetSourceOutput.name max_size:40 # CHRE_AUDIO_SOURCE_NAME_MAX_SIZE
+chre.rpc.ChreAudioDataEvent.samples max_size:64000
+chre.rpc.ChreAudioDataSamples.samples max_size:200
+chre.rpc.ChreBleGenericFilter.data max_size:29 # CHRE_BLE_DATA_LEN_MAX
+chre.rpc.ChreBleGenericFilter.mask max_size:29 # CHRE_BLE_DATA_LEN_MAX
+chre.rpc.ChreGetHostEndpointInfoOutput.endpointName max_size:51 # CHRE_MAX_ENDPOINT_NAME_LEN
+chre.rpc.ChreGetHostEndpointInfoOutput.endpointTag max_size:51 # CHRE_MAX_ENDPOINT_TAG_LEN
+chre.rpc.GatherEventsInput.eventTypes max_count:10
+chre.rpc.ChreBleAdvertisingReport.address max_size:6 # CHRE_BLE_ADDRESS_LEN
+chre.rpc.ChreBleAdvertisingReport.directAddress max_size:6 # CHRE_BLE_ADDRESS_LEN
+chre.rpc.ChreBleAdvertisingReport.data max_size:255 # extended range is [0, 255]
diff --git a/apps/test/common/chre_cross_validator_wifi/Makefile b/apps/test/common/chre_cross_validator_wifi/Makefile
index 862f248..e3f53f0 100644
--- a/apps/test/common/chre_cross_validator_wifi/Makefile
+++ b/apps/test/common/chre_cross_validator_wifi/Makefile
@@ -44,7 +44,7 @@
 # Compiler Flags ###############################################################
 
 # Defines
-COMMON_CFLAGS += -DNANOAPP_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_INFO
+COMMON_CFLAGS += -DNANOAPP_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_DEBUG
 COMMON_CFLAGS += -DLOG_TAG=\"[ChreCrossValidatorWifi]\"
 COMMON_CFLAGS += -DCHRE_ASSERTIONS_ENABLED
 
diff --git a/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h b/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h
index 0a7abc2..b8db737 100644
--- a/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h
+++ b/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h
@@ -32,9 +32,7 @@
 #include "chre_test_common.nanopb.h"
 #include "wifi_scan_result.h"
 
-namespace chre {
-
-namespace cross_validator_wifi {
+namespace chre::cross_validator_wifi {
 
 /**
  * Class to manage a CHRE cross validator wifi nanoapp.
@@ -69,6 +67,10 @@
   // CHRE to receive more result.
   uint8_t mExpectedMaxChreResultCanHandle = 100;
 
+  // Bool for tracking if we have seen the start of a scan result series. Used
+  // to avoid catching the tail end of a previous scan result.
+  bool mScanStartSeen = false;
+
   //! Bools indicating that data collection is complete for each side
   bool mApDataCollectionDone = false;
   bool mChreDataCollectionDone = false;
@@ -90,22 +92,21 @@
       chre_cross_validation_wifi_StepStartCommand stepStartCommand);
 
   /**
+   * Sends the test result to host.
+   *
    * @param success true if the result was success.
    * @param errMessage The error message that should be sent to host with
    * failure.
-   *
-   * @return The TestResult proto message that is encoded with these fields.
    */
-  chre_test_common_TestResult makeTestResultProtoMessage(
-      bool success, const char *errMessage = nullptr);
+  void sendTestResult(bool success, const char *errorMessage = nullptr) const;
 
   /**
    * @param capabilitiesFromChre The number with flags that represent the
    *        different wifi capabilities.
    * @return The wifi capabilities proto message for the host.
    */
-  chre_cross_validation_wifi_WifiCapabilities makeWifiCapabilitiesMessage(
-      uint32_t capabilitiesFromChre);
+  static chre_cross_validation_wifi_WifiCapabilities
+  makeWifiCapabilitiesMessage(uint32_t capabilitiesFromChre);
 
   /**
    * Handle a wifi scan result data message sent from AP.
@@ -144,43 +145,21 @@
    * @return               The index of the matched scan result in the list if
    *                       found, otherwise SIZE_MAX.
    */
-  size_t getMatchingScanResultIndex(
+  static size_t getMatchingScanResultIndex(
       const DynamicVector<WifiScanResult> &results,
       const WifiScanResult &queryResult);
 
   /**
-   * Setup WiFi scan monitoring from CHRE apis.
-   *
-   * @return true if chreWifiConfigureScanMonitorAsync() returns true
-   */
-  bool setupWifiScanMonitoring();
-
-  /**
    * Handle wifi async result event with event data.
    *
    * @param result The data for the event.
    */
   void handleWifiAsyncResult(const chreAsyncResult *result);
-
-  /**
-   * The function to pass as the encode function pointer for the errorMessage
-   * field of the TestResult message.
-   *
-   * @param stream The stream to write bytes to.
-   * @param field The field that should be encoded. Unused by us.
-   * @param arg The argument that will be set to a pointer to the string to
-   * encode as error message.
-   */
-  static bool encodeErrorMessage(pb_ostream_t *stream,
-                                 const pb_field_t * /*field*/,
-                                 void *const *arg);
 };
 
 // The chre cross validator manager singleton.
 typedef chre::Singleton<Manager> ManagerSingleton;
 
-}  // namespace cross_validator_wifi
-
-}  // namespace chre
+}  // namespace chre::cross_validator_wifi
 
 #endif  // CHRE_CROSS_VALIDATOR_WIFI_MANAGER_H_
diff --git a/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc b/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc
index 4bd78ce..a210237 100644
--- a/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc
+++ b/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc
@@ -15,25 +15,15 @@
 
 #include "chre_cross_validator_wifi_manager.h"
 
-#include <stdio.h>
-#include <algorithm>
 #include <cinttypes>
 #include <cstring>
 
-#include "chre/util/nanoapp/assert.h"
-#include "chre/util/nanoapp/callbacks.h"
 #include "chre/util/nanoapp/log.h"
 #include "chre/util/nanoapp/wifi.h"
 #include "chre_api/chre.h"
-#include "chre_cross_validation_wifi.nanopb.h"
-#include "chre_test_common.nanopb.h"
 #include "send_message.h"
 
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
-namespace chre {
-
-namespace cross_validator_wifi {
+namespace chre::cross_validator_wifi {
 
 // Fake scan monitor cookie which is not used
 constexpr uint32_t kScanMonitoringCookie = 0;
@@ -62,31 +52,31 @@
                                     const chreMessageFromHostData *hostData) {
   if (senderInstanceId != CHRE_INSTANCE_ID) {
     LOGE("Incorrect sender instance id: %" PRIu32, senderInstanceId);
-  } else {
-    mCrossValidatorState.hostEndpoint = hostData->hostEndpoint;
-    switch (hostData->messageType) {
-      case chre_cross_validation_wifi_MessageType_STEP_START: {
-        pb_istream_t stream = pb_istream_from_buffer(
-            static_cast<const pb_byte_t *>(
-                const_cast<const void *>(hostData->message)),
-            hostData->messageSize);
-        chre_cross_validation_wifi_StepStartCommand stepStartCommand;
-        if (!pb_decode(&stream,
-                       chre_cross_validation_wifi_StepStartCommand_fields,
-                       &stepStartCommand)) {
-          LOGE("Error decoding StepStartCommand");
-        } else {
-          handleStepStartMessage(stepStartCommand);
-        }
+    return;
+  }
+  mCrossValidatorState.hostEndpoint = hostData->hostEndpoint;
+  switch (hostData->messageType) {
+    case chre_cross_validation_wifi_MessageType_STEP_START: {
+      pb_istream_t stream = pb_istream_from_buffer(
+          static_cast<const pb_byte_t *>(
+              const_cast<const void *>(hostData->message)),
+          hostData->messageSize);
+      chre_cross_validation_wifi_StepStartCommand stepStartCommand;
+      if (!pb_decode(&stream,
+                     chre_cross_validation_wifi_StepStartCommand_fields,
+                     &stepStartCommand)) {
+        LOGE("Error decoding StepStartCommand");
         break;
       }
-      case chre_cross_validation_wifi_MessageType_SCAN_RESULT:
-        handleDataMessage(hostData);
-        break;
-      default:
-        LOGE("Unknown message type %" PRIu32 " for host message",
-             hostData->messageType);
+      handleStepStartMessage(stepStartCommand);
+      break;
     }
+    case chre_cross_validation_wifi_MessageType_SCAN_RESULT:
+      handleDataMessage(hostData);
+      break;
+    default:
+      LOGE("Unknown message type %" PRIu32 " for host message",
+           hostData->messageType);
   }
 }
 
@@ -97,6 +87,7 @@
       LOGE("Received StepStartCommand for INIT step");
       break;
     case chre_cross_validation_wifi_Step_CAPABILITIES: {
+      LOGD("%s: Received Step_CAPABILITIES", __func__);
       chre_cross_validation_wifi_WifiCapabilities wifiCapabilities =
           makeWifiCapabilitiesMessage(chreWifiGetCapabilities());
       test_shared::sendMessageToHost(
@@ -106,24 +97,28 @@
       break;
     }
     case chre_cross_validation_wifi_Step_SETUP: {
-      if (!chreWifiConfigureScanMonitorAsync(true /* enable */,
+      if (!chreWifiConfigureScanMonitorAsync(/* enable= */ true,
                                              &kScanMonitoringCookie)) {
         LOGE("chreWifiConfigureScanMonitorAsync() failed");
         test_shared::sendTestResultWithMsgToHost(
             mCrossValidatorState.hostEndpoint,
             chre_cross_validation_wifi_MessageType_STEP_RESULT,
-            false /*success*/, "setupWifiScanMonitoring failed",
-            false /*abortOnFailure*/);
-      } else {
-        LOGD("chreWifiConfigureScanMonitorAsync() succeeded");
-        if (stepStartCommand.has_chreScanCapacity) {
-          mExpectedMaxChreResultCanHandle = stepStartCommand.chreScanCapacity;
-        }
+            /*success=*/false,
+            /* errMessage= */ "setupWifiScanMonitoring failed",
+            /*abortOnFailure=*/false);
+        break;
+      }
+      LOGD("chreWifiConfigureScanMonitorAsync() succeeded");
+      if (stepStartCommand.has_chreScanCapacity) {
+        mExpectedMaxChreResultCanHandle = stepStartCommand.chreScanCapacity;
       }
       break;
     }
     case chre_cross_validation_wifi_Step_VALIDATE:
       break;
+    default:
+      LOGE("Unexpected start step: %" PRIu8,
+           static_cast<uint8_t>(stepStartCommand.step));
   }
   mStep = stepStartCommand.step;
 }
@@ -137,28 +132,40 @@
   uint8_t scanResultIndex = scanResult.getResultIndex();
   if (scanResultIndex > scanResult.getTotalNumResults()) {
     LOGE("AP scan result index is greater than scan results size");
-  } else {
-    if (!mApScanResults.push_back(scanResult)) {
-      LOG_OOM();
-    }
-    if (scanResult.isLastMessage()) {
-      mApDataCollectionDone = true;
-      if (mChreDataCollectionDone) {
-        compareAndSendResultToHost();
-      }
-    }
+    return;
+  }
+  if (!mApScanResults.push_back(scanResult)) {
+    LOG_OOM();
+  }
+  LOGD("%s: AP wifi result %" PRIu8 "/%" PRIu8 " is received", __func__,
+       static_cast<uint8_t>(scanResultIndex + 1),
+       scanResult.getTotalNumResults());
+  if (!scanResult.isLastMessage()) {
+    return;
+  }
+  mApDataCollectionDone = true;
+  if (mChreDataCollectionDone) {
+    compareAndSendResultToHost();
   }
 }
 
 void Manager::handleWifiScanResult(const chreWifiScanEvent *event) {
+  if (!mScanStartSeen && event->eventIndex != 0) {
+    LOGW("Dropping chreWifiScanEvent because we haven't seen eventIndex=0");
+    return;
+  }
+  mScanStartSeen = true;
   for (uint8_t i = 0; i < event->resultCount; i++) {
     mChreScanResults.push_back(event->results[i]);
   }
-  if (mChreScanResults.size() >= event->resultTotal) {
-    mChreDataCollectionDone = true;
-    if (mApDataCollectionDone) {
-      compareAndSendResultToHost();
-    }
+  LOGD("%s: CHRE wifi result %zu/%" PRIu8 " is received", __func__,
+       mChreScanResults.size(), event->resultTotal);
+  if (mChreScanResults.size() < event->resultTotal) {
+    return;
+  }
+  mChreDataCollectionDone = true;
+  if (mApDataCollectionDone) {
+    compareAndSendResultToHost();
   }
 }
 
@@ -178,17 +185,12 @@
   verifyScanResults(&testResult);
 
   if (belowMaxSizeCheck || aboveMaxSizeCheck) {
-    LOGE(
-        "AP and CHRE wifi scan result counts differ, AP = %zu, CHRE = %zu, MAX "
-        "= %" PRIu8,
-        mApScanResults.size(), mChreScanResults.size(),
-        mExpectedMaxChreResultCanHandle);
-    test_shared::sendTestResultWithMsgToHost(
-        mCrossValidatorState.hostEndpoint,
-        chre_cross_validation_wifi_MessageType_STEP_RESULT, false /*success*/,
-        "There is a different number of AP and CHRE scan results.",
-        false /*abortOnFailure*/);
-
+    LOGE("Scan results differ: AP = %zu, CHRE = %zu, MAX = %" PRIu8,
+         mApScanResults.size(), mChreScanResults.size(),
+         mExpectedMaxChreResultCanHandle);
+    sendTestResult(/*success=*/false,
+                   /* errorMessage= */
+                   "There is a different number of AP and CHRE scan results.");
     return;
   }
 
@@ -200,32 +202,32 @@
 
 void Manager::verifyScanResults(chre_test_common_TestResult *testResultOut) {
   bool allResultsValid = true;
-  for (uint8_t i = 0; i < mChreScanResults.size(); i++) {
-    const WifiScanResult chreScanResult = WifiScanResult(mChreScanResults[i]);
+  for (const chreWifiScanResult &result : mChreScanResults) {
+    const WifiScanResult chreWifiScanResult = WifiScanResult(result);
     bool isValidResult = true;
-    size_t index = getMatchingScanResultIndex(mApScanResults, chreScanResult);
+    size_t index =
+        getMatchingScanResultIndex(mApScanResults, chreWifiScanResult);
 
     const char *bssidStr = "<non-printable>";
     char bssidBuffer[chre::kBssidStrLen];
-    if (chre::parseBssidToStr(chreScanResult.getBssid(), bssidBuffer,
+    if (chre::parseBssidToStr(chreWifiScanResult.getBssid(), bssidBuffer,
                               sizeof(bssidBuffer))) {
       bssidStr = bssidBuffer;
     }
 
+    // chreWifiScanResult is found
     if (index != SIZE_MAX) {
       WifiScanResult &apScanResult = mApScanResults[index];
       if (apScanResult.getSeen()) {
-        *testResultOut = makeTestResultProtoMessage(
-            false, "Saw a CHRE scan result with a duplicate BSSID.");
+        *testResultOut = test_shared::makeTestResultProtoMessage(
+            /*success=*/false, "Saw a CHRE scan result with a duplicate BSSID");
         isValidResult = false;
-        LOGE("CHRE Scan Result with bssid: %s has a dupplicate BSSID",
-             bssidStr);
+        LOGE("CHRE Scan Result with bssid: %s has a duplicate BSSID", bssidStr);
       }
-      if (!WifiScanResult::areEqual(chreScanResult, apScanResult)) {
-        *testResultOut =
-            makeTestResultProtoMessage(false,
-                                       "Fields differ between an AP and "
-                                       "CHRE scan result with same Bssid.");
+      if (!WifiScanResult::areEqual(chreWifiScanResult, apScanResult)) {
+        *testResultOut = test_shared::makeTestResultProtoMessage(
+            /*success=*/false,
+            "Fields differ between an AP and CHRE scan result with same Bssid");
         isValidResult = false;
         LOGE(
             "CHRE Scan Result with bssid: %s found fields differ with "
@@ -237,9 +239,9 @@
       apScanResult.didSee();
     } else {
       // Error CHRE BSSID does not match any AP
-      *testResultOut = makeTestResultProtoMessage(
-          false,
-          "Could not find an AP scan result with the same Bssid as a CHRE "
+      *testResultOut = test_shared::makeTestResultProtoMessage(
+          /*success=*/false,
+          "Could not find an AP scan result with the same Bssid in CHRE "
           "result");
       isValidResult = false;
       LOGE(
@@ -247,14 +249,15 @@
           "with same Bssid",
           bssidStr);
     }
+
     if (!isValidResult) {
       LOGE("False CHRE Scan Result with the following info:");
-      logChreWifiResult(mChreScanResults[i]);
+      logChreWifiResult(result);
       allResultsValid = false;
     }
   }
 
-  for (auto &scanResult : mApScanResults) {
+  for (const WifiScanResult &scanResult : mApScanResults) {
     if (!scanResult.getSeen()) {
       const char *bssidStr = "<non-printable>";
       char bssidBuffer[chre::kBssidStrLen];
@@ -267,16 +270,17 @@
       // Since CHRE is more constrained in memory, it is expected that if we
       // receive over a cretin amount of AP, we will drop some of them.
       if (mApScanResults.size() <= mExpectedMaxChreResultCanHandle) {
-        *testResultOut = makeTestResultProtoMessage(
-            false /*success*/,
+        *testResultOut = test_shared::makeTestResultProtoMessage(
+            /*success=*/false,
             "Extra AP information shown in host "
             "when small number of AP results presenting");
         allResultsValid = false;
       }
     }
   }
+
   if (allResultsValid) {
-    *testResultOut = makeTestResultProtoMessage(true);
+    *testResultOut = test_shared::makeTestResultProtoMessage(true);
   }
 }
 
@@ -291,32 +295,11 @@
   return SIZE_MAX;
 }
 
-bool Manager::encodeErrorMessage(pb_ostream_t *stream,
-                                 const pb_field_t * /*field*/,
-                                 void *const *arg) {
-  const char *str = static_cast<const char *>(const_cast<const void *>(*arg));
-  size_t len = strlen(str);
-  return pb_encode_tag_for_field(
-             stream, &chre_test_common_TestResult_fields
-                         [chre_test_common_TestResult_errorMessage_tag - 1]) &&
-         pb_encode_string(stream, reinterpret_cast<const pb_byte_t *>(str),
-                          len);
-}
-
-chre_test_common_TestResult Manager::makeTestResultProtoMessage(
-    bool success, const char *errMessage) {
-  // TODO(b/154271547): Move this implementation into
-  // common/shared/send_message.cc::sendTestResultToHost
-  chre_test_common_TestResult testResult =
-      chre_test_common_TestResult_init_default;
-  testResult.has_code = true;
-  testResult.code = success ? chre_test_common_TestResult_Code_PASSED
-                            : chre_test_common_TestResult_Code_FAILED;
-  if (!success && errMessage != nullptr) {
-    testResult.errorMessage = {.funcs = {.encode = encodeErrorMessage},
-                               .arg = const_cast<char *>(errMessage)};
-  }
-  return testResult;
+void Manager::sendTestResult(bool success, const char *errorMessage) const {
+  test_shared::sendTestResultWithMsgToHost(
+      mCrossValidatorState.hostEndpoint,
+      chre_cross_validation_wifi_MessageType_STEP_RESULT, success, errorMessage,
+      /* abortOnFailure= */ false);
 }
 
 chre_cross_validation_wifi_WifiCapabilities
@@ -328,40 +311,26 @@
 }
 
 void Manager::handleWifiAsyncResult(const chreAsyncResult *result) {
-  chre_test_common_TestResult testResult;
-  bool sendMessage = false;
-  if (result->requestType == CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR) {
-    if (mStep != chre_cross_validation_wifi_Step_SETUP) {
-      testResult = makeTestResultProtoMessage(
-          false, "Received scan monitor result event when step is not SETUP");
-      sendMessage = true;
-    } else {
-      if (result->success) {
-        LOGD("Wifi scan monitoring setup successfully");
-        testResult = makeTestResultProtoMessage(true);
-        sendMessage = true;
-      } else {
-        LOGE("Wifi scan monitoring setup failed async w/ error code %" PRIu8
-             ".",
-             result->errorCode);
-        testResult = makeTestResultProtoMessage(
-            false, "Wifi scan monitoring setup failed async.");
-        sendMessage = true;
-      }
-    }
-  } else {
-    testResult = makeTestResultProtoMessage(
-        false, "Unknown chre async result type received");
-    sendMessage = true;
+  if (result->requestType != CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR) {
+    sendTestResult(/*success=*/false,
+                   /*errorMessage=*/"Unknown chre async result type received");
+    return;
   }
-  if (sendMessage) {
-    test_shared::sendMessageToHost(
-        mCrossValidatorState.hostEndpoint, &testResult,
-        chre_test_common_TestResult_fields,
-        chre_cross_validation_wifi_MessageType_STEP_RESULT);
+  if (mStep != chre_cross_validation_wifi_Step_SETUP) {
+    sendTestResult(
+        /*success=*/false,
+        /*errorMessage=*/"Received scan monitor result but step is not SETUP");
+    return;
+  }
+  if (result->success) {
+    LOGI("Wifi scan monitoring setup successfully");
+    sendTestResult(/*success=*/true);
+  } else {
+    LOGE("Wifi scan monitoring setup failed async w/ error code %" PRIu8,
+         result->errorCode);
+    sendTestResult(/*success=*/false,
+                   /*errorMessage=*/"Wifi scan monitoring setup failed async");
   }
 }
 
-}  // namespace cross_validator_wifi
-
-}  // namespace chre
+}  // namespace chre::cross_validator_wifi
diff --git a/apps/test/common/chre_settings_test/src/chre_settings_test_manager.cc b/apps/test/common/chre_settings_test/src/chre_settings_test_manager.cc
index 85502cc..8ffb6ee 100644
--- a/apps/test/common/chre_settings_test/src/chre_settings_test_manager.cc
+++ b/apps/test/common/chre_settings_test/src/chre_settings_test_manager.cc
@@ -431,7 +431,7 @@
 
           // Retry on CHRE_ERROR_BUSY after a short delay
           mWifiRequestRetries++;
-          uint64_t delay = kOneSecondInNanoseconds;
+          uint64_t delay = kOneSecondInNanoseconds * 2;
           gRangingRequestRetryTimerHandle = chreTimerSet(
               delay, &kRangingRequestRetryTimerCookie, /*oneShot=*/true);
           LOGW(
diff --git a/apps/test/common/shared/inc/send_message.h b/apps/test/common/shared/inc/send_message.h
index d79387d..cd41e34 100644
--- a/apps/test/common/shared/inc/send_message.h
+++ b/apps/test/common/shared/inc/send_message.h
@@ -20,10 +20,16 @@
 #include <pb_encode.h>
 #include <cinttypes>
 
+#include "chre/util/system/napp_permissions.h"
+#include "chre_test_common.nanopb.h"
+
 namespace chre {
 
 namespace test_shared {
 
+chre_test_common_TestResult makeTestResultProtoMessage(
+    bool success, const char *errMessage = nullptr);
+
 /**
  * Same as sendTestResultWithMsgToHost, but doesn't accept an error message and
  * uses the free callback specified in chre/util/nanoapp/callbacks.h
@@ -55,7 +61,7 @@
 void sendEmptyMessageToHost(uint16_t hostEndpointId, uint32_t messageType);
 
 /**
- * Sends a message to the host.
+ * Sends a message to the host with default NanoappPermissions (CHRE_PERMS_NONE)
  *
  * @param hostEndpointId The endpoint Id of the host to send the message to.
  * @param message The proto message struct pointer.
@@ -65,6 +71,21 @@
 void sendMessageToHost(uint16_t hostEndpointId, const void *message,
                        const pb_field_t *fields, uint32_t messageType);
 
+/**
+ * Sends a message to the host with the provided NanoappPermissions.
+ *
+ * @param hostEndpointId The endpoint Id of the host to send the message to.
+ * @param message The proto message struct pointer.
+ * @param fields The fields descriptor of the proto message to encode.
+ * @param messageType The message type of the message.
+   @param perms The NanoappPermissions associated with the message.
+ */
+void sendMessageToHostWithPermissions(uint16_t hostEndpointId,
+                                      const void *message,
+                                      const pb_field_t *fields,
+                                      uint32_t messageType,
+                                      chre::NanoappPermissions perms);
+
 }  // namespace test_shared
 
 }  // namespace chre
diff --git a/apps/test/common/shared/src/send_message.cc b/apps/test/common/shared/src/send_message.cc
index 9f600fc..949f7fd 100644
--- a/apps/test/common/shared/src/send_message.cc
+++ b/apps/test/common/shared/src/send_message.cc
@@ -17,19 +17,17 @@
 #include "send_message.h"
 
 #include <pb_encode.h>
-#include <cinttypes>
 
 #include "chre/util/nanoapp/callbacks.h"
 #include "chre/util/nanoapp/log.h"
+#include "chre/util/system/napp_permissions.h"
 #include "chre_api/chre.h"
-#include "chre_test_common.nanopb.h"
 
 #ifndef LOG_TAG
 #define LOG_TAG "[TestShared]"
 #endif
 
-namespace chre {
-namespace test_shared {
+namespace chre::test_shared {
 namespace {
 
 bool encodeErrorMessage(pb_ostream_t *stream, const pb_field_t * /*field*/,
@@ -45,6 +43,21 @@
 
 }  // namespace
 
+chre_test_common_TestResult makeTestResultProtoMessage(bool success,
+                                                       const char *errMessage) {
+  chre_test_common_TestResult testResult =
+      chre_test_common_TestResult_init_default;
+  testResult.has_code = true;
+  testResult.code = success ? chre_test_common_TestResult_Code_PASSED
+                            : chre_test_common_TestResult_Code_FAILED;
+  if (!success && errMessage != nullptr) {
+    testResult.errorMessage = {.funcs = {.encode = encodeErrorMessage},
+                               .arg = const_cast<char *>(errMessage)};
+    LOGE("%s", errMessage);
+  }
+  return testResult;
+}
+
 void sendTestResultWithMsgToHost(uint16_t hostEndpointId, uint32_t messageType,
                                  bool success, const char *errMessage,
                                  bool abortOnFailure) {
@@ -55,15 +68,8 @@
     success = false;
   }
 
-  chre_test_common_TestResult result = chre_test_common_TestResult_init_default;
-  result.has_code = true;
-  result.code = success ? chre_test_common_TestResult_Code_PASSED
-                        : chre_test_common_TestResult_Code_FAILED;
-  if (!success && errMessage != nullptr) {
-    result.errorMessage = {.funcs = {.encode = encodeErrorMessage},
-                           .arg = const_cast<char *>(errMessage)};
-    LOGE("%s", errMessage);
-  }
+  chre_test_common_TestResult result =
+      makeTestResultProtoMessage(success, errMessage);
 
   sendMessageToHost(hostEndpointId, &result, chre_test_common_TestResult_fields,
                     messageType);
@@ -95,6 +101,15 @@
 
 void sendMessageToHost(uint16_t hostEndpointId, const void *message,
                        const pb_field_t *fields, uint32_t messageType) {
+  sendMessageToHostWithPermissions(hostEndpointId, message, fields, messageType,
+                                   chre::NanoappPermissions::CHRE_PERMS_NONE);
+}
+
+void sendMessageToHostWithPermissions(uint16_t hostEndpointId,
+                                      const void *message,
+                                      const pb_field_t *fields,
+                                      uint32_t messageType,
+                                      chre::NanoappPermissions perms) {
   size_t size;
   if (!pb_get_encoded_size(&size, fields, message)) {
     LOGE("Failed to get message size");
@@ -107,15 +122,13 @@
       if (!pb_encode(&stream, fields, message)) {
         LOGE("Failed to encode message error %s", PB_GET_ERROR(&stream));
         chreHeapFree(bytes);
-      } else if (!chreSendMessageToHostEndpoint(bytes, size, messageType,
-                                                hostEndpointId,
-                                                heapFreeMessageCallback)) {
+      } else if (!chreSendMessageWithPermissions(
+                     bytes, size, messageType, hostEndpointId,
+                     static_cast<uint32_t>(perms), heapFreeMessageCallback)) {
         LOGE("Failed to send message to host");
       }
     }
   }
 }
 
-}  // namespace test_shared
-
-}  // namespace chre
+}  // namespace chre::test_shared
diff --git a/apps/wifi_world/wifi_world.cc b/apps/wifi_world/wifi_world.cc
index b0f2eb0..92767c6 100644
--- a/apps/wifi_world/wifi_world.cc
+++ b/apps/wifi_world/wifi_world.cc
@@ -208,8 +208,6 @@
     if (result->cookie != &kOnDemandScanCookie) {
       LOGE("On-demand scan cookie mismatch");
     }
-
-    requestDelayedWifiScan();
   } else if (result->requestType == CHRE_WIFI_REQUEST_TYPE_RANGING) {
     uint64_t timeSinceRequest = chreGetTime() - gLastRangingTimeNs;
     if (result->success) {
@@ -277,9 +275,10 @@
  * @param event a pointer to the details of the WiFi scan event.
  */
 void handleWifiScanEvent(const chreWifiScanEvent *event) {
-  LOGI("Received Wifi scan event of type %" PRIu8 " with %" PRIu8
-       " results at %" PRIu64 "ns",
-       event->scanType, event->resultCount, event->referenceTime);
+  LOGI("Received Wifi scan event of type %" PRIu8 " index %" PRIu8
+       " with %" PRIu8 "/%" PRIu8 " results completed at %" PRIu64 "ns",
+       event->scanType, event->eventIndex, event->resultCount,
+       event->resultTotal, event->referenceTime);
 
   if (gPendingOnDemandScan) {
     uint64_t timeSinceRequest = chreGetTime() - gLastRequestTimeNs;
@@ -349,6 +348,8 @@
   } else {
     LOGE("Received invalid timer handle");
   }
+
+  requestDelayedWifiScan();
 }
 
 }  // namespace
diff --git a/build/common.mk b/build/common.mk
index 8de6ed4..7a3a490 100644
--- a/build/common.mk
+++ b/build/common.mk
@@ -47,8 +47,8 @@
 include $(CHRE_PREFIX)/build/clean.mk
 include $(CHRE_PREFIX)/build/tools_config.mk
 
-# Flag config
-include $(CHRE_PREFIX)/build/embedded_flags.mk
+# Flag config (currently disabled)
+# include $(CHRE_PREFIX)/build/embedded_flags.mk
 
 # NanoPB Source Generation
 include $(CHRE_PREFIX)/build/nanopb.mk
diff --git a/build/nanopb.mk b/build/nanopb.mk
index 75d77af..57a287e 100644
--- a/build/nanopb.mk
+++ b/build/nanopb.mk
@@ -204,13 +204,15 @@
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --out-dir=$(PW_RPC_GEN_PATH)/py/pw_rpc/internal \
 	  --compile-dir=$(dir $<) --sources $(PW_RPC_GENERATOR_PROTO) \
-	  --language python
+	  --language python \
+	  --no-experimental-editions
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \
 	  --compile-dir=$(dir $<) --sources $(PW_RPC_GENERATOR_PROTO) \
-	  --language pwpb
+	  --language pwpb \
+	  --no-experimental-editions
 
 # Generated PW RPC Files #######################################################
 
@@ -266,7 +268,7 @@
 
 # Pigweed RPC sources
 COMMON_SRCS += $(PIGWEED_DIR)/pw_assert_log/assert_log.cc
-COMMON_SRCS += $(PIGWEED_DIR)/pw_containers/intrusive_list.cc
+COMMON_SRCS += $(PIGWEED_DIR)/pw_containers/intrusive_item.cc
 COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/decoder.cc
 COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/encoder.cc
 COMMON_SRCS += $(PIGWEED_DIR)/pw_protobuf/stream_decoder.cc
@@ -326,26 +328,31 @@
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(NANOPB_PROTOC) \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb \
+	  --no-experimental-editions \
 	  --sources $<
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb \
+	  --no-experimental-editions \
 	  --sources $<
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_nanopb.py \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb_rpc \
+	  --no-experimental-editions \
 	  --sources $<
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_raw.py \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language raw_rpc \
+	  --no-experimental-editions \
 	  --sources $<
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_pwpb.py \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb_rpc \
+	  --no-experimental-editions \
 	  --sources $<
 
 $(PW_RPC_GEN_PATH)/%.pb.c \
@@ -359,26 +366,31 @@
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(NANOPB_PROTOC) \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb \
+	  --no-experimental-editions \
 	  --sources $<
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_protobuf/py/pw_protobuf/plugin.py \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb \
+	  --no-experimental-editions \
 	  --sources $<
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_nanopb.py \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language nanopb_rpc \
+	  --no-experimental-editions \
 	  --sources $<
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_raw.py \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language raw_rpc \
+	  --no-experimental-editions \
 	  --sources $<
 
 	$(V)$(PW_RPC_GENERATOR_CMD) $(PW_RPC_PROTO_GENERATOR) \
 	  --plugin-path=$(PIGWEED_DIR)/pw_rpc/py/pw_rpc/plugin_pwpb.py \
 	  --out-dir=$(PW_RPC_GEN_PATH)/$(dir $<) --compile-dir=$(dir $<) --language pwpb_rpc \
+	  --no-experimental-editions \
 	  --sources $<
 
 endif # ifneq ($(PW_RPC_SRCS),)
diff --git a/build/sys_support/qcom/chre.scons b/build/sys_support/qcom/chre.scons
deleted file mode 100644
index f911d59..0000000
--- a/build/sys_support/qcom/chre.scons
+++ /dev/null
@@ -1,382 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#===============================================================================
-#
-# CHRE builder scons
-#
-# GENERAL DESCRIPTION
-#    Build script used to build the CHRE library for use on SLPI
-#
-#-------------------------------------------------------------------------------
-
-Import('env')
-import os
-
-#-------------------------------------------------------------------------------
-# Configure optional features
-#-------------------------------------------------------------------------------
-
-# There are two options to configure the optional features that CHRE bundles in:
-#  1. Set the flags below to True/False depending on which features are desired
-#  2. Set the command-line flag -f USES_CHRE_BUILD_FLAGS to ignore the defaults
-#     and control the features by presence of -f USES_CHRE_WIFI, etc. in the
-#     command line flags
-USE_CHRE_AUDIO = False  # not currently supported
-if 'USES_CHRE_BUILD_FLAGS' not in env:
-    # Option 1
-    USE_CHRE_GNSS = True
-    USE_CHRE_WIFI = True
-    USE_CHRE_WWAN = True
-else:
-    # Option 2
-    USE_CHRE_GNSS = 'USES_CHRE_GNSS' in env
-    USE_CHRE_WIFI = 'USES_CHRE_WIFI' in env
-    USE_CHRE_WWAN = 'USES_CHRE_WWAN' in env
-
-#-------------------------------------------------------------------------------
-# Configure testing nanoapps
-#-------------------------------------------------------------------------------
-
-# CHRE provides several nanoapps that can be used to verify that APIs are
-# working as expected. To use these nanoapps, set 1 or more of the following
-# flags to True and recompile CHRE. Once restarted, CHRE will load the enabled
-# nanoapps
-LOAD_GNSS_WORLD = False
-LOAD_SENSOR_WORLD = False
-LOAD_WIFI_WORLD = False
-LOAD_WWAN_WORLD = False
-
-TESTING_NANOAPPS_ENABLED = False
-if LOAD_GNSS_WORLD or LOAD_SENSOR_WORLD or LOAD_WIFI_WORLD or LOAD_WWAN_WORLD:
-    TESTING_NANOAPPS_ENABLED = True
-
-#-------------------------------------------------------------------------------
-# Setup source and tooling paths
-#-------------------------------------------------------------------------------
-SRCPATH = "${BUILD_ROOT}/chre/chre/src"
-LIBNAME = 'libchre_slpi_skel'
-env.VariantDir('${BUILDPATH}', SRCPATH, duplicate=0)
-LIB_ROOT = "${BUILD_ROOT}/chre/chre"
-
-target_so = '${BUILDPATH}/'+LIBNAME+'.so'
-hexagon_root = env.get('HEXAGON_ROOT')
-hexgaon_rtos_release = env.get('HEXAGON_RTOS_RELEASE')
-hexagon_version = env.get('Q6VERSION')
-HEXAGON_LIB_PATH = "{0}/{1}/Tools/target/hexagon/lib/{2}/G0/pic".format(hexagon_root,
-    hexgaon_rtos_release,hexagon_version)
-
-#-------------------------------------------------------------------------------
-# Setup various env flags
-#-------------------------------------------------------------------------------
-env.Replace(ASM_DFLAGS = '-D__V_DYNAMIC__')
-env.Replace(CC_DFLAGS = '-D__V_DYNAMIC__')
-env.Replace(HEXAGONCC_OPT = ' -Os ')
-env.Replace(CFLAGS = ' -G0')
-
-env.Replace(HEXAGONCC_WARN = '-Wall  -Wno-cast-align -Wpointer-arith -Wno-missing-braces' \
-                             ' -Wno-strict-aliasing -Wstrict-prototypes -Wnested-externs -Werror')
-env.Replace(HEXAGON_UNDEF = ' ')
-env.Replace(HEXAGON_LANIND = '-fno-exceptions -fno-strict-aliasing -fno-zero-initialized-in-bss' \
-                             ' -fdata-sections -fno-signed-char -fdiagnostics-show-option ')
-env.Append(SHCXXFLAGS = ' -std=c++11 ')
-env.Append(SHLINKFLAGS = '-G0 --wrap=malloc --wrap=calloc --wrap=free --wrap=realloc --wrap=memalign' \
-                         ' --wrap=__stack_chk_fail -call_shared ')
-
-env['SKEL_DEP'] = ''
-if USE_CHRE_GNSS:
-    env['SKEL_DEP'] += ' ${BUILD_ROOT}/chre/chre_drv_loc/build/${BUILDPATH}/chre_drv_loc.so '
-    env.Depends(target_so,'${BUILD_ROOT}/chre/chre_drv_loc/build/${BUILDPATH}/chre_drv_loc.so')
-
-if USE_CHRE_WIFI:
-    env['SKEL_DEP'] += ' ${BUILD_ROOT}/chre/chre_drv_wifi/build/${BUILDPATH}/chre_drv_wifi.so ' \
-                       ' ${BUILD_ROOT}/chre/lowi_client/build/${BUILDPATH}/lowi_client.so '
-    env.Depends(target_so,'${BUILD_ROOT}/chre/chre_drv_wifi/build/${BUILDPATH}/chre_drv_wifi.so')
-    env.Depends(target_so,'${BUILD_ROOT}/chre/lowi_client/build/${BUILDPATH}/lowi_client.so')
-
-if USE_CHRE_WWAN:
-    env['SKEL_DEP'] += ' ${BUILD_ROOT}/chre/chre_drv_modem/build/${BUILDPATH}/chre_drv_modem.so '
-    env.Depends(target_so,'${BUILD_ROOT}/chre/chre_drv_modem/build/${BUILDPATH}/chre_drv_modem.so')
-
-env.Replace(QDSP6OBJS1= '${QDSP6_RELEASE_DIR}/Tools/target/hexagon/lib/${Q6VERSION}/G0/pic/initS.o')
-env.Replace(QDSP6OBJS3= ['${QDSP6_RELEASE_DIR}/Tools/target/hexagon/lib/${Q6VERSION}/G0/pic/libgcc.a',
-                         '${QDSP6_RELEASE_DIR}/Tools/target/hexagon/lib/${Q6VERSION}/G0/pic/libstdc++.a',
-                         '${QDSP6_RELEASE_DIR}/Tools/target/hexagon/lib/${Q6VERSION}/G0/pic/libc++.a'])
-env.Replace(QDSP6OBJS4= '${QDSP6_RELEASE_DIR}/Tools/target/hexagon/lib/${Q6VERSION}/G0/pic/finiS.o')
-
-env['SHLINKGRP'] = ' -shared -fPIC -Bsymbolic --start-group $QDSP6OBJS1 $QDSP6OBJS3 $SOURCES.posix' \
-                   ' $QDSP6OBJS4 $SKEL_DEP --end-group ${HEXAGONLD_MAP_CMD} ${TARGET.posix}.map'
-
-# Tells SCons to link various shared libraries against the CHRE library (drivers, various Hexagon libraries).
-env.Replace(SHLINKCOM="${TEMPFILE('$SHLINK $SHLINKFLAGS $HEXAGON_OUTPUT_CMD $TARGET.posix $SHLINKGRP')}")
-
-#-------------------------------------------------------------------------------
-# Ensure required libraries are available to the CHRE source code
-#-------------------------------------------------------------------------------
-core_public_apis = [
-    'KERNEL',
-    'SERVICES',
-    'DEBUGTOOLS',
-    'ADSPPM',
-    'POWER',
-    'MPROC',
-    'SYSTEMDRIVERS',
-    'DEBUGTRACE',
-    'DAL',
-    'DIAG',
-    'BUSES',
-]
-
-platform_public_apis = [
-    'PLATFORM_LIBS',
-    'MOD_TABLE',
-    'QAIC',
-    'STDDEF',
-    'REMOTE',
-    'HAPSDK',
-    'DLW',
-    'PLS',
-    'UTILS',
-    'ADSP_MMAP',
-    'A1STD',
-    'RTLD',
-]
-
-qmimsgs_public_apis = [
-    'COMMON',
-    'SNS_CLIENT_API',
-]
-
-chre_priv_api = [
-    "${BUILD_ROOT}/chre/chre/src/system/chre/chre_api/include",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/chre_api/include/chre_api",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/core/include",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/external/flatbuffers/include",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/pal/include",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/platform/include",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/platform/shared/include",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/platform/slpi",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/platform/slpi/include",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/platform/slpi/see/include",
-    "${BUILD_ROOT}/chre/chre/src/system/chre/util/include",
-    "${BUILD_ROOT}/core/api/kernel/libstd/stringl",
-    "${BUILD_ROOT}/qmimsgs/common/api",
-    "${BUILD_ROOT}/ssc_api/pb",
-    "${BUILD_ROOT}/ssc/framework/cm/inc",
-    "${BUILD_ROOT}/ssc/inc",
-    "${BUILD_ROOT}/ssc/inc/internal",
-    "${BUILD_ROOT}/ssc/inc/utils/nanopb",
-]
-
-if TESTING_NANOAPPS_ENABLED:
-    chre_priv_api.append("${BUILD_ROOT}/chre/chre/src/system/chre/apps/include")
-
-env.PublishPrivateApi('CHRE', chre_priv_api)
-
-env.RequirePublicApi(core_public_apis, area='core')
-env.RequirePublicApi(platform_public_apis, area='platform')
-env.RequirePublicApi(qmimsgs_public_apis, area='qmimsgs')
-env.RequirePublicApi(['SNS_API_INCLUDES'], area='ssc_api')
-env.RequirePrivateApi('CHRE')
-
-#-------------------------------------------------------------------------------
-# Define various preprocessor definitions needed to compile CHRE
-#-------------------------------------------------------------------------------
-
-oem_ver = os.environ.get('SCONS_OEM_BUILD_VER')
-if oem_ver is not None:
-    oem_ver = str(oem_ver).strip().strip('"')
-else:
-    oem_ver = 'undefined'
-
-CHRE_DEFINES = [
-    'CHRE_SLPI_DEFAULT_BUILD',
-    'CHRE_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_DEBUG',
-    'NANOAPP_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_DEBUG',
-    'CHRE_ASSERTIONS_DISABLED',
-    'CHRE_NANOAPP_INTERNAL',
-    'CHRE_VERSION_STRING=\\\"' + oem_ver + '\\\"',
-    'CHRE_PATCH_VERSION=1',
-    'CHRE_FILENAME=__FILE__',
-    'CHRE_PLATFORM_ID=0x476f6f676c000005',
-    'CHRE_SEE_NUM_TEMP_SENSORS=1',
-    'FLATBUFFERS_CHRE',
-    'SSC_TARGET_HEXAGON',
-    'CHRE_SLPI_SEE',
-    'PB_FIELD_16BIT',
-    'CHRE_MESSAGE_TO_HOST_MAX_SIZE=4000',
-    'QDSP6',
-    'CHRE_USE_FARF_LOGGING',
-]
-
-if USE_CHRE_GNSS:
-    CHRE_DEFINES.append('CHRE_GNSS_SUPPORT_ENABLED')
-if USE_CHRE_WIFI:
-    CHRE_DEFINES.append('CHRE_WIFI_SUPPORT_ENABLED')
-if USE_CHRE_WWAN:
-    CHRE_DEFINES.append('CHRE_WWAN_SUPPORT_ENABLED')
-
-if TESTING_NANOAPPS_ENABLED:
-    CHRE_DEFINES.append('CHRE_INCLUDE_DEFAULT_STATIC_NANOAPPS')
-if LOAD_GNSS_WORLD:
-    CHRE_DEFINES.append('CHRE_LOAD_GNSS_WORLD')
-if LOAD_SENSOR_WORLD:
-    CHRE_DEFINES.append('CHRE_LOAD_SENSOR_WORLD')
-if LOAD_WIFI_WORLD:
-    CHRE_DEFINES.append('CHRE_LOAD_WIFI_WORLD')
-if LOAD_WWAN_WORLD:
-    CHRE_DEFINES.append('CHRE_LOAD_WWAN_WORLD')
-
-env.Append(CPPDEFINES = CHRE_DEFINES)
-
-#-------------------------------------------------------------------------------
-# Setup source files to be built
-#-------------------------------------------------------------------------------
-
-# Define path to the IDL file used to communicate with the host via FastRPC
-libchre_slpi_skel_idl = "${BUILDPATH}/system/chre/host/msm/daemon/idl/chre_slpi.idl"
-
-# Compile chre_slpi.h from chre_slpi.idl, This is a prereq for the libchre_slpi_skel.so
-prereq = env.HeaderBuilder("${BUILDPATH}/chre_slpi.h", libchre_slpi_skel_idl)
-
-chre_cc_src = [
-    # QMI interfaces
-    "${BUILD_ROOT}/qmimsgs/common/src/common_v01.c",
-    "${BUILD_ROOT}/qmimsgs/sns_client_api/src/sns_client_api_v01.c",
-
-    # Qualcomm protobufs needed by CHRE code
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_amd.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_client.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_suid.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_cal.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_physical_sensor_test.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_proximity.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_remote_proc_state.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_resampler.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_std.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_std_sensor.pb.c",
-    "${BUILD_ROOT}/ssc_api/build/${BUILDPATH}/pb/sns_std_type.pb.c",
-
-    # Core CHRE framework code
-    "${BUILDPATH}/system/chre/core/debug_dump_manager.cc",
-    "${BUILDPATH}/system/chre/core/event.cc",
-    "${BUILDPATH}/system/chre/core/event_loop.cc",
-    "${BUILDPATH}/system/chre/core/event_loop_manager.cc",
-    "${BUILDPATH}/system/chre/core/event_ref_queue.cc",
-    "${BUILDPATH}/system/chre/core/host_comms_manager.cc",
-    "${BUILDPATH}/system/chre/core/init.cc",
-    "${BUILDPATH}/system/chre/core/nanoapp.cc",
-    "${BUILDPATH}/system/chre/core/sensor_request.cc",
-    "${BUILDPATH}/system/chre/core/sensor_request_manager.cc",
-    "${BUILDPATH}/system/chre/core/sensor_request_multiplexer.cc",
-    "${BUILDPATH}/system/chre/core/sensor.cc",
-    "${BUILDPATH}/system/chre/core/sensor_type.cc",
-    "${BUILDPATH}/system/chre/core/sensor_type_helpers.cc",
-    "${BUILDPATH}/system/chre/core/static_nanoapps.cc",
-    "${BUILDPATH}/system/chre/core/timer_pool.cc",
-
-    # CHRE platform-specific implementation
-    "${BUILDPATH}/system/chre/platform/shared/chre_api_audio.cc",
-    "${BUILDPATH}/system/chre/platform/shared/chre_api_core.cc",
-    "${BUILDPATH}/system/chre/platform/shared/chre_api_gnss.cc",
-    "${BUILDPATH}/system/chre/platform/shared/chre_api_re.cc",
-    "${BUILDPATH}/system/chre/platform/shared/chre_api_sensor.cc",
-    "${BUILDPATH}/system/chre/platform/shared/chre_api_version.cc",
-    "${BUILDPATH}/system/chre/platform/shared/chre_api_wifi.cc",
-    "${BUILDPATH}/system/chre/platform/shared/chre_api_wwan.cc",
-    "${BUILDPATH}/system/chre/platform/shared/host_protocol_chre.cc",
-    "${BUILDPATH}/system/chre/platform/shared/host_protocol_common.cc",
-    "${BUILDPATH}/system/chre/platform/shared/memory_manager.cc",
-    "${BUILDPATH}/system/chre/platform/shared/nanoapp_load_manager.cc",
-    "${BUILDPATH}/system/chre/platform/shared/nanoapp/nanoapp_dso_util.cc",
-    "${BUILDPATH}/system/chre/platform/shared/pal_system_api.cc",
-    "${BUILDPATH}/system/chre/platform/shared/platform_debug_dump_manager.cc",
-    "${BUILDPATH}/system/chre/platform/shared/system_time.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/chre_api_re.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/host_link.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/init.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/memory.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/memory_manager.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/platform_nanoapp.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/platform_pal.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/platform_sensor_type_helpers.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/system_time.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/system_time_util.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/system_timer.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/see/island_vote_client.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/see/platform_sensor.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/see/platform_sensor_manager.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/see/power_control_manager.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/see/see_helper.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/see/see_cal_helper.cc",
-    "${BUILDPATH}/system/chre/platform/slpi/see/sns_qmi_client.c",
-
-    # Common utilities
-    "${BUILDPATH}/system/chre/util/system/debug_dump.cc",
-    "${BUILDPATH}/system/chre/util/buffer_base.cc",
-    "${BUILDPATH}/system/chre/util/dynamic_vector_base.cc",
-    "${BUILDPATH}/system/chre/util/hash.cc",
-    "${BUILDPATH}/system/chre/util/nanoapp/audio.cc",
-    "${BUILDPATH}/system/chre/util/nanoapp/ble.cc",
-    "${BUILDPATH}/system/chre/util/nanoapp/callbacks.cc",
-    "${BUILDPATH}/system/chre/util/nanoapp/debug.cc",
-    "${BUILDPATH}/system/chre/util/nanoapp/wifi.cc",
-]
-
-if USE_CHRE_AUDIO:
-    chre_cc_src.extend([
-        "${BUILDPATH}/system/chre/core/audio_request_manager.cc",
-        "${BUILDPATH}/system/chre/platform/slpi/platform_audio.cc",
-    ])
-
-if USE_CHRE_GNSS:
-    chre_cc_src.extend([
-        "${BUILDPATH}/system/chre/core/gnss_manager.cc",
-        "${BUILDPATH}/system/chre/platform/shared/platform_gnss.cc",
-    ])
-
-if USE_CHRE_WIFI:
-    chre_cc_src.extend([
-        "${BUILDPATH}/system/chre/core/wifi_request_manager.cc",
-        "${BUILDPATH}/system/chre/core/wifi_scan_request.cc",
-        "${BUILDPATH}/system/chre/platform/shared/platform_wifi.cc",
-    ])
-
-if USE_CHRE_WWAN:
-    chre_cc_src.extend([
-        "${BUILDPATH}/system/chre/core/wwan_request_manager.cc",
-        "${BUILDPATH}/system/chre/platform/shared/platform_wwan.cc",
-    ])
-
-if LOAD_GNSS_WORLD:
-    chre_cc_src.append("${BUILDPATH}/system/chre/apps/gnss_world/gnss_world.cc")
-if LOAD_SENSOR_WORLD:
-    chre_cc_src.append("${BUILDPATH}/system/chre/apps/sensor_world/sensor_world.cc")
-if LOAD_WIFI_WORLD:
-    chre_cc_src.append("${BUILDPATH}/system/chre/apps/wifi_world/wifi_world.cc")
-if LOAD_WWAN_WORLD:
-    chre_cc_src.append("${BUILDPATH}/system/chre/apps/wwan_world/wwan_world.cc")
-
-#-------------------------------------------------------------------------------
-# Add CHRE as a shared library that should be built
-#-------------------------------------------------------------------------------
-
-target = env.AddMySharedLibrary(['ADSP_SHARED_LIBS'], '${BUILDPATH}/'+LIBNAME, chre_cc_src, libchre_slpi_skel_idl)
-env.Requires(target, prereq)
-
-# Clean / pack rules
-CLEAN_LIST=[]
-CLEAN_LIST.extend(env.FindFiles(['*'], LIB_ROOT + '/inc'))
-CLEAN_LIST.extend(env.FindFiles(['*'], LIB_ROOT + '/src'))
-env.CleanPack(['ADSP_SHARED_LIBS'], CLEAN_LIST)
diff --git a/build/variant/aosp_riscv55e03_tinysys.mk b/build/variant/aosp_riscv55e03_tinysys.mk
index 616ca54..aacd366 100644
--- a/build/variant/aosp_riscv55e03_tinysys.mk
+++ b/build/variant/aosp_riscv55e03_tinysys.mk
@@ -1,89 +1,10 @@
 #
-# Google Reference CHRE Implementation for MTK riscv (v55e03) Tinysys
+# Google Reference CHRE Implementation for MTK riscv (55e03) Tinysys
 #
 
-include $(CHRE_PREFIX)/build/clean_build_template_args.mk
+RISCV_TARGET_NAME = aosp_riscv55e03_tinysys
+RISCV_TARGET_PLATFORM_ID = 0x476f6f676c003000
+RISCV_TOOLCHAIN_TYPE = MRV55E03
+RISCV_CPU_TYPE = MRV55E03
 
-TARGET_NAME = aosp_riscv55e03_tinysys
-ifneq ($(filter $(TARGET_NAME)% all, $(MAKECMDGOALS)),)
-
-ifneq ($(IS_NANOAPP_BUILD),)
-  # Inline functions of ctype.h for nanoapps
-  COMMON_CFLAGS += -DUSE_CHARSET_ASCII
-else
-  # only enforce RISCV_TINYSYS_PREFIX when building CHRE
-  ifeq ($(RISCV_TINYSYS_PREFIX),)
-  $(error "The tinysys code directory needs to be exported as the RISCV_TINYSYS_PREFIX \
-           environment variable")
-  endif
-endif
-
-TARGET_CFLAGS = $(TINYSYS_CFLAGS)
-TARGET_VARIANT_SRCS = $(TINYSYS_SRCS)
-TARGET_BIN_LDFLAGS = $(AOSP_RISCV_TINYSYS_BIN_LDFLAGS)
-TARGET_SO_EARLY_LIBS = $(AOSP_RISCV_TINYSYS_EARLY_LIBS)
-TARGET_SO_LATE_LIBS = $(AOSP_RISCV_TINYSYS_LATE_LIBS)
-TARGET_PLATFORM_ID = 0x476f6f676c003000
-
-# Macros #######################################################################
-TINYSYS_CFLAGS += $(FLATBUFFERS_CFLAGS)
-TINYSYS_CFLAGS += $(MBEDTLS_CFLAGS)
-
-TINYSYS_CFLAGS += -DCFG_DRAM_HEAP_SUPPORT
-TINYSYS_CFLAGS += -DCHRE_LOADER_ARCH=EM_RISCV
-TINYSYS_CFLAGS += -DCHRE_NANOAPP_LOAD_ALIGNMENT=4096
-
-TINYSYS_CFLAGS += -D__riscv
-TINYSYS_CFLAGS += -DMRV55
-TINYSYS_CFLAGS += -D_LIBCPP_HAS_NO_LONG_LONG
-
-TINYSYS_CFLAGS += --target=riscv32-unknown-elf
-TINYSYS_CFLAGS += -march=rv32imafcv
-TINYSYS_CFLAGS += -mcpu=MRV55E03
-
-# Word size for this architecture
-TARGET_CFLAGS += -DCHRE_32_BIT_WORD_SIZE
-
-# chre platform
-TARGET_CFLAGS += -DCHRE_FIRST_SUPPORTED_API_VERSION=CHRE_API_VERSION_1_7
-TARGET_CFLAGS += -DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4096
-TARGET_CFLAGS += -DCHRE_USE_BUFFERED_LOGGING
-# enable static allocation in freertos
-TINYSYS_CFLAGS += -DCFG_STATIC_ALLOCATE
-
-# Compiling flags ##############################################################
-
-# -fpic and -shared are only needed for dynamic linking
-ifeq ($(IS_ARCHIVE_ONLY_BUILD),)
-TARGET_SO_LDFLAGS += -shared
-TARGET_CFLAGS += -fpic
-
-# Enable compiler-rt dependencies
-LLVM_RTLIB=$(RISCV_TOOLCHAIN_PATH)/lib/clang/9.0.1/libpic/riscv32/MRV55E03
-TARGET_SO_LDFLAGS += -L$(LLVM_RTLIB)
-TARGET_SO_LDFLAGS += -lclang_rt.builtins-riscv32
-endif
-
-ifneq ($(IS_NANOAPP_BUILD),)
-# Used to expose libc headers to nanoapps that aren't supported on the given platform
-TARGET_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/include/chre/platform/shared/libc
-
-TARGET_VARIANT_SRCS += $(DSO_SUPPORT_LIB_SRCS)
-TARGET_CFLAGS += $(DSO_SUPPORT_LIB_CFLAGS)
-
-ifeq ($(CHRE_TCM_ENABLED),true)
-TARGET_CFLAGS += -DCHRE_TCM_ENABLED
-# Flags:
-# Signed                 = 0x00000001
-# TCM-capable            = 0x00000004
-TARGET_NANOAPP_FLAGS = 0x00000005
-endif
-endif
-
-# Other makefiles ##############################################################
-
-include $(CHRE_PREFIX)/platform/shared/mbedtls/mbedtls.mk
-include $(CHRE_PREFIX)/build/arch/riscv.mk
-include $(CHRE_PREFIX)/build/build_template.mk
-endif
-
+include $(CHRE_PREFIX)/build/variant/aosp_riscv_tinysys_common.mk
\ No newline at end of file
diff --git a/build/variant/aosp_riscv55e300_tinysys.mk b/build/variant/aosp_riscv55e300_tinysys.mk
index f8ffbf3..0c8c890 100644
--- a/build/variant/aosp_riscv55e300_tinysys.mk
+++ b/build/variant/aosp_riscv55e300_tinysys.mk
@@ -1,90 +1,10 @@
 #
-# Google Reference CHRE Implementation for MTK riscv (v55e300) Tinysys
+# Google Reference CHRE Implementation for MTK riscv (55e300) Tinysys
 #
 
-include $(CHRE_PREFIX)/build/clean_build_template_args.mk
+RISCV_TARGET_NAME = aosp_riscv55e300_tinysys
+RISCV_TARGET_PLATFORM_ID = 0x476f6f676c003001
+RISCV_TOOLCHAIN_TYPE = MRV55E300
+RISCV_CPU_TYPE = MRV55E300
 
-TARGET_NAME = aosp_riscv55e300_tinysys
-ifneq ($(filter $(TARGET_NAME)% all, $(MAKECMDGOALS)),)
-
-ifneq ($(IS_NANOAPP_BUILD),)
-  # Inline functions of ctype.h for nanoapps
-  COMMON_CFLAGS += -DUSE_CHARSET_ASCII
-else
-  # only enforce RISCV_TINYSYS_PREFIX when building CHRE
-  ifeq ($(RISCV_TINYSYS_PREFIX),)
-  $(error "The tinysys code directory needs to be exported as the RISCV_TINYSYS_PREFIX \
-           environment variable")
-  endif
-endif
-
-TARGET_CFLAGS = $(TINYSYS_CFLAGS)
-TARGET_VARIANT_SRCS = $(TINYSYS_SRCS)
-TARGET_BIN_LDFLAGS = $(AOSP_RISCV_TINYSYS_BIN_LDFLAGS)
-TARGET_SO_EARLY_LIBS = $(AOSP_RISCV_TINYSYS_EARLY_LIBS)
-TARGET_SO_LATE_LIBS = $(AOSP_RISCV_TINYSYS_LATE_LIBS)
-TARGET_PLATFORM_ID = 0x476f6f676c003001
-
-# Macros #######################################################################
-
-TINYSYS_CFLAGS += $(FLATBUFFERS_CFLAGS)
-TINYSYS_CFLAGS += $(MBEDTLS_CFLAGS)
-
-TINYSYS_CFLAGS += -DCFG_DRAM_HEAP_SUPPORT
-TINYSYS_CFLAGS += -DCHRE_LOADER_ARCH=EM_RISCV
-TINYSYS_CFLAGS += -DCHRE_NANOAPP_LOAD_ALIGNMENT=4096
-
-TINYSYS_CFLAGS += -D__riscv
-TINYSYS_CFLAGS += -DMRV55
-TINYSYS_CFLAGS += -D_LIBCPP_HAS_NO_LONG_LONG
-
-TINYSYS_CFLAGS += --target=riscv32-unknown-elf
-TINYSYS_CFLAGS += -march=rv32imafcv
-TINYSYS_CFLAGS += -mcpu=MRV55E300
-
-# Word size for this architecture
-TARGET_CFLAGS += -DCHRE_32_BIT_WORD_SIZE
-
-# chre platform
-TARGET_CFLAGS += -DCHRE_FIRST_SUPPORTED_API_VERSION=CHRE_API_VERSION_1_7
-# TODO(b/254121302): Needs to confirm with MTK about the max message size below
-TARGET_CFLAGS += -DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4096
-TARGET_CFLAGS += -DCHRE_USE_BUFFERED_LOGGING
-# TODO(b/256870101): create mutex on heap for now
-TARGET_CFLAGS += -DCHRE_CREATE_MUTEX_ON_HEAP
-
-# Compiling flags ##############################################################
-
-# -fpic and -shared are only needed for dynamic linking
-ifeq ($(IS_ARCHIVE_ONLY_BUILD),)
-TARGET_SO_LDFLAGS += -shared
-TARGET_CFLAGS += -fpic
-
-# Enable compiler-rt dependencies
-LLVM_RTLIB=$(RISCV_TOOLCHAIN_PATH)/lib/clang/12.0.0/libpic/riscv32/MRV55E300
-TARGET_SO_LDFLAGS += -L$(LLVM_RTLIB)
-TARGET_SO_LDFLAGS += -lclang_rt.builtins-riscv32
-endif
-
-ifneq ($(IS_NANOAPP_BUILD),)
-# Used to expose libc headers to nanoapps that aren't supported on the given platform
-TARGET_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/include/chre/platform/shared/libc
-
-TARGET_VARIANT_SRCS += $(DSO_SUPPORT_LIB_SRCS)
-TARGET_CFLAGS += $(DSO_SUPPORT_LIB_CFLAGS)
-
-ifeq ($(CHRE_TCM_ENABLED),true)
-TARGET_CFLAGS += -DCHRE_TCM_ENABLED
-# Flags:
-# Signed                 = 0x00000001
-# TCM-capable            = 0x00000004
-TARGET_NANOAPP_FLAGS = 0x00000005
-endif
-endif
-
-# Other makefiles ##############################################################
-
-include $(CHRE_PREFIX)/platform/shared/mbedtls/mbedtls.mk
-include $(CHRE_PREFIX)/build/arch/riscv.mk
-include $(CHRE_PREFIX)/build/build_template.mk
-endif
+include $(CHRE_PREFIX)/build/variant/aosp_riscv_tinysys_common.mk
\ No newline at end of file
diff --git a/build/variant/aosp_riscv_tinysys_common.mk b/build/variant/aosp_riscv_tinysys_common.mk
new file mode 100644
index 0000000..136f2a4
--- /dev/null
+++ b/build/variant/aosp_riscv_tinysys_common.mk
@@ -0,0 +1,92 @@
+#
+# Google Reference CHRE Implementation for common MTK riscv Tinysys platforms.
+#
+
+# Having a clean start
+include $(CHRE_PREFIX)/build/clean_build_template_args.mk
+
+TARGET_NAME = $(RISCV_TARGET_NAME)
+TARGET_PLATFORM_ID = $(RISCV_TARGET_PLATFORM_ID)
+
+ifneq ($(filter $(TARGET_NAME)% all, $(MAKECMDGOALS)),)
+  TARGET_CFLAGS = $(TINYSYS_CFLAGS)
+  TARGET_VARIANT_SRCS = $(TINYSYS_SRCS)
+  TARGET_BIN_LDFLAGS = $(AOSP_RISCV_TINYSYS_BIN_LDFLAGS)
+  TARGET_SO_EARLY_LIBS = $(AOSP_RISCV_TINYSYS_EARLY_LIBS)
+  TARGET_SO_LATE_LIBS = $(AOSP_RISCV_TINYSYS_LATE_LIBS)
+
+  ifneq ($(IS_NANOAPP_BUILD),)
+    # Inline functions of ctype.h for nanoapps
+    COMMON_CFLAGS += -DUSE_CHARSET_ASCII
+
+    # Used to expose libc headers to nanoapps that aren't supported on the given platform
+    TARGET_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/include/chre/platform/shared/libc
+
+    TARGET_VARIANT_SRCS += $(DSO_SUPPORT_LIB_SRCS)
+    TARGET_CFLAGS += $(DSO_SUPPORT_LIB_CFLAGS)
+
+    ifeq ($(CHRE_TCM_ENABLED),true)
+      TARGET_CFLAGS += -DCHRE_TCM_ENABLED
+      # Flags:
+      # Signed                 = 0x00000001
+      # TCM-capable            = 0x00000004
+      TARGET_NANOAPP_FLAGS = 0x00000005
+    endif
+  else
+    # only enforce RISCV_TINYSYS_PREFIX when building CHRE
+    ifeq ($(RISCV_TINYSYS_PREFIX),)
+    $(error "The tinysys code directory needs to be exported as the RISCV_TINYSYS_PREFIX \
+             environment variable")
+    endif
+  endif
+
+  # Macros #######################################################################
+
+  TINYSYS_CFLAGS += -DCFG_DRAM_HEAP_SUPPORT
+  TINYSYS_CFLAGS += -DCHRE_LOADER_ARCH=EM_RISCV
+  TINYSYS_CFLAGS += -DCHRE_NANOAPP_LOAD_ALIGNMENT=4096
+
+  TINYSYS_CFLAGS += -DCFG_TASK_MULTINOTIFY_SUPPORT
+  TINYSYS_CFLAGS += -mllvm -enable-printf-opt=false
+
+  TINYSYS_CFLAGS += -D__riscv
+  TINYSYS_CFLAGS += -DMRV55
+  TINYSYS_CFLAGS += -D_LIBCPP_HAS_NO_LONG_LONG
+
+  # Word size for this architecture
+  TARGET_CFLAGS += -DCHRE_32_BIT_WORD_SIZE
+
+  # CHRE platform
+  TARGET_CFLAGS += -DCHRE_FIRST_SUPPORTED_API_VERSION=CHRE_API_VERSION_1_7
+  TARGET_CFLAGS += -DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4096
+  TARGET_CFLAGS += -DCHRE_USE_BUFFERED_LOGGING
+
+  # Enable static allocation in freertos
+  TINYSYS_CFLAGS += -DCFG_STATIC_ALLOCATE
+  TINYSYS_CFLAGS += -DconfigSUPPORT_STATIC_ALLOCATION=1
+
+  # Compiling flags ##############################################################
+
+  TINYSYS_CFLAGS += $(FLATBUFFERS_CFLAGS)
+  TINYSYS_CFLAGS += $(MBEDTLS_CFLAGS)
+  TINYSYS_CFLAGS += -mcpu=$(RISCV_CPU_TYPE)
+  TINYSYS_CFLAGS += --target=riscv32-unknown-elf
+  TINYSYS_CFLAGS += -march=rv32imafcv
+
+  # -fpic and -shared are only needed for dynamic linking
+  ifeq ($(IS_ARCHIVE_ONLY_BUILD),)
+    TARGET_SO_LDFLAGS += -shared
+    TARGET_CFLAGS += -fpic
+
+    # Enable compiler-rt dependencies
+    LLVM_RTLIB=$(RISCV_TOOLCHAIN_PATH)/lib/clang/12.0.0/libpic/riscv32/$(RISCV_TOOLCHAIN_TYPE)
+    TARGET_SO_LDFLAGS += -L$(LLVM_RTLIB)
+    TARGET_SO_LDFLAGS += -lclang_rt.builtins-riscv32
+  endif
+
+  # Other makefiles ##############################################################
+
+  include $(CHRE_PREFIX)/platform/shared/mbedtls/mbedtls.mk
+  include $(CHRE_PREFIX)/build/arch/riscv.mk
+  include $(CHRE_PREFIX)/build/build_template.mk
+endif
\ No newline at end of file
diff --git a/chpp/clients/gnss.c b/chpp/clients/gnss.c
index d084b2c..18b0e94 100644
--- a/chpp/clients/gnss.c
+++ b/chpp/clients/gnss.c
@@ -192,7 +192,8 @@
     switch (rxHeader->command) {
       case CHPP_GNSS_OPEN: {
         chppClientProcessOpenResponse(&gnssClientContext->client, buf, len);
-        if (gnssClientContext->requestStateResyncPending) {
+        if (rxHeader->error == CHPP_APP_ERROR_NONE &&
+            gnssClientContext->requestStateResyncPending) {
           gCallbacks->requestStateResync();
           gnssClientContext->requestStateResyncPending = false;
         }
@@ -326,18 +327,13 @@
   chppClientCloseOpenRequests(&gnssClientContext->client, &kGnssClientConfig,
                               false /* clearOnly */);
 
-  if (gnssClientContext->client.openState != CHPP_OPEN_STATE_OPENED &&
-      !gnssClientContext->client.pseudoOpen) {
-    CHPP_LOGW("GNSS client reset but wasn't open");
-  } else {
-    CHPP_LOGD("GNSS client reopening from state=%" PRIu8,
-              gnssClientContext->client.openState);
-    gnssClientContext->requestStateResyncPending = true;
-    chppClientSendOpenRequest(&gGnssClientContext.client,
-                              &gGnssClientContext.outReqStates[CHPP_GNSS_OPEN],
-                              CHPP_GNSS_OPEN,
-                              /*blocking=*/false);
-  }
+  CHPP_LOGD("GNSS client reopening from state=%" PRIu8,
+            gnssClientContext->client.openState);
+  gnssClientContext->requestStateResyncPending = true;
+  chppClientSendOpenRequest(&gGnssClientContext.client,
+                            &gGnssClientContext.outReqStates[CHPP_GNSS_OPEN],
+                            CHPP_GNSS_OPEN,
+                            /*blocking=*/false);
 }
 
 /**
diff --git a/chpp/clients/wifi.c b/chpp/clients/wifi.c
index 2f2b19b..87f28a3 100644
--- a/chpp/clients/wifi.c
+++ b/chpp/clients/wifi.c
@@ -213,7 +213,9 @@
     switch (rxHeader->command) {
       case CHPP_WIFI_OPEN: {
         chppClientProcessOpenResponse(&wifiClientContext->client, buf, len);
-        chppWiFiRecoverScanMonitor(wifiClientContext);
+        if (rxHeader->error == CHPP_APP_ERROR_NONE) {
+          chppWiFiRecoverScanMonitor(wifiClientContext);
+        }
         break;
       }
 
@@ -376,17 +378,12 @@
                               false /* clearOnly */);
   chppCheckWifiScanEventNotificationReset();
 
-  if (wifiClientContext->client.openState != CHPP_OPEN_STATE_OPENED &&
-      !wifiClientContext->client.pseudoOpen) {
-    CHPP_LOGW("WiFi client reset but wasn't open");
-  } else {
-    CHPP_LOGI("WiFi client reopening from state=%" PRIu8,
-              wifiClientContext->client.openState);
-    chppClientSendOpenRequest(&wifiClientContext->client,
-                              &wifiClientContext->outReqStates[CHPP_WIFI_OPEN],
-                              CHPP_WIFI_OPEN,
-                              /*blocking=*/false);
-  }
+  CHPP_LOGI("WiFi client reopening from state=%" PRIu8,
+            wifiClientContext->client.openState);
+  chppClientSendOpenRequest(&wifiClientContext->client,
+                            &wifiClientContext->outReqStates[CHPP_WIFI_OPEN],
+                            CHPP_WIFI_OPEN,
+                            /*blocking=*/false);
 }
 
 /**
@@ -491,9 +488,10 @@
 
   if (len < sizeof(struct ChppWifiConfigureScanMonitorAsyncResponse)) {
     // Short response length indicates an error
-    gCallbacks->scanMonitorStatusChangeCallback(
-        false, chppAppShortResponseErrorHandler(buf, len, "ScanMonitor"));
-
+    uint8_t error = chppAppShortResponseErrorHandler(buf, len, "ScanMonitor");
+    if (!gWifiClientContext.scanMonitorSilenceCallback) {
+      gCallbacks->scanMonitorStatusChangeCallback(false, error);
+    }
   } else {
     struct ChppWifiConfigureScanMonitorAsyncResponseParameters *result =
         &((struct ChppWifiConfigureScanMonitorAsyncResponse *)buf)->params;
@@ -643,7 +641,7 @@
                                         CHPP_WIFI_MAX_TIMESYNC_AGE_NS);
     uint64_t currentTime = chppGetCurrentTimeNs();
     if (correctedTime > currentTime) {
-      CHPP_LOGD("WiFi scan time overcorrected %" PRIu64 " current %" PRIu64,
+      CHPP_LOGW("WiFi scan time overcorrected %" PRIu64 " current %" PRIu64,
                 correctedTime / CHPP_NSEC_PER_MSEC,
                 currentTime / CHPP_NSEC_PER_MSEC);
       correctedTime = currentTime;
@@ -691,6 +689,13 @@
         results[i].timestamp -
         (uint64_t)chppTimesyncGetOffset(gWifiClientContext.client.appContext,
                                         CHPP_WIFI_MAX_TIMESYNC_AGE_NS);
+    uint64_t currentTime = chppGetCurrentTimeNs();
+    if (correctedTime > currentTime) {
+      CHPP_LOGW("WiFi ranging time overcorrected %" PRIu64 " current %" PRIu64,
+                correctedTime / CHPP_NSEC_PER_MSEC,
+                currentTime / CHPP_NSEC_PER_MSEC);
+      correctedTime = currentTime;
+    }
     CHPP_LOGD("WiFi ranging result time corrected from %" PRIu64 "to %" PRIu64,
               results[i].timestamp / CHPP_NSEC_PER_MSEC,
               correctedTime / CHPP_NSEC_PER_MSEC);
@@ -946,10 +951,7 @@
     CHPP_LOG_OOM();
   } else {
     request->header.command = CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC;
-    request->params.enable = enable;
-    request->params.cookie =
-        &gWifiClientContext
-             .outReqStates[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC];
+    request->enable = enable;
 
     result = chppClientSendTimestampedRequestOrFail(
         &gWifiClientContext.client,
diff --git a/chpp/clients/wwan.c b/chpp/clients/wwan.c
index 1ac8aa3..396a100 100644
--- a/chpp/clients/wwan.c
+++ b/chpp/clients/wwan.c
@@ -247,17 +247,12 @@
   chppClientCloseOpenRequests(&wwanClientContext->client, &kWwanClientConfig,
                               false /* clearOnly */);
 
-  if (wwanClientContext->client.openState != CHPP_OPEN_STATE_OPENED &&
-      !wwanClientContext->client.pseudoOpen) {
-    CHPP_LOGW("WWAN client reset but wasn't open");
-  } else {
-    CHPP_LOGI("WWAN client reopening from state=%" PRIu8,
-              wwanClientContext->client.openState);
-    chppClientSendOpenRequest(&wwanClientContext->client,
-                              &wwanClientContext->outReqStates[CHPP_WWAN_OPEN],
-                              CHPP_WWAN_OPEN,
-                              /*blocking=*/false);
-  }
+  CHPP_LOGI("WWAN client reopening from state=%" PRIu8,
+            wwanClientContext->client.openState);
+  chppClientSendOpenRequest(&wwanClientContext->client,
+                            &wwanClientContext->outReqStates[CHPP_WWAN_OPEN],
+                            CHPP_WWAN_OPEN,
+                            /*blocking=*/false);
 }
 
 /**
diff --git a/chpp/include/chpp/common/wifi.h b/chpp/include/chpp/common/wifi.h
index c1a6d2b..239a08b 100644
--- a/chpp/include/chpp/common/wifi.h
+++ b/chpp/include/chpp/common/wifi.h
@@ -37,16 +37,9 @@
  * Data structures used by the Configure Scan Monitor request.
  */
 CHPP_PACKED_START
-struct ChppWifiConfigureScanMonitorAsyncRequestParameters {
-  bool enable;
-  const void *cookie;
-} CHPP_PACKED_ATTR;
-CHPP_PACKED_END
-
-CHPP_PACKED_START
 struct ChppWifiConfigureScanMonitorAsyncRequest {
   struct ChppAppHeader header;
-  struct ChppWifiConfigureScanMonitorAsyncRequestParameters params;
+  bool enable;
 } CHPP_PACKED_ATTR;
 CHPP_PACKED_END
 
diff --git a/chpp/include/chpp/transport.h b/chpp/include/chpp/transport.h
index f25b321..32d8cfe 100644
--- a/chpp/include/chpp/transport.h
+++ b/chpp/include/chpp/transport.h
@@ -38,20 +38,11 @@
  ***********************************************/
 
 /**
- * CHPP Transport layer reset timeout in ns. The transport layer will attempt
- * another reset if the previous reset is not acked in time.
- */
-#ifndef CHPP_TRANSPORT_RESET_TIMEOUT_NS
-#define CHPP_TRANSPORT_RESET_TIMEOUT_NS \
-  (UINT64_C(1500) * CHPP_NSEC_PER_MSEC)  // 1500 ms
-#endif
-
-/**
  * CHPP Transport layer timeout for tx packets.
  */
 #ifndef CHPP_TRANSPORT_TX_TIMEOUT_NS
 #define CHPP_TRANSPORT_TX_TIMEOUT_NS \
-  (UINT64_C(100) * CHPP_NSEC_PER_MSEC)  // 100 ms
+  (UINT64_C(500) * CHPP_NSEC_PER_MSEC)  // 500 ms
 #endif
 
 /**
@@ -71,6 +62,15 @@
 #endif
 
 /**
+ * CHPP Transport layer reset timeout in ns. The transport layer will attempt
+ * another reset if the previous reset is not acked in time.
+ */
+#ifndef CHPP_TRANSPORT_RESET_TIMEOUT_NS
+#define CHPP_TRANSPORT_RESET_TIMEOUT_NS \
+  (UINT64_C(1) * CHPP_TRANSPORT_TX_TIMEOUT_NS * (CHPP_TRANSPORT_MAX_RETX + 1))
+#endif
+
+/**
  * CHPP Transport layer maximum reset attempts. Current functional values are 1
  * or higher (setting to 0 currently functions identically to 1).
  */
diff --git a/chpp/platform/linux/include/chpp/platform/platform_log.h b/chpp/platform/linux/include/chpp/platform/platform_log.h
index 5fdcda7..604e421 100644
--- a/chpp/platform/linux/include/chpp/platform/platform_log.h
+++ b/chpp/platform/linux/include/chpp/platform/platform_log.h
@@ -37,13 +37,16 @@
 
 // TODO: Should use PRIu8 etc. from inttypes.h instead of %d, etc. (add -Wall
 // and -Werror to cflags to catch these)
-#define CHPP_LINUX_LOG(level, color, fmt, ...)                              \
-  {                                                                         \
-    char name[16];                                                          \
-    uint64_t ms = chppGetCurrentTimeNs() / 1000000;                         \
-    pthread_getname_np(pthread_self(), name, 16);                           \
-    printf("\e[" color "m[%" PRIu64 "] %s %s:%d\t (%s) " fmt "\e[0m\n", ms, \
-           level, __FILENAME__, __LINE__, name, ##__VA_ARGS__);             \
+#define CHPP_LINUX_LOG(level, color, fmt, ...)                             \
+  {                                                                        \
+    char name[16];                                                         \
+    uint64_t currentTimeMs = chppGetCurrentTimeNs() / 1000000;             \
+    uint64_t sec = currentTimeMs / 1000;                                   \
+    uint64_t msec = currentTimeMs % 1000;                                  \
+    pthread_getname_np(pthread_self(), name, 16);                          \
+    printf("\e[" color "m[%" PRIu64 ".%03" PRIu64 "] %s %s:%d\t (%s) " fmt \
+           "\e[0m\n",                                                      \
+           sec, msec, level, __FILENAME__, __LINE__, name, ##__VA_ARGS__); \
   }
 
 #define CHPP_LOGE(fmt, ...) CHPP_LINUX_LOG("E", "91", fmt, ##__VA_ARGS__)
diff --git a/chpp/test/fake_link_sync_test.cpp b/chpp/test/fake_link_sync_test.cpp
index f926dd6..d35152b 100644
--- a/chpp/test/fake_link_sync_test.cpp
+++ b/chpp/test/fake_link_sync_test.cpp
@@ -191,4 +191,79 @@
   EXPECT_FALSE(mFakeLink->waitForTxPacket());
 }
 
+// This test is essentially CheckRetryOnTimeout but with a twist: we send a
+// packet, then don't send an ACK in the expected time so it gets retried, then
+// after the retry, we send two equivalent ACKs back-to-back
+TEST_F(FakeLinkSyncTests, DelayedThenDupeAck) {
+  // Post the TX packet
+  txPacket();
+  ASSERT_TRUE(mFakeLink->waitForTxPacket());
+  ASSERT_EQ(mFakeLink->getTxPacketCount(), 1);
+  (void)mFakeLink->popTxPacket();  // discard the first packet
+
+  // Second wait should yield timeout + retry
+  ASSERT_TRUE(mFakeLink->waitForTxPacket());
+  ASSERT_EQ(mFakeLink->getTxPacketCount(), 1);
+
+  // Now deliver duplicate ACKs
+  ChppEmptyPacket ack = generateAck(mFakeLink->popTxPacket());
+  chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&ack),
+               sizeof(ack));
+  chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&ack),
+               sizeof(ack));
+
+  // We shouldn't get another packet (e.g. NAK)
+  EXPECT_FALSE(mFakeLink->waitForTxPacket())
+      << "Got unexpected packet: " << asChpp(mFakeLink->popTxPacket());
+
+  // The next outbound packet should carry the next sequence number
+  txPacket();
+  ASSERT_TRUE(mFakeLink->waitForTxPacket());
+  EXPECT_EQ(asChpp(mFakeLink->popTxPacket()).header.seq, ack.header.ackSeq);
+}
+
+// This tests the opposite side of DelayedThenDuplicateAck: confirms that if we
+// receive a packet, then send an ACK, then we receive a duplicate, we send the
+// ACK again
+TEST_F(FakeLinkSyncTests, ResendAckOnDupe) {
+  // Note that seq and ackSeq should both be 1, since RESET/RESET_ACK will use 0
+  constexpr uint8_t kSeq = 1;
+  constexpr uint8_t kAckSeq = 1;
+  auto rxPkt = generatePacketWithPayload<1>(kAckSeq, kSeq);
+  EXPECT_TRUE(chppRxDataCb(&mTransportContext,
+                           reinterpret_cast<const uint8_t *>(&rxPkt),
+                           sizeof(rxPkt)));
+
+  ASSERT_TRUE(mFakeLink->waitForTxPacket());
+  ASSERT_EQ(mFakeLink->getTxPacketCount(), 1);
+  std::vector<uint8_t> pkt = mFakeLink->popTxPacket();
+  // We should get an ACK in response
+  EXPECT_TRUE(comparePacket(pkt, generateEmptyPacket(kSeq + 1)))
+      << "Expected first ACK for seq 1 but got: " << asEmptyPacket(pkt);
+
+  // Pretend that we lost that ACK, so resend the same packet
+  EXPECT_TRUE(chppRxDataCb(&mTransportContext,
+                           reinterpret_cast<const uint8_t *>(&rxPkt),
+                           sizeof(rxPkt)));
+
+  // We should get another ACK that matches the first
+  ASSERT_TRUE(mFakeLink->waitForTxPacket());
+  ASSERT_EQ(mFakeLink->getTxPacketCount(), 1);
+  pkt = mFakeLink->popTxPacket();
+  EXPECT_TRUE(comparePacket(pkt, generateEmptyPacket(kSeq + 1)))
+      << "Expected second ACK for seq 1 but got: " << asEmptyPacket(pkt);
+
+  // Sending another packet should succeed
+  auto secondRxPkt = generatePacketWithPayload<2>(kAckSeq, kSeq + 1);
+  EXPECT_TRUE(chppRxDataCb(&mTransportContext,
+                           reinterpret_cast<const uint8_t *>(&secondRxPkt),
+                           sizeof(secondRxPkt)));
+
+  ASSERT_TRUE(mFakeLink->waitForTxPacket());
+  ASSERT_EQ(mFakeLink->getTxPacketCount(), 1);
+  pkt = mFakeLink->popTxPacket();
+  EXPECT_TRUE(comparePacket(pkt, generateEmptyPacket(kSeq + 2)))
+      << "Expected ACK for seq 2 but got: " << asEmptyPacket(pkt);
+}
+
 }  // namespace chpp::test
diff --git a/chpp/test/packet_util.cpp b/chpp/test/packet_util.cpp
index 44a4b42..3617227 100644
--- a/chpp/test/packet_util.cpp
+++ b/chpp/test/packet_util.cpp
@@ -16,6 +16,8 @@
 
 #include "packet_util.h"
 
+#include "chpp/app.h"
+
 #include <cstring>
 
 namespace chpp::test {
@@ -82,19 +84,134 @@
   return pkt;
 }
 
-ChppEmptyPacket generateAck(std::vector<uint8_t> &pkt) {
+ChppEmptyPacket generateAck(const std::vector<uint8_t> &pkt) {
   // An ACK consists of an empty packet with the ackSeq set to the received
   // packet's seq + 1 (since ackSeq indicates the next seq value we expect), and
   // seq set to the received packet's ackSeq - 1 (since we don't increment seq
   // on empty packets and ackSeq indicates the next expected seq)
-  ChppTransportHeader &hdr = getHeader(pkt);
+  const ChppTransportHeader &hdr = getHeader(pkt);
   return generateEmptyPacket(/*acqSeq=*/hdr.seq + 1, /*seq=*/hdr.ackSeq - 1);
 }
 
 // Utilities for debugging -----------------------------------------------------
 
+const char *appErrorCodeToStr(uint8_t error) {
+  switch (error) {
+    case CHPP_APP_ERROR_NONE:
+      return "NONE";
+    case CHPP_APP_ERROR_INVALID_COMMAND:
+      return "INVALID_COMMAND";
+    case CHPP_APP_ERROR_INVALID_ARG:
+      return "INVALID_ARG";
+    case CHPP_APP_ERROR_BUSY:
+      return "BUSY";
+    case CHPP_APP_ERROR_OOM:
+      return "OOM";
+    case CHPP_APP_ERROR_UNSUPPORTED:
+      return "UNSUPPORTED";
+    case CHPP_APP_ERROR_TIMEOUT:
+      return "TIMEOUT";
+    case CHPP_APP_ERROR_DISABLED:
+      return "DISABLED";
+    case CHPP_APP_ERROR_RATELIMITED:
+      return "RATELIMITED";
+    case CHPP_APP_ERROR_BLOCKED:
+      return "BLOCKED";
+    case CHPP_APP_ERROR_INVALID_LENGTH:
+      return "INVALID_LENGTH";
+    case CHPP_APP_ERROR_NOT_READY:
+      return "NOT_READY";
+    case CHPP_APP_ERROR_BEYOND_CHPP:
+      return "BEYOND_CHPP";
+    case CHPP_APP_ERROR_UNEXPECTED_RESPONSE:
+      return "UNEXPECTED_RESPONSE";
+    case CHPP_APP_ERROR_CONVERSION_FAILED:
+      return "CONVERSION_FAILED";
+    case CHPP_APP_ERROR_UNSPECIFIED:
+      return "UNSPECIFIED";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+const char *appMessageTypeToStr(uint8_t type) {
+  switch (type) {
+    case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
+      return "CLIENT_REQ";
+    case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
+      return "SERVICE_RESP";
+    case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION:
+      return "CLIENT_NOTIF";
+    case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION:
+      return "SERVICE_NOTIF";
+    case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
+      return "SERVICE_REQ";
+    case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE:
+      return "CLIENT_RESP";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+const char *handleToStr(uint8_t handle) {
+  switch (handle) {
+    case CHPP_HANDLE_NONE:
+      return "(NONE)";
+    case CHPP_HANDLE_LOOPBACK:
+      return "(LOOPBACK)";
+    case CHPP_HANDLE_TIMESYNC:
+      return "(TIMESYNC)";
+    case CHPP_HANDLE_DISCOVERY:
+      return "(DISCOVERY)";
+    default:
+      return "";
+  }
+}
+
+const char *packetAttrToStr(uint8_t attr) {
+  switch (attr) {
+    case CHPP_TRANSPORT_ATTR_NONE:
+      return "none";
+    case CHPP_TRANSPORT_ATTR_RESET:
+      return "reset";
+    case CHPP_TRANSPORT_ATTR_RESET_ACK:
+      return "reset-ack";
+    case CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST:
+      return "loopback-req";
+    case CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE:
+      return "loopback-rsp";
+    default:
+      return "invalid";
+  }
+}
+
+const char *transportErrorToStr(uint8_t error) {
+  switch (error) {
+    case CHPP_TRANSPORT_ERROR_NONE:
+      return "none";
+    case CHPP_TRANSPORT_ERROR_CHECKSUM:
+      return "checksum";
+    case CHPP_TRANSPORT_ERROR_OOM:
+      return "oom";
+    case CHPP_TRANSPORT_ERROR_BUSY:
+      return "busy";
+    case CHPP_TRANSPORT_ERROR_HEADER:
+      return "header";
+    case CHPP_TRANSPORT_ERROR_ORDER:
+      return "order";
+    case CHPP_TRANSPORT_ERROR_TIMEOUT:
+      return "timeout";
+    case CHPP_TRANSPORT_ERROR_MAX_RETRIES:
+      return "max-retries";
+    case CHPP_TRANSPORT_ERROR_APPLAYER:
+      return "app-layer";
+    default:
+      return "invalid";
+  }
+}
+
 void dumpRaw(std::ostream &os, const void *ptr, size_t len) {
-  const char *buffer = static_cast<const char *>(ptr);
+  const uint8_t *buffer = static_cast<const uint8_t *>(ptr);
   char line[32];
   char lineChars[32];
   size_t offset = 0;
@@ -150,64 +267,14 @@
   } else {
     os << " (finished)";
   }
+  uint8_t attr = CHPP_TRANSPORT_GET_ATTR(hdr.packetCode);
+  uint8_t error = CHPP_TRANSPORT_GET_ERROR(hdr.packetCode);
   os << std::endl
      << "  packetCode: 0x" << std::hex << (unsigned)hdr.packetCode
-     << " (attr: ";
-  uint8_t attr = CHPP_TRANSPORT_GET_ATTR(hdr.packetCode);
-  switch (attr) {
-    case CHPP_TRANSPORT_ATTR_NONE:
-      os << "none";
-      break;
-    case CHPP_TRANSPORT_ATTR_RESET:
-      os << "reset";
-      break;
-    case CHPP_TRANSPORT_ATTR_RESET_ACK:
-      os << "reset-ack";
-      break;
-    case CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST:
-      os << "loopback-req";
-      break;
-    case CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE:
-      os << "loopback-rsp";
-      break;
-    default:
-      os << "invalid";
-  }
-  os << " | error: ";
-  uint8_t error = CHPP_TRANSPORT_GET_ERROR(hdr.packetCode);
-  switch (error) {
-    case CHPP_TRANSPORT_ERROR_NONE:
-      os << "none";
-      break;
-    case CHPP_TRANSPORT_ERROR_CHECKSUM:
-      os << "checksum";
-      break;
-    case CHPP_TRANSPORT_ERROR_OOM:
-      os << "oom";
-      break;
-    case CHPP_TRANSPORT_ERROR_BUSY:
-      os << "busy";
-      break;
-    case CHPP_TRANSPORT_ERROR_HEADER:
-      os << "header";
-      break;
-    case CHPP_TRANSPORT_ERROR_ORDER:
-      os << "order";
-      break;
-    case CHPP_TRANSPORT_ERROR_TIMEOUT:
-      os << "timeout";
-      break;
-    case CHPP_TRANSPORT_ERROR_MAX_RETRIES:
-      os << "max-retries";
-      break;
-    case CHPP_TRANSPORT_ERROR_APPLAYER:
-      os << "app-layer";
-      break;
-    default:
-      os << "invalid";
-  }
-  os << ")" << std::endl
-     << "  ackSeq: " << std::dec << (unsigned)hdr.ackSeq << std::endl
+     << " (attr: " << packetAttrToStr(attr)
+     << " | error: " << transportErrorToStr(error) << ")" << std::endl;
+
+  os << "  ackSeq: " << std::dec << (unsigned)hdr.ackSeq << std::endl
      << "  seq: " << std::dec << (unsigned)hdr.seq << std::endl
      << "  length: " << std::dec << hdr.length << std::endl
      << "  reserved: " << std::dec << hdr.reserved << std::endl
@@ -238,9 +305,30 @@
 void dumpPacket(std::ostream &os, const ChppPacketPrefix &pkt) {
   dumpPreamble(os, pkt.preamble);
   dumpHeader(os, pkt.header);
-  os << "Payload {" << std::endl;
-  dumpRaw(os, pkt.payload, pkt.header.length);
-  os << "}" << std::endl;
+  size_t payloadOffset = 0;
+  if (CHPP_TRANSPORT_GET_ATTR(pkt.header.packetCode) ==
+          CHPP_TRANSPORT_ATTR_NONE &&
+      pkt.header.length >= sizeof(ChppAppHeader)) {
+    auto &appHdr = reinterpret_cast<const ChppAppHeader &>(*pkt.payload);
+    os << "AppHeader {" << std::endl;
+    os << " handle: 0x" << std::hex << (unsigned)appHdr.handle << " "
+       << handleToStr(appHdr.handle) << std::endl;
+    os << " type: " << std::dec << (unsigned)appHdr.type << " ("
+       << appMessageTypeToStr(appHdr.type) << ")" << std::endl;
+    os << " transaction: " << std::dec << (unsigned)appHdr.transaction
+       << std::endl;
+    os << " error: " << std::dec << (unsigned)appHdr.error << " ("
+       << appErrorCodeToStr(appHdr.error) << ")" << std::endl;
+    os << " command: " << std::dec << (unsigned)appHdr.command << std::endl;
+    os << "}" << std::endl;
+    payloadOffset = sizeof(ChppAppHeader);
+  }
+  size_t payloadSize = pkt.header.length - payloadOffset;
+  if (payloadSize > 0) {
+    os << "Payload (size " << payloadSize << ") {" << std::endl;
+    dumpRaw(os, &pkt.payload[payloadOffset], pkt.header.length - payloadOffset);
+    os << "}" << std::endl;
+  }
 
   const auto &footer = *reinterpret_cast<const ChppTransportFooter *>(
       &pkt.payload[pkt.header.length]);
diff --git a/chpp/test/packet_util.h b/chpp/test/packet_util.h
index faedd08..42bceda 100644
--- a/chpp/test/packet_util.h
+++ b/chpp/test/packet_util.h
@@ -18,12 +18,14 @@
  * @file Utilities for working with raw CHPP packets in a test setting
  */
 
+#include <array>
 #include <cinttypes>
 #include <iostream>
 #include <vector>
 
 #include <gtest/gtest.h>
 
+#include "chpp/app.h"
 #include "chpp/crc.h"
 #include "chpp/transport.h"
 
@@ -53,6 +55,21 @@
   uint8_t payload[1];  // Variable size per header.length
 } CHPP_PACKED_ATTR;
 
+template <size_t kPayloadSize>
+struct ChppPacketWithPayload {
+  uint16_t preamble;
+  ChppTransportHeader header;
+  uint8_t payload[kPayloadSize];
+  ChppTransportFooter footer;
+} CHPP_PACKED_ATTR;
+
+struct ChppPacketWithAppHeader {
+  uint16_t preamble;
+  ChppTransportHeader transportHeader;
+  ChppAppHeader appHeader;
+  uint8_t payload[];
+};
+
 // Utilities for packet creation -----------------------------------------------
 
 //! Computes the CRC of one of the complete packet types defined above
@@ -68,33 +85,72 @@
                                     uint8_t error = CHPP_TRANSPORT_ERROR_NONE);
 
 //! Create an empty ACK packet for the given packet
-ChppEmptyPacket generateAck(std::vector<uint8_t> &pkt);
+ChppEmptyPacket generateAck(const std::vector<uint8_t> &pkt);
+
+//! Create a packet with payload of the given size. If a payload array is not
+//! provided, it is set to all-zeros.
+template <size_t kPayloadSize>
+ChppPacketWithPayload<kPayloadSize> generatePacketWithPayload(
+    uint8_t ackSeq = 0, uint8_t seq = 0,
+    const std::span<uint8_t, kPayloadSize> *payload = nullptr) {
+  // clang-format off
+  ChppPacketWithPayload<kPayloadSize> pkt = {
+    .preamble = kPreamble,
+    .header = {
+      .flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM,
+      .packetCode = static_cast<uint8_t>(CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
+          CHPP_TRANSPORT_ATTR_NONE, CHPP_TRANSPORT_ERROR_NONE)),
+      .ackSeq = ackSeq,
+      .seq = seq,
+      .length = kPayloadSize,
+      .reserved = 0,
+    },
+  };
+  // clang-format on
+  if (payload != nullptr) {
+    std::memcpy(pkt.payload, payload->data(), sizeof(pkt.payload));
+  }
+  pkt.footer.checksum = computeCrc(pkt);
+  return pkt;
+}
 
 // Utilities for packet parsing ------------------------------------------------
 
-inline ChppEmptyPacket &asEmptyPacket(std::vector<uint8_t> &pkt) {
+inline const ChppEmptyPacket &asEmptyPacket(const std::vector<uint8_t> &pkt) {
   EXPECT_EQ(pkt.size(), sizeof(ChppEmptyPacket));
-  return *reinterpret_cast<ChppEmptyPacket *>(pkt.data());
+  return *reinterpret_cast<const ChppEmptyPacket *>(pkt.data());
 }
 
-inline ChppResetPacket &asResetPacket(std::vector<uint8_t> &pkt) {
+inline const ChppResetPacket &asResetPacket(const std::vector<uint8_t> &pkt) {
   EXPECT_EQ(pkt.size(), sizeof(ChppResetPacket));
-  return *reinterpret_cast<ChppResetPacket *>(pkt.data());
+  return *reinterpret_cast<const ChppResetPacket *>(pkt.data());
 }
 
-inline ChppPacketPrefix &asChpp(std::vector<uint8_t> &pkt) {
+inline const ChppPacketPrefix &asChpp(const std::vector<uint8_t> &pkt) {
   EXPECT_GE(pkt.size(), sizeof(ChppEmptyPacket));
-  return *reinterpret_cast<ChppPacketPrefix *>(pkt.data());
+  return *reinterpret_cast<const ChppPacketPrefix *>(pkt.data());
 }
 
-inline ChppTransportHeader &getHeader(std::vector<uint8_t> &pkt) {
+inline const ChppTransportHeader &getHeader(const std::vector<uint8_t> &pkt) {
   static_assert(CHPP_PREAMBLE_LEN_BYTES == sizeof(uint16_t));
   EXPECT_GE(pkt.size(), sizeof(uint16_t) + sizeof(ChppTransportHeader));
-  return *reinterpret_cast<ChppTransportHeader *>(&pkt[sizeof(uint16_t)]);
+  return *reinterpret_cast<const ChppTransportHeader *>(&pkt[sizeof(uint16_t)]);
+}
+
+inline const ChppPacketWithAppHeader &asApp(const std::vector<uint8_t> &pkt) {
+  EXPECT_GE(pkt.size(),
+            sizeof(ChppPacketWithAppHeader) + sizeof(ChppTransportFooter));
+  return *reinterpret_cast<const ChppPacketWithAppHeader *>(pkt.data());
 }
 
 // Utilities for debugging -----------------------------------------------------
 
+const char *appErrorCodeToStr(uint8_t error);
+const char *appMessageTypeToStr(uint8_t type);
+const char *handleToStr(uint8_t handle);
+const char *packetAttrToStr(uint8_t attr);
+const char *transportErrorToStr(uint8_t error);
+
 //! Tuned for outputting a raw binary buffer (e.g. payload or full packet)
 void dumpRaw(std::ostream &os, const void *ptr, size_t len);
 
diff --git a/chpp/transport.c b/chpp/transport.c
index 53687c0..5ba5751 100644
--- a/chpp/transport.c
+++ b/chpp/transport.c
@@ -540,7 +540,7 @@
  */
 static void chppProcessResetAck(struct ChppTransportState *context) {
   if (context->resetState == CHPP_RESET_STATE_NONE) {
-    CHPP_LOGE("Unexpected reset-ack seq=%" PRIu8 " code=0x%" PRIx8,
+    CHPP_LOGW("Unexpected reset-ack seq=%" PRIu8 " code=0x%" PRIx8,
               context->rxHeader.seq, context->rxHeader.packetCode);
     // In a reset race condition with both endpoints sending resets and
     // reset-acks, the sent resets and reset-acks will both have a sequence
@@ -604,8 +604,18 @@
     // There are packets to send out (could be new or retx)
     // Note: For a future ACK window > 1, makes more sense to cap the NACKs
     // to one instead of flooding with out of order NACK errors.
-    chppEnqueueTxPacket(context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
-                                     CHPP_TRANSPORT_ATTR_NONE, errorCode));
+
+    // If the sender is retrying a packet we've already received successfully,
+    // send an ACK so it will continue normally
+    enum ChppTransportErrorCode errorCodeToSend = errorCode;
+    if (context->rxHeader.length > 0 &&
+        context->rxHeader.seq == context->rxStatus.expectedSeq - 1) {
+      errorCodeToSend = CHPP_TRANSPORT_ERROR_NONE;
+    }
+
+    chppEnqueueTxPacket(
+        context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(CHPP_TRANSPORT_ATTR_NONE,
+                                                    errorCodeToSend));
   }
 
   if (errorCode == CHPP_TRANSPORT_ERROR_ORDER) {
@@ -767,7 +777,8 @@
       context->rxStatus.receivedAckSeq = rxAckSeq;
       if (context->txStatus.txAttempts > 1) {
         CHPP_LOGW("Seq %" PRIu8 " ACK'd after %" PRIuSIZE " reTX",
-                  context->rxHeader.seq, context->txStatus.txAttempts - 1);
+                  context->rxHeader.ackSeq - 1,
+                  context->txStatus.txAttempts - 1);
       }
       context->txStatus.txAttempts = 0;
 
@@ -1560,6 +1571,7 @@
 // TODO(b/192359485): Consider removing this function, or making it more robust.
 void chppEnqueueTxErrorDatagram(struct ChppTransportState *context,
                                 enum ChppTransportErrorCode errorCode) {
+  chppMutexLock(&context->mutex);
   bool resetting = (context->resetState == CHPP_RESET_STATE_RESETTING);
   if (resetting) {
     CHPP_LOGE("Discarding app error 0x%" PRIx8 " (resetting)", errorCode);
@@ -1582,6 +1594,7 @@
     chppEnqueueTxPacket(context, CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
                                      CHPP_TRANSPORT_ATTR_NONE, errorCode));
   }
+  chppMutexUnlock(&context->mutex);
 }
 
 uint64_t chppTransportGetTimeUntilNextDoWorkNs(
@@ -1688,11 +1701,13 @@
   const uint64_t currentTimeNs = chppGetCurrentTimeNs();
   const bool isTxTimeout = currentTimeNs - context->txStatus.lastTxTimeNs >=
                            CHPP_TRANSPORT_TX_TIMEOUT_NS;
+  const bool isResetting = context->resetState == CHPP_RESET_STATE_RESETTING;
 
   // Call chppTransportDoWork for both TX and request timeouts.
   if (isTxTimeout) {
-    CHPP_LOGE("ACK timeout. Tx t=%" PRIu64,
-              context->txStatus.lastTxTimeNs / CHPP_NSEC_PER_MSEC);
+    CHPP_LOGE("ACK timeout. Tx t=%" PRIu64 ", attempt %zu, isResetting=%d",
+              context->txStatus.lastTxTimeNs / CHPP_NSEC_PER_MSEC,
+              context->txStatus.txAttempts, isResetting);
     chppTransportDoWork(context);
   } else {
     const uint64_t requestTimeoutNs =
@@ -1704,9 +1719,8 @@
     }
   }
 
-  if ((context->resetState == CHPP_RESET_STATE_RESETTING) &&
-      (currentTimeNs - context->resetTimeNs >=
-       CHPP_TRANSPORT_RESET_TIMEOUT_NS)) {
+  if (isResetting && (currentTimeNs - context->resetTimeNs >=
+                      CHPP_TRANSPORT_RESET_TIMEOUT_NS)) {
     if (context->resetCount + 1 < CHPP_TRANSPORT_MAX_RESET) {
       CHPP_LOGE("RESET-ACK timeout; retrying");
       context->resetCount++;
@@ -1714,6 +1728,7 @@
                 CHPP_TRANSPORT_ERROR_TIMEOUT);
     } else {
       CHPP_LOGE("RESET-ACK timeout; giving up");
+      context->txStatus.txAttempts = 0;
       context->resetState = CHPP_RESET_STATE_PERMANENT_FAILURE;
       chppClearTxDatagramQueue(context);
     }
diff --git a/chre_api/CMakeLists.txt b/chre_api/CMakeLists.txt
new file mode 100644
index 0000000..26e429d
--- /dev/null
+++ b/chre_api/CMakeLists.txt
@@ -0,0 +1,34 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+include(backend.cmake)
+
+# The interface which provides the base CHRE API for nanoapps including
+# "chre.h", "chre/version.h", etc.
+#
+# Note that this does not implement the interface, this is done either by the
+# runtime or the nanoapp support lib DSO for dynamic nanoapp builds -- neither
+# of which nanoapps are permitted to directly depend on.
+pw_add_facade(chre.chre_api INTERFACE
+  BACKEND
+    chre.chre_api_BACKEND
+  HEADERS
+    include/chre_api/chre.h
+    include/chre_api/chre/audio.h
+    include/chre_api/chre/ble.h
+    include/chre_api/chre/common.h
+    include/chre_api/chre/event.h
+    include/chre_api/chre/gnss.h
+    include/chre_api/chre/nanoapp.h
+    include/chre_api/chre/re.h
+    include/chre_api/chre/sensor.h
+    include/chre_api/chre/sensor_types.h
+    include/chre_api/chre/toolchain.h
+    include/chre_api/chre/user_settings.h
+    include/chre_api/chre/version.h
+    include/chre_api/chre/wifi.h
+    include/chre_api/chre/wwan.h
+  PUBLIC_INCLUDES
+    include
+    include/chre_api
+  PUBLIC_DEPS
+    chre.variant.config
+)
diff --git a/chre_api/backend.cmake b/chre_api/backend.cmake
new file mode 100644
index 0000000..e4c9d8b
--- /dev/null
+++ b/chre_api/backend.cmake
@@ -0,0 +1,6 @@
+include_guard(GLOBAL)
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+# Backend for chre.chre_api.
+pw_add_backend_variable(chre.chre_api_BACKEND)
diff --git a/chre_api/include/chre_api/chre/ble.h b/chre_api/include/chre_api/chre/ble.h
index dc8e900..230fe7d 100644
--- a/chre_api/include/chre_api/chre/ble.h
+++ b/chre_api/include/chre_api/chre/ble.h
@@ -56,16 +56,16 @@
  * @{
  */
 //! No BLE APIs are supported
-#define CHRE_BLE_CAPABILITIES_NONE UINT32_C(0)
+#define CHRE_BLE_CAPABILITIES_NONE (UINT32_C(0))
 
 //! CHRE supports BLE scanning
-#define CHRE_BLE_CAPABILITIES_SCAN UINT32_C(1 << 0)
+#define CHRE_BLE_CAPABILITIES_SCAN (UINT32_C(1) << 0)
 
 //! CHRE BLE supports batching of scan results, either through Android-specific
 //! HCI (OCF: 0x156), or by the CHRE framework, internally.
 //! @since v1.7 Platforms with this capability must also support flushing scan
 //! results during a batched scan.
-#define CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING UINT32_C(1 << 1)
+#define CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING (UINT32_C(1) << 1)
 
 //! CHRE BLE scan supports best-effort hardware filtering. If filtering is
 //! available, chreBleGetFilterCapabilities() returns a bitmap indicating the
@@ -74,10 +74,14 @@
 //! must be met for this flag:
 //! If only one nanoapp is requesting BLE scans and there are no BLE scans from
 //! the AP, only filtered results will be provided to the nanoapp.
-#define CHRE_BLE_CAPABILITIES_SCAN_FILTER_BEST_EFFORT UINT32_C(1 << 2)
+#define CHRE_BLE_CAPABILITIES_SCAN_FILTER_BEST_EFFORT (UINT32_C(1) << 2)
 
 //! CHRE BLE supports reading the RSSI of a specified LE-ACL connection handle.
-#define CHRE_BLE_CAPABILITIES_READ_RSSI UINT32_C(1 << 3)
+#define CHRE_BLE_CAPABILITIES_READ_RSSI (UINT32_C(1) << 3)
+
+//! CHRE supports offloading a Bluetooth connection socket for bidirectional
+//! data transfer over a Connection-Oriented Channel (COC).
+#define CHRE_BLE_CAPABILITIES_LE_COC_SOCKET UINT32_C (UINT32_C(1) << 4)
 /** @} */
 
 /**
@@ -95,24 +99,24 @@
  * @{
  */
 //! No CHRE BLE filters are supported
-#define CHRE_BLE_FILTER_CAPABILITIES_NONE UINT32_C(0)
+#define CHRE_BLE_FILTER_CAPABILITIES_NONE (UINT32_C(0))
 
 //! CHRE BLE supports RSSI filters
-#define CHRE_BLE_FILTER_CAPABILITIES_RSSI UINT32_C(1 << 1)
+#define CHRE_BLE_FILTER_CAPABILITIES_RSSI (UINT32_C(1) << 1)
 
 //! CHRE BLE supports Broadcaster Address filters (Corresponding HCI OCF:
 //! 0x0157, Sub-command: 0x02)
 //! @since v1.9
-#define CHRE_BLE_FILTER_CAPABILITIES_BROADCASTER_ADDRESS UINT32_C(1 << 2)
+#define CHRE_BLE_FILTER_CAPABILITIES_BROADCASTER_ADDRESS (UINT32_C(1) << 2)
 
 //! CHRE BLE supports Manufacturer Data filters (Corresponding HCI OCF: 0x0157,
 //! Sub-command: 0x06)
 //! @since v1.8
-#define CHRE_BLE_FILTER_CAPABILITIES_MANUFACTURER_DATA UINT32_C(1 << 6)
+#define CHRE_BLE_FILTER_CAPABILITIES_MANUFACTURER_DATA (UINT32_C(1) << 6)
 
 //! CHRE BLE supports Service Data filters (Corresponding HCI OCF: 0x0157,
 //! Sub-command: 0x07)
-#define CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA UINT32_C(1 << 7)
+#define CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA (UINT32_C(1) << 7)
 /** @} */
 
 /**
@@ -194,6 +198,47 @@
  */
 #define CHRE_EVENT_BLE_SCAN_STATUS_CHANGE CHRE_BLE_EVENT_ID(5)
 
+/**
+ * nanoappHandleEvent argument: struct chreBleSocketConnectionEvent
+ *
+ * This is a unicast event that is sent to a nanoapp when an offloaded socket is
+ * connected and is available to be used by the nanoapp. The nanoapp must call
+ * chreBleSocketAccept() to accept ownership of the socket and
+ * subscribe to CHRE_EVENT_BLE_SOCKET_PACKET events.
+ *
+ * @since v1.11
+ */
+#define CHRE_EVENT_BLE_SOCKET_CONNECTION CHRE_BLE_EVENT_ID(6)
+
+/**
+ * nanoappHandleEvent argument: struct chreBleSocketDisconnectionEvent
+ *
+ * This is a unicast event that is sent to a nanoapp when an offloaded socket is
+ * disconnected and can no longer be used by the nanoapp.
+ *
+ * @since v1.11
+ */
+#define CHRE_EVENT_BLE_SOCKET_DISCONNECTION CHRE_BLE_EVENT_ID(7)
+
+/**
+ * nanoappHandleEvent argument: struct chreBleSocketPacketEvent
+ *
+ * This event is sent when the nanoapp receives a packet from the offload
+ * socket.
+ *
+ * @since v1.11
+ */
+#define CHRE_EVENT_BLE_SOCKET_PACKET CHRE_BLE_EVENT_ID(8)
+
+/**
+ * nanoappHandleEvent argument: NULL
+ *
+ * This event is sent when the socket is available to send packets again.
+ *
+ * @since v1.11
+ */
+#define CHRE_EVENT_BLE_SOCKET_SEND_AVAILABLE CHRE_BLE_EVENT_ID(9)
+
 // NOTE: Do not add new events with ID > 15
 /** @} */
 
@@ -677,6 +722,102 @@
 };
 
 /**
+ * Notifies a nanoapp that a socket has been connected and offloaded and is
+ * ready to be used. The nanoapp is expected to accept ownership of the socket
+ * by calling the chreBleSocketAccept() API. If the nanoapp does not accept
+ * ownership of the socket, the transfer of ownership to the nanoapp is aborted.
+ *
+ * @since v1.11
+ */
+struct chreBleSocketConnectionEvent {
+  //! Unique identifier for this socket connection. This ID in CHRE matches the
+  //! ID used on the host side. It is valid only while the socket is connected.
+  uint64_t socketId;
+
+  //! Descriptive socket name provided by the host app that initiated the socket
+  //! offload request. This is not guaranteed to be unique across the system,
+  //! but can help the offload app understand the purpose of the socket when it
+  //! receives a socket connection event. This pointer is only valid for the
+  //! duration of the event.
+  const char *socketName;
+
+  //! When sending a packet to the socket via chreBleSocketSend(), the length
+  //! must not exceed this value.
+  uint16_t maxTxPacketLength;
+
+  //! When the nanoapp receives packets from the socket via the
+  //! chreBleSocketPacketEvent, the length will not exceed this value.
+  uint16_t maxRxPacketLength;
+};
+
+/**
+ * Notifies a nanoapp that a socket has been disconnected and can no longer be
+ * used by the nanoapp. Once a socket is disconnected, the same socket ID will
+ * not be reconnected. If the nanoapp wants to continue using an offloaded
+ * socket, a new offloaded socket must be created and connected.
+ *
+ * @since v1.11
+ */
+struct chreBleSocketDisconnectionEvent {
+  //! @see chreBleSocketConnectionEvent.socketId
+  uint64_t socketId;
+};
+
+/**
+ * Notifies a nanoapp that it has received a packet from a socket.
+ *
+ * @since v1.11
+ */
+struct chreBleSocketPacketEvent {
+  //! @see chreBleSocketConnectionEvent.socketId
+  uint64_t socketId;
+
+  //! Length of data in bytes. The length will not exceed the maxRxPacketLength
+  //! provided in the CHRE event CHRE_EVENT_BLE_SOCKET_CONNECTION.
+  uint16_t length;
+
+  //! Packet payload that is length bytes.
+  const uint8_t *data;
+};
+
+/**
+ * Result code used with chreBleSocketSend().
+ *
+ * @since v1.11
+ */
+enum chreBleSocketSendStatus {
+  //! The packet has successfully been sent to the platform layer.
+  CHRE_BLE_SOCKET_SEND_STATUS_SUCCESS = 1,
+
+  //! The packet will not be sent.
+  CHRE_BLE_SOCKET_SEND_STATUS_FAILURE = 2,
+
+  //! The packet cannot be sent at this time because too many packets are in
+  //! flight. The nanoapp will be notified via a
+  //! CHRE_EVENT_BLE_SOCKET_SEND_AVAILABLE event when the socket is available to
+  //! send the packet.
+  CHRE_BLE_SOCKET_SEND_STATUS_QUEUE_FULL = 3,
+};
+
+/**
+ * Callback which frees the packet sent via chreBleSocketSend().
+ *
+ * This callback is (optionally) provided to the chreBleSocketSend() function as
+ * a means for freeing the packet. When this callback is invoked, the packet is
+ * no longer needed and can be released. Note that this in no way assures that
+ * said packet was sent to the offload socket, simply that this memory is no
+ * longer needed.
+ *
+ * @param data The data argument from chreBleSocketSend().
+ * @param length The length argument from chreBleSocketSend().
+ *
+ * @see chreBleSocketSend()
+ *
+ * @since v1.11
+ */
+typedef void(chreBleSocketPacketFreeFunction)(void *data, uint16_t length);
+
+/**
  * Retrieves a set of flags indicating the BLE features supported by the
  * current CHRE implementation. The value returned by this function must be
  * consistent for the entire duration of the nanoapp's execution.
@@ -938,6 +1079,52 @@
 bool chreBleGetScanStatus(struct chreBleScanStatus *status);
 
 /**
+ * Accepts that this nanoapp owns the socket and subscribes to
+ * CHRE_EVENT_BLE_SOCKET_PACKET events from this socket. This API is only
+ * valid to call while handling the CHRE_EVENT_BLE_SOCKET_CONNECTION event.
+ *
+ * @param socketId @see chreBleSocketConnectionEvent.socketId
+ * @return True if CHRE confirms that socket ownership has been transferred.
+ *
+ * @since v1.11
+ */
+bool chreBleSocketAccept(uint64_t socketId);
+
+/**
+ * Sends a packet to the socket with the corresponding socketId. This API can
+ * only be used after the nanoapp has received a
+ * CHRE_EVENT_BLE_SOCKET_CONNECTION event indicating the offloaded socket is
+ * connected and has accepted ownership of the socket by calling
+ * chreBleSocketAccept().
+ *
+ * NOTE: freeCallback WILL NOT be invoked if the return status is
+ * CHRE_BLE_SOCKET_SEND_STATUS_QUEUE_FULL.
+ *
+ * @param socketId @see chreBleSocketConnectionEvent.socketId
+ * @param data Packet to be sent to the socket that is length bytes. After this
+ *     API is called, ownership of this memory passes to CHRE and the nanoapp
+ *     must ensure that the packet remains valid and unmodified until the
+ *     freeCallback is invoked.
+  * @param length Length of packet to be sent to the socket in bytes. Cannot
+ *     exceed the maxTxPacketLength provided in the CHRE event
+ *     CHRE_EVENT_BLE_SOCKET_CONNECTION.
+ * @param freeCallback Callback invoked to indicate that the packet data buffer
+ *     is not needed by CHRE anymore. Note that invocation of this function does
+ *     not mean that the packet has been delivered, only that memory can be
+ *     released. This is guaranteed to be invoked if this function returns
+ *     CHRE_BLE_SOCKET_SEND_STATUS_SUCCESS or
+ *     CHRE_BLE_SOCKET_SEND_STATUS_FAILURE, but WILL NOT be invoked for
+ *     CHRE_BLE_SOCKET_SEND_STATUS_QUEUE_FULL. This may be invoked
+ *     synchronously, so nanoapp developers should not call chreBleSocketSend()
+ *     from within the callback to avoid potential infinite recursion.
+ * @return A value from enum chreBleSocketSendStatus.
+ *
+ * @since v1.11
+ */
+int32_t chreBleSocketSend(uint64_t socketId, const void *data, uint16_t length,
+                          chreBleSocketPacketFreeFunction *freeCallback);
+
+/**
  * Definitions for handling unsupported CHRE BLE scenarios.
  */
 #else  // defined(CHRE_NANOAPP_USES_BLE) || !defined(CHRE_IS_NANOAPP_BUILD)
@@ -958,6 +1145,15 @@
 #define chreBleReadRssiAsync(...) \
   CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleReadRssiAsync")
 
+#define chreBleGetScanStatus(...) \
+  CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleGetScanStatus")
+
+#define chreBleSocketAccept(...) \
+  CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleSocketAccept")
+
+#define chreBleSocketSend(...) \
+  CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleSocketSend")
+
 #endif  // defined(CHRE_NANOAPP_USES_BLE) || !defined(CHRE_IS_NANOAPP_BUILD)
 
 #ifdef __cplusplus
diff --git a/chre_api/include/chre_api/chre/event.h b/chre_api/include/chre_api/chre/event.h
index d08b7d0..fb56733 100644
--- a/chre_api/include/chre_api/chre/event.h
+++ b/chre_api/include/chre_api/chre/event.h
@@ -26,6 +26,7 @@
  */
 
 #include <stdbool.h>
+#include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
 
@@ -202,6 +203,31 @@
 #define CHRE_EVENT_RELIABLE_MSG_ASYNC_RESULT UINT16_C(0x000B)
 
 /**
+ * nanoappHandleEvent argument: struct chreMessageFromEndpointData
+ *
+ * The format of the 'message' part of this structure is left undefined,
+ * and it's up to the nanoapp and endpoint to have an established protocol
+ * beforehand.
+ *
+ * On receiving the first message from an endpoint, the nanoapp can assume
+ * a session with the sessionId has been created and can be used to send
+ * messages to the endpoint. The nanoapp will receive a
+ * CHRE_EVENT_ENDPOINT_SESSION_CLOSED event when the session is closed.
+ *
+ * @since v1.11
+ */
+#define CHRE_EVENT_MESSAGE_FROM_ENDPOINT UINT16_C(0x000C)
+
+/**
+ * nanoappHandleEvent argument: struct chreEndpointSessionClosedData
+ *
+ * Indicates that a session with an endpoint has been closed.
+ *
+ * @since v1.11
+ */
+#define CHRE_EVENT_ENDPOINT_SESSION_CLOSED UINT16_C(0x000D)
+
+/**
  * First possible value for CHRE_EVENT_SENSOR events.
  *
  * This allows us to separately define our CHRE_EVENT_SENSOR_* events in
@@ -425,6 +451,68 @@
 };
 
 /**
+ * Data provided with CHRE_EVENT_MESSAGE_FROM_ENDPOINT.
+ */
+struct chreMessageFromEndpointData {
+    /**
+     * Message type supplied by the endpoint.
+     */
+    uint32_t messageType;
+
+    /**
+     * Message permissions supplied by the endpoint. The format is specified by
+     * the CHRE_MESSAGE_PERMISSION_* values if the endpoint is a nanoapp, else
+     * it is specified by the endpoint. These permissions are enforced by CHRE.
+     * A nanoapp without the required permissions will not receive the message.
+     */
+    uint32_t messagePermissions;
+
+    /**
+     * The message from the endpoint.
+     *
+     * These contents are of a format that the endpoint and nanoapp must have
+     * established beforehand.
+     *
+     * This data is 'messageSize' bytes in length.  Note that if 'messageSize'
+     * is 0, this might contain NULL.
+     */
+    const void *message;
+
+    /**
+     * The size, in bytes of the following 'message'.
+     *
+     * This can be 0.
+     */
+    size_t messageSize;
+
+    /**
+     * The session ID of the message. A session is the active connection between
+     * two endpoints. The receiving nanoapp or endpoint initiated the session
+     * before sending this message. If the nanoapp has not yet received a
+     * message with this session ID, it can assume the session was created by
+     * the nanoapp or other endpoint. The nanoapp may send messages to the other
+     * endpoint with this session ID.
+     */
+    uint16_t sessionId;
+};
+
+/**
+ * Data provided with CHRE_EVENT_ENDPOINT_SESSION_CLOSED.
+ */
+struct chreEndpointSessionClosedData {
+    /**
+     * The message hub ID and endpoint ID of the other party in the session.
+     */
+    uint64_t hubId;
+    uint64_t endpointId;
+
+    /**
+     * The ID of the session that was closed.
+     */
+    uint16_t sessionId;
+};
+
+/**
  * Provides metadata for a nanoapp in the system.
  */
 struct chreNanoappInfo {
@@ -471,7 +559,7 @@
      */
     uint8_t rpcServiceCount;
 
-    /*
+    /**
      * Array of RPC services published by this nanoapp.
      * Services are published via chrePublishRpcServices.
      * The array contains rpcServiceCount entries.
diff --git a/chre_api/include/chre_api/chre/gnss.h b/chre_api/include/chre_api/chre/gnss.h
index a326e85..75d0168 100644
--- a/chre_api/include/chre_api/chre/gnss.h
+++ b/chre_api/include/chre_api/chre/gnss.h
@@ -55,21 +55,21 @@
  */
 
 //! A lack of flags indicates that GNSS is not supported in this CHRE
-#define CHRE_GNSS_CAPABILITIES_NONE          UINT32_C(0)
+#define CHRE_GNSS_CAPABILITIES_NONE          (UINT32_C(0))
 
 //! GNSS position fixes are supported via chreGnssLocationSessionStartAsync()
-#define CHRE_GNSS_CAPABILITIES_LOCATION      UINT32_C(1 << 0)
+#define CHRE_GNSS_CAPABILITIES_LOCATION      (UINT32_C(1) << 0)
 
 //! GNSS raw measurements are supported via
 //! chreGnssMeasurementSessionStartAsync()
-#define CHRE_GNSS_CAPABILITIES_MEASUREMENTS  UINT32_C(1 << 1)
+#define CHRE_GNSS_CAPABILITIES_MEASUREMENTS  (UINT32_C(1) << 1)
 
 //! Location fixes supplied from chreGnssConfigurePassiveLocationListener()
 //! are tapped in at the GNSS engine level, so they include additional fixes
 //! such as those requested by the AP, and not just those requested by other
 //! nanoapps within CHRE (which is the case when this flag is not set)
 #define CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER \
-                                             UINT32_C(1 << 2)
+                                             (UINT32_C(1) << 2)
 
 /** @} */
 
@@ -120,34 +120,34 @@
 
 // Flags indicating the Accumulated Delta Range's states
 // (ref: GnssAccumulatedDeltaRangeState)
-#define CHRE_GNSS_ADR_STATE_UNKNOWN     UINT16_C(0)
-#define CHRE_GNSS_ADR_STATE_VALID       UINT16_C(1 << 0)
-#define CHRE_GNSS_ADR_STATE_RESET       UINT16_C(1 << 1)
-#define CHRE_GNSS_ADR_STATE_CYCLE_SLIP  UINT16_C(1 << 2)
+#define CHRE_GNSS_ADR_STATE_UNKNOWN     (UINT16_C(0))
+#define CHRE_GNSS_ADR_STATE_VALID       (UINT16_C(1) << 0)
+#define CHRE_GNSS_ADR_STATE_RESET       (UINT16_C(1) << 1)
+#define CHRE_GNSS_ADR_STATE_CYCLE_SLIP  (UINT16_C(1) << 2)
 
 // Flags to indicate what fields in chreGnssClock are valid (ref: GnssClockFlags)
-#define CHRE_GNSS_CLOCK_HAS_LEAP_SECOND        UINT16_C(1 << 0)
-#define CHRE_GNSS_CLOCK_HAS_TIME_UNCERTAINTY   UINT16_C(1 << 1)
-#define CHRE_GNSS_CLOCK_HAS_FULL_BIAS          UINT16_C(1 << 2)
-#define CHRE_GNSS_CLOCK_HAS_BIAS               UINT16_C(1 << 3)
-#define CHRE_GNSS_CLOCK_HAS_BIAS_UNCERTAINTY   UINT16_C(1 << 4)
-#define CHRE_GNSS_CLOCK_HAS_DRIFT              UINT16_C(1 << 5)
-#define CHRE_GNSS_CLOCK_HAS_DRIFT_UNCERTAINTY  UINT16_C(1 << 6)
+#define CHRE_GNSS_CLOCK_HAS_LEAP_SECOND        (UINT16_C(1) << 0)
+#define CHRE_GNSS_CLOCK_HAS_TIME_UNCERTAINTY   (UINT16_C(1) << 1)
+#define CHRE_GNSS_CLOCK_HAS_FULL_BIAS          (UINT16_C(1) << 2)
+#define CHRE_GNSS_CLOCK_HAS_BIAS               (UINT16_C(1) << 3)
+#define CHRE_GNSS_CLOCK_HAS_BIAS_UNCERTAINTY   (UINT16_C(1) << 4)
+#define CHRE_GNSS_CLOCK_HAS_DRIFT              (UINT16_C(1) << 5)
+#define CHRE_GNSS_CLOCK_HAS_DRIFT_UNCERTAINTY  (UINT16_C(1) << 6)
 
 // Flags to indicate which values are valid in a GpsLocation
 // (ref: GpsLocationFlags)
-#define CHRE_GPS_LOCATION_HAS_LAT_LONG           UINT16_C(1 << 0)
-#define CHRE_GPS_LOCATION_HAS_ALTITUDE           UINT16_C(1 << 1)
-#define CHRE_GPS_LOCATION_HAS_SPEED              UINT16_C(1 << 2)
-#define CHRE_GPS_LOCATION_HAS_BEARING            UINT16_C(1 << 3)
-#define CHRE_GPS_LOCATION_HAS_ACCURACY           UINT16_C(1 << 4)
+#define CHRE_GPS_LOCATION_HAS_LAT_LONG           (UINT16_C(1) << 0)
+#define CHRE_GPS_LOCATION_HAS_ALTITUDE           (UINT16_C(1) << 1)
+#define CHRE_GPS_LOCATION_HAS_SPEED              (UINT16_C(1) << 2)
+#define CHRE_GPS_LOCATION_HAS_BEARING            (UINT16_C(1) << 3)
+#define CHRE_GPS_LOCATION_HAS_ACCURACY           (UINT16_C(1) << 4)
 
 //! @since v1.3
-#define CHRE_GPS_LOCATION_HAS_ALTITUDE_ACCURACY  UINT16_C(1 << 5)
+#define CHRE_GPS_LOCATION_HAS_ALTITUDE_ACCURACY  (UINT16_C(1) << 5)
 //! @since v1.3
-#define CHRE_GPS_LOCATION_HAS_SPEED_ACCURACY     UINT16_C(1 << 6)
+#define CHRE_GPS_LOCATION_HAS_SPEED_ACCURACY     (UINT16_C(1) << 6)
 //! @since v1.3
-#define CHRE_GPS_LOCATION_HAS_BEARING_ACCURACY   UINT16_C(1 << 7)
+#define CHRE_GPS_LOCATION_HAS_BEARING_ACCURACY   (UINT16_C(1) << 7)
 
 /**
  * The maximum number of instances of struct chreGnssMeasurement that may be
@@ -161,21 +161,21 @@
 #define CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5  UINT8_C(64)
 
 // Flags indicating the GNSS measurement state (ref: GnssMeasurementState)
-#define CHRE_GNSS_MEASUREMENT_STATE_UNKNOWN                UINT16_C(0)
-#define CHRE_GNSS_MEASUREMENT_STATE_CODE_LOCK              UINT16_C(1 << 0)
-#define CHRE_GNSS_MEASUREMENT_STATE_BIT_SYNC               UINT16_C(1 << 1)
-#define CHRE_GNSS_MEASUREMENT_STATE_SUBFRAME_SYNC          UINT16_C(1 << 2)
-#define CHRE_GNSS_MEASUREMENT_STATE_TOW_DECODED            UINT16_C(1 << 3)
-#define CHRE_GNSS_MEASUREMENT_STATE_MSEC_AMBIGUOUS         UINT16_C(1 << 4)
-#define CHRE_GNSS_MEASUREMENT_STATE_SYMBOL_SYNC            UINT16_C(1 << 5)
-#define CHRE_GNSS_MEASUREMENT_STATE_GLO_STRING_SYNC        UINT16_C(1 << 6)
-#define CHRE_GNSS_MEASUREMENT_STATE_GLO_TOD_DECODED        UINT16_C(1 << 7)
-#define CHRE_GNSS_MEASUREMENT_STATE_BDS_D2_BIT_SYNC        UINT16_C(1 << 8)
-#define CHRE_GNSS_MEASUREMENT_STATE_BDS_D2_SUBFRAME_SYNC   UINT16_C(1 << 9)
-#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1BC_CODE_LOCK     UINT16_C(1 << 10)
-#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1C_2ND_CODE_LOCK  UINT16_C(1 << 11)
-#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1B_PAGE_SYNC      UINT16_C(1 << 12)
-#define CHRE_GNSS_MEASUREMENT_STATE_SBAS_SYNC              UINT16_C(1 << 13)
+#define CHRE_GNSS_MEASUREMENT_STATE_UNKNOWN                (UINT16_C(0))
+#define CHRE_GNSS_MEASUREMENT_STATE_CODE_LOCK              (UINT16_C(1) << 0)
+#define CHRE_GNSS_MEASUREMENT_STATE_BIT_SYNC               (UINT16_C(1) << 1)
+#define CHRE_GNSS_MEASUREMENT_STATE_SUBFRAME_SYNC          (UINT16_C(1) << 2)
+#define CHRE_GNSS_MEASUREMENT_STATE_TOW_DECODED            (UINT16_C(1) << 3)
+#define CHRE_GNSS_MEASUREMENT_STATE_MSEC_AMBIGUOUS         (UINT16_C(1) << 4)
+#define CHRE_GNSS_MEASUREMENT_STATE_SYMBOL_SYNC            (UINT16_C(1) << 5)
+#define CHRE_GNSS_MEASUREMENT_STATE_GLO_STRING_SYNC        (UINT16_C(1) << 6)
+#define CHRE_GNSS_MEASUREMENT_STATE_GLO_TOD_DECODED        (UINT16_C(1) << 7)
+#define CHRE_GNSS_MEASUREMENT_STATE_BDS_D2_BIT_SYNC        (UINT16_C(1) << 8)
+#define CHRE_GNSS_MEASUREMENT_STATE_BDS_D2_SUBFRAME_SYNC   (UINT16_C(1) << 9)
+#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1BC_CODE_LOCK     (UINT16_C(1) << 10)
+#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1C_2ND_CODE_LOCK  (UINT16_C(1) << 11)
+#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1B_PAGE_SYNC      (UINT16_C(1) << 12)
+#define CHRE_GNSS_MEASUREMENT_STATE_SBAS_SYNC              (UINT16_C(1) << 13)
 
 #define CHRE_GNSS_MEASUREMENT_CARRIER_FREQUENCY_UNKNOWN    0.f
 
diff --git a/chre_api/include/chre_api/chre/re.h b/chre_api/include/chre_api/chre/re.h
index 8d76987..24ebff6 100644
--- a/chre_api/include/chre_api/chre/re.h
+++ b/chre_api/include/chre_api/chre/re.h
@@ -73,11 +73,11 @@
  */
 
 //! None of the optional capabilities are supported
-#define CHRE_CAPABILITIES_NONE                 UINT32_C(0)
+#define CHRE_CAPABILITIES_NONE                 (UINT32_C(0))
 
 //! Support for reliable messages.
 //! @see chreSendReliableMessageAsync()
-#define CHRE_CAPABILITIES_RELIABLE_MESSAGES    UINT32_C(1 << 0)
+#define CHRE_CAPABILITIES_RELIABLE_MESSAGES    (UINT32_C(1) << 0)
 
 /** @} */
 
diff --git a/chre_api/include/chre_api/chre/version.h b/chre_api/include/chre_api/chre/version.h
index 8f4e3d6..3dc4984 100644
--- a/chre_api/include/chre_api/chre/version.h
+++ b/chre_api/include/chre_api/chre/version.h
@@ -164,12 +164,22 @@
  * This version of the CHRE API is shipped with Android V. It adds support for
  * reliable messaging.
  *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_10 UINT32_C(0x010a0000)
+
+/**
+ * Value for version 1.11 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API is shipped with Android 16. It adds definitions
+ * for WWAN Cell Neighbors.
+ *
  * @note This version of the CHRE API has not been finalized yet, and is
  * currently considered a preview that is subject to change.
  *
  * @see CHRE_API_VERSION
  */
-#define CHRE_API_VERSION_1_10 UINT32_C(0x010a0000)
+#define CHRE_API_VERSION_1_11 UINT32_C(0x010b0000)
 
 /**
  * Major and Minor Version of this Context Hub Runtime Environment API.
@@ -188,7 +198,7 @@
  * Note that version numbers can always be numerically compared with
  * expected results, so 1.0.0 < 1.0.4 < 1.1.0 < 2.0.300 < 3.5.0.
  */
-#define CHRE_API_VERSION CHRE_API_VERSION_1_10
+#define CHRE_API_VERSION CHRE_API_VERSION_1_11
 
 /**
  * Utility macro to extract only the API major version of a composite CHRE
diff --git a/chre_api/include/chre_api/chre/wifi.h b/chre_api/include/chre_api/chre/wifi.h
index 44d3d41..e02f419 100644
--- a/chre_api/include/chre_api/chre/wifi.h
+++ b/chre_api/include/chre_api/chre/wifi.h
@@ -64,29 +64,29 @@
  */
 
 //! No WiFi APIs are supported
-#define CHRE_WIFI_CAPABILITIES_NONE              UINT32_C(0)
+#define CHRE_WIFI_CAPABILITIES_NONE              (UINT32_C(0))
 
 //! Listening to scan results is supported, as enabled via
 //! chreWifiConfigureScanMonitorAsync()
-#define CHRE_WIFI_CAPABILITIES_SCAN_MONITORING   UINT32_C(1 << 0)
+#define CHRE_WIFI_CAPABILITIES_SCAN_MONITORING   (UINT32_C(1) << 0)
 
 //! Requesting WiFi scans on-demand is supported via chreWifiRequestScanAsync()
-#define CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN    UINT32_C(1 << 1)
+#define CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN    (UINT32_C(1) << 1)
 
 //! Specifying the radio chain preference in on-demand scan requests, and
 //! reporting it in scan events is supported
 //! @since v1.2
-#define CHRE_WIFI_CAPABILITIES_RADIO_CHAIN_PREF  UINT32_C(1 << 2)
+#define CHRE_WIFI_CAPABILITIES_RADIO_CHAIN_PREF  (UINT32_C(1) << 2)
 
 //! Requesting RTT ranging is supported via chreWifiRequestRangingAsync()
 //! @since v1.2
-#define CHRE_WIFI_CAPABILITIES_RTT_RANGING       UINT32_C(1 << 3)
+#define CHRE_WIFI_CAPABILITIES_RTT_RANGING       (UINT32_C(1) << 3)
 
 //! Specifies if WiFi NAN service subscription is supported. If a platform
 //! supports subscriptions, then it must also support RTT ranging for NAN
 //! services via chreWifiNanRequestRangingAsync()
 //! @since v1.6
-#define CHRE_WIFI_CAPABILITIES_NAN_SUB           UINT32_C(1 << 4)
+#define CHRE_WIFI_CAPABILITIES_NAN_SUB           (UINT32_C(1) << 4)
 
 /** @} */
 
@@ -226,8 +226,8 @@
  * @{
  */
 
-#define CHRE_WIFI_BAND_MASK_2_4_GHZ  UINT8_C(1 << 0)  //!< 2.4 GHz
-#define CHRE_WIFI_BAND_MASK_5_GHZ    UINT8_C(1 << 1)  //!< 5 GHz
+#define CHRE_WIFI_BAND_MASK_2_4_GHZ  (UINT8_C(1) << 0)  //!< 2.4 GHz
+#define CHRE_WIFI_BAND_MASK_5_GHZ    (UINT8_C(1) << 1)  //!< 5 GHz
 
 /** @} */
 
@@ -237,17 +237,17 @@
  * @{
  */
 
-#define CHRE_WIFI_SCAN_RESULT_FLAGS_NONE                         UINT8_C(0)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_NONE                       UINT8_C(0)
 
 //! Element ID 61 (HT Operation) is present (see HT 7.3.2)
-#define CHRE_WIFI_SCAN_RESULT_FLAGS_HT_OPS_PRESENT               UINT8_C(1 << 0)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_HT_OPS_PRESENT             (UINT8_C(1) << 0)
 
 //! Element ID 192 (VHT Operation) is present (see VHT 8.4.2)
-#define CHRE_WIFI_SCAN_RESULT_FLAGS_VHT_OPS_PRESENT              UINT8_C(1 << 1)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_VHT_OPS_PRESENT            (UINT8_C(1) << 1)
 
 //! Element ID 127 (Extended Capabilities) is present, and bit 70 (Fine Timing
 //! Measurement Responder) is set to 1 (see IEEE Std 802.11-2016 9.4.2.27)
-#define CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER             UINT8_C(1 << 2)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER           (UINT8_C(1) << 2)
 
 //! Retained for backwards compatibility
 //! @see CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER
@@ -256,12 +256,13 @@
 
 //! HT Operation element indicates that a secondary channel is present
 //! (see HT 7.3.2.57)
-#define CHRE_WIFI_SCAN_RESULT_FLAGS_HAS_SECONDARY_CHANNEL_OFFSET UINT8_C(1 << 3)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_HAS_SECONDARY_CHANNEL_OFFSET \
+                                                               (UINT8_C(1) << 3)
 
 //! HT Operation element indicates that the secondary channel is below the
 //! primary channel (see HT 7.3.2.57)
 #define CHRE_WIFI_SCAN_RESULT_FLAGS_SECONDARY_CHANNEL_OFFSET_IS_BELOW  \
-                                                                 UINT8_C(1 << 4)
+                                                               (UINT8_C(1) << 4)
 
 /** @} */
 
@@ -273,23 +274,23 @@
  * @{
  */
 
-#define CHRE_WIFI_SECURITY_MODE_UNKNOWN  UINT8_C(0)
+#define CHRE_WIFI_SECURITY_MODE_UNKNOWN  (UINT8_C(0))
 //! @deprecated since v1.10. Use CHRE_WIFI_SECURITY_MODE_UNKNOWN instead.
-#define CHRE_WIFI_SECURITY_MODE_UNKONWN CHRE_WIFI_SECURITY_MODE_UNKNOWN
+#define CHRE_WIFI_SECURITY_MODE_UNKONWN  CHRE_WIFI_SECURITY_MODE_UNKNOWN
 
-#define CHRE_WIFI_SECURITY_MODE_OPEN  UINT8_C(1 << 0)  //!< No auth/security
-#define CHRE_WIFI_SECURITY_MODE_WEP   UINT8_C(1 << 1)
-#define CHRE_WIFI_SECURITY_MODE_PSK   UINT8_C(1 << 2)  //!< WPA-PSK or WPA2-PSK
-#define CHRE_WIFI_SECURITY_MODE_EAP   UINT8_C(1 << 3)  //!< WPA-EAP or WPA2-EAP
+#define CHRE_WIFI_SECURITY_MODE_OPEN (UINT8_C(1) << 0)  //!< No auth/security
+#define CHRE_WIFI_SECURITY_MODE_WEP  (UINT8_C(1) << 1)
+#define CHRE_WIFI_SECURITY_MODE_PSK  (UINT8_C(1) << 2)  //!< WPA-PSK or WPA2-PSK
+#define CHRE_WIFI_SECURITY_MODE_EAP  (UINT8_C(1) << 3)  //!< WPA-EAP or WPA2-EAP
 
 //! @since v1.5
-#define CHRE_WIFI_SECURITY_MODE_SAE   UINT8_C(1 << 4)
+#define CHRE_WIFI_SECURITY_MODE_SAE  (UINT8_C(1) << 4)
 
 //! @since v1.5
-#define CHRE_WIFI_SECURITY_MODE_EAP_SUITE_B  UINT8_C(1 << 5)
+#define CHRE_WIFI_SECURITY_MODE_EAP_SUITE_B  (UINT8_C(1) << 5)
 
 //! @since v1.5
-#define CHRE_WIFI_SECURITY_MODE_OWE   UINT8_C(1 << 6)
+#define CHRE_WIFI_SECURITY_MODE_OWE  (UINT8_C(1) << 6)
 
 /** @} */
 
@@ -300,9 +301,9 @@
  * @{
  */
 
-#define CHRE_WIFI_RADIO_CHAIN_UNKNOWN  UINT8_C(0)
-#define CHRE_WIFI_RADIO_CHAIN_0        UINT8_C(1 << 0)
-#define CHRE_WIFI_RADIO_CHAIN_1        UINT8_C(1 << 1)
+#define CHRE_WIFI_RADIO_CHAIN_UNKNOWN  (UINT8_C(0))
+#define CHRE_WIFI_RADIO_CHAIN_0        (UINT8_C(1) << 0)
+#define CHRE_WIFI_RADIO_CHAIN_1        (UINT8_C(1) << 1)
 
 /** @} */
 
@@ -320,7 +321,7 @@
 
 //! If set, the nested chreWifiLci structure is populated; otherwise it is
 //! invalid and must be ignored
-#define CHRE_WIFI_RTT_RESULT_HAS_LCI  UINT8_C(1 << 0)
+#define CHRE_WIFI_RTT_RESULT_HAS_LCI  (UINT8_C(1) << 0)
 
 /** @} */
 
@@ -1281,6 +1282,8 @@
  * @param cookie An opaque value that will be included in the chreAsyncResult
  *        sent in relation to this request.
  * @return true if the request was accepted for processing, false otherwise.
+ * @since v1.6
+ * @note Requires WiFi permission
  */
 bool chreWifiNanRequestRangingAsync(const struct chreWifiNanRangingParams *params,
                                     const void *cookie);
diff --git a/chre_api/include/chre_api/chre/wwan.h b/chre_api/include/chre_api/chre/wwan.h
index dc46182..80cbf3d 100644
--- a/chre_api/include/chre_api/chre/wwan.h
+++ b/chre_api/include/chre_api/chre/wwan.h
@@ -58,10 +58,16 @@
  */
 
 //! No WWAN APIs are supported
-#define CHRE_WWAN_CAPABILITIES_NONE  UINT32_C(0)
+#define CHRE_WWAN_CAPABILITIES_NONE       (UINT32_C(0))
 
 //! Current cell information can be queried via chreWwanGetCellInfoAsync()
-#define CHRE_WWAN_GET_CELL_INFO      UINT32_C(1 << 0)
+#define CHRE_WWAN_GET_CELL_INFO           (UINT32_C(1) << 0)
+
+//! The chreWwanCellInfoResult from chreWwanGetCellInfoAsync() will include
+//! all available chreWwanCellInfo as entries in cells, not just a single
+//! primary result.
+//! @since v1.11 - Neighbor support in prior versions of the API is unspecified.
+#define CHRE_WWAN_GET_CELL_NEIGHBOR_INFO  (UINT32_C(1) << 1)
 
 /** @} */
 
diff --git a/chre_api/legacy/v1_10/chre.h b/chre_api/legacy/v1_10/chre.h
new file mode 100644
index 0000000..9b87d08
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CHRE_H_
+#define _CHRE_H_
+
+/**
+ * @file
+ * This header file includes all the headers which combine to fully define the
+ * interface for the Context Hub Runtime Environment (CHRE).  This interface is
+ * of interest to both implementers of CHREs and authors of nanoapps.  The API
+ * documentation attempts to address concerns of both.
+ *
+ * See individual header files for API details, and general comments below
+ * for overall platform information.
+ */
+
+#include <chre/audio.h>
+#include <chre/ble.h>
+#include <chre/common.h>
+#include <chre/event.h>
+#include <chre/gnss.h>
+#include <chre/nanoapp.h>
+#include <chre/re.h>
+#include <chre/sensor.h>
+#include <chre/toolchain.h>
+#include <chre/user_settings.h>
+#include <chre/version.h>
+#include <chre/wifi.h>
+#include <chre/wwan.h>
+
+/**
+ * @mainpage
+ * CHRE is the Context Hub Runtime Environment.  CHRE is used in Android to run
+ * contextual applications, called nanoapps, in a low-power processing domain
+ * other than the applications processor that runs Android itself.  The CHRE
+ * API, documented herein, is the common interface exposed to nanoapps for any
+ * compatible CHRE implementation.  The CHRE API provides the ability for
+ * creating nanoapps that are code-compatible across different CHRE
+ * implementations and underlying platforms. Refer to the following sections for
+ * a discussion on some important details of CHRE that aren't explicitly exposed
+ * in the API itself.
+ *
+ * @section entry_points Entry points
+ *
+ * The following entry points are used to bind a nanoapp to the CHRE system, and
+ * all three must be implemented by any nanoapp (see chre/nanoapp.h):
+ * - nanoappStart: initialization
+ * - nanoappHandleEvent: hook for event-driven processing
+ * - nanoappEnd: graceful teardown
+ *
+ * The CHRE implementation must also ensure that it performs these functions
+ * prior to invoking nanoappStart, or after nanoappEnd returns:
+ * - bss section zeroed out (prior to nanoappStart)
+ * - static variables initialized (prior to nanoappStart)
+ * - global C++ constructors called (prior to nanoappStart)
+ * - global C++ destructors called (after nanoappEnd)
+ *
+ * @section threading Threading model
+ *
+ * A CHRE implementation is free to choose among many different
+ * threading models, including a single-threaded system or a multi-threaded
+ * system with preemption.  The current platform definition is agnostic to this
+ * underlying choice.  However, the CHRE implementation must ensure that time
+ * spent executing within a nanoapp does not significantly degrade or otherwise
+ * interfere with other functions of the system in which CHRE is implemented,
+ * especially latency-sensitive tasks such as sensor event delivery to the AP.
+ * In other words, it must ensure that these functions can either occur in
+ * parallel or preempt a nanoapp's execution.  The current version of the API
+ * does not specify whether the implementation allows for CPU sharing between
+ * nanoapps on a more granular level than the handling of individual events [1].
+ * In any case, event ordering from the perspective of an individual nanoapp
+ * must be FIFO, but the CHRE implementation may choose to violate total
+ * ordering of events across all nanoapps to achieve more fair resource sharing,
+ * but this is not required.
+ *
+ * This version of the CHRE API does require that all nanoapps are treated as
+ * non-reentrant, meaning that only one instance of program flow can be inside
+ * an individual nanoapp at any given time.  That is, any of the functions of
+ * the nanoapp, including the entry points and all other callbacks, cannot be
+ * invoked if a previous invocation to the same or any other function in the
+ * nanoapp has not completed yet.
+ *
+ * For example, if a nanoapp is currently in nanoappHandleEvent(), the CHRE is
+ * not allowed to call nanoappHandleEvent() again, or to call a memory freeing
+ * callback.  Similarly, if a nanoapp is currently in a memory freeing
+ * callback, the CHRE is not allowed to call nanoappHandleEvent(), or invoke
+ * another memory freeing callback.
+ *
+ * There are two exceptions to this rule: If an invocation of chreSendEvent()
+ * fails (returns 'false'), it is allowed to immediately invoke the memory
+ * freeing callback passed into that function.  This is a rare case, and one
+ * where otherwise a CHRE implementation is likely to leak memory. Similarly,
+ * chreSendMessageToHost() is allowed to invoke the memory freeing callback
+ * directly, whether it returns 'true' or 'false'.  This is because the CHRE
+ * implementation may copy the message data to its own buffer, and therefore
+ * wouldn't need the nanoapp-supplied buffer after chreSendMessageToHost()
+ * returns.
+ *
+ * For a nanoapp author, this means no thought needs to be given to
+ * synchronization issues with global objects, as they will, by definition,
+ * only be accessed by a single thread at once.
+ *
+ * [1]: Note to CHRE implementers: A future version of the CHRE platform may
+ * require multi-threading with preemption.  This is mentioned as a heads up,
+ * and to allow implementors deciding between implementation approaches to
+ * make the most informed choice.
+ *
+ * @section timing Timing
+ *
+ * Nanoapps should expect to be running on a highly constrained system, with
+ * little memory and little CPU.  Any single nanoapp should expect to
+ * be one of several nanoapps on the system, which also share the CPU with the
+ * CHRE and possibly other services as well.
+ *
+ * Thus, a nanoapp needs to be efficient in its memory and CPU usage.
+ * Also, as noted in the Threading Model section, a CHRE implementation may
+ * be single threaded.  As a result, all methods invoked in a nanoapp
+ * (like nanoappStart, nanoappHandleEvent, memory free callbacks, etc.)
+ * must run "quickly".  "Quickly" is difficult to define, as there is a
+ * diversity of Context Hub hardware.  Nanoapp authors are strongly recommended
+ * to limit their application to consuming no more than 1 second of CPU time
+ * prior to returning control to the CHRE implementation.  A CHRE implementation
+ * may consider a nanoapp as unresponsive if it spends more time than this to
+ * process a single event, and take corrective action.
+ *
+ * A nanoapp may have the need to occasionally perform a large block of
+ * calculations that exceeds the 1 second guidance.  The recommended approach in
+ * this case is to split up the large block of calculations into smaller
+ * batches.  In one call into the nanoapp, the nanoapp can perform the first
+ * batch, and then set a timer or send an event (chreSendEvent()) to itself
+ * indicating which batch should be done next. This will allow the nanoapp to
+ * perform the entire calculation over time, without monopolizing system
+ * resources.
+ *
+ * @section floats Floating point support
+ *
+ * The C type 'float' is used in this API, and thus a CHRE implementation
+ * is required to support 'float's.
+ *
+ * Support of the C types 'double' and 'long double' is optional for a
+ * CHRE implementation.  Note that if a CHRE decides to support them, unlike
+ * 'float' support, there is no requirement that this support is particularly
+ * efficient.  So nanoapp authors should be aware this may be inefficient.
+ *
+ * If a CHRE implementation chooses not to support 'double' or
+ * 'long double', then the build toolchain setup provided needs to set
+ * the preprocessor define CHRE_NO_DOUBLE_SUPPORT.
+ *
+ * @section compat CHRE and Nanoapp compatibility
+ *
+ * CHRE implementations must make affordances to maintain binary compatibility
+ * across minor revisions of the API version (e.g. v1.1 to v1.2).  This applies
+ * to both running a nanoapp compiled for a newer version of the API on a CHRE
+ * implementation built against an older version (backwards compatibility), and
+ * vice versa (forwards compatibility).  API changes that are acceptable in
+ * minor version changes that may require special measures to ensure binary
+ * compatibility include: addition of new functions; addition of arguments to
+ * existing functions when the default value used for nanoapps compiled against
+ * the old version is well-defined and does not affect existing functionality;
+ * and addition of fields to existing structures, even when this induces a
+ * binary layout change (this should be made rare via judicious use of reserved
+ * fields).  API changes that must only occur alongside a major version change
+ * and are therefore not compatible include: removal of any function, argument,
+ * field in a data structure, or mandatory functional behavior that a nanoapp
+ * may depend on; any change in the interpretation of an existing data structure
+ * field that alters the way it was defined previously (changing the units of a
+ * field would fall under this, but appropriating a previously reserved field
+ * for some new functionality would not); and any change in functionality or
+ * expected behavior that conflicts with the previous definition.
+ *
+ * Note that the CHRE API only specifies the software interface between a
+ * nanoapp and the CHRE system - the binary interface (ABI) between nanoapp and
+ * CHRE is necessarily implementation-dependent.  Therefore, the recommended
+ * approach to accomplish binary compatibility is to build a Nanoapp Support
+ * Library (NSL) that is specific to the CHRE implementation into the nanoapp
+ * binary, and use it to handle ABI details in a way that ensures compatibility.
+ * In addition, to accomplish forwards compatibility, the CHRE implementation is
+ * expected to recognize the CHRE API version that a nanoapp is targeting and
+ * engage compatibility behaviors where necessary.
+ *
+ * By definition, major API version changes (e.g. v1.1 to v2.0) break
+ * compatibility.  Therefore, a CHRE implementation must not attempt to load a
+ * nanoapp that is targeting a newer major API version.
+ */
+
+#endif  /* _CHRE_H_ */
+
diff --git a/chre_api/legacy/v1_10/chre/audio.h b/chre_api/legacy/v1_10/chre/audio.h
new file mode 100644
index 0000000..085329e
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/audio.h
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_AUDIO_H_
+#define _CHRE_AUDIO_H_
+
+/**
+ * @file
+ * The API for requesting audio in the Context Hub Runtime Environment.
+ *
+ * This includes the definition of audio data structures and the ability to
+ * request audio streams.
+ */
+
+#include <chre/event.h>
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The current compatibility version of the chreAudioDataEvent structure.
+ */
+#define CHRE_AUDIO_DATA_EVENT_VERSION  UINT8_C(1)
+
+/**
+ * Produce an event ID in the block of IDs reserved for audio
+ * @param offset Index into audio event ID block; valid range [0,15]
+ */
+#define CHRE_AUDIO_EVENT_ID(offset)  (CHRE_EVENT_AUDIO_FIRST_EVENT + (offset))
+
+/**
+ * nanoappHandleEvent argument: struct chreAudioSourceStatusEvent
+ *
+ * Indicates a change in the format and/or rate of audio data provided to a
+ * nanoapp.
+ */
+#define CHRE_EVENT_AUDIO_SAMPLING_CHANGE  CHRE_AUDIO_EVENT_ID(0)
+
+/**
+ * nanoappHandleEvent argument: struct chreAudioDataEvent
+ *
+ * Provides a buffer of audio data to a nanoapp.
+ */
+#define CHRE_EVENT_AUDIO_DATA  CHRE_AUDIO_EVENT_ID(1)
+
+/**
+ * The maximum size of the name of an audio source including the
+ * null-terminator.
+ */
+#define CHRE_AUDIO_SOURCE_NAME_MAX_SIZE  (40)
+
+/**
+ * Helper values for sample rates.
+ *
+ * @defgroup CHRE_AUDIO_SAMPLE_RATES
+ * @{
+ */
+
+//! 16kHz Audio Sample Data
+#define CHRE_AUDIO_SAMPLE_RATE_16KHZ  (16000)
+
+/** @} */
+
+/**
+ * Formats for audio that can be provided to a nanoapp.
+ */
+enum chreAudioDataFormat {
+  /**
+   * Unsigned, 8-bit u-Law encoded data as specified by ITU-T G.711.
+   */
+  CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW = 0,
+
+  /**
+   * Signed, 16-bit linear PCM data. Endianness must be native to the local
+   * processor.
+   */
+  CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM = 1,
+};
+
+/**
+ * A description of an audio source available to a nanoapp.
+ *
+ * This provides a description of an audio source with a name and a
+ * description of the format of the provided audio data.
+ */
+struct chreAudioSource {
+  /**
+   * A human readable name for this audio source. This is a C-style,
+   * null-terminated string. The length must be less than or equal to
+   * CHRE_AUDIO_SOURCE_NAME_MAX_SIZE bytes (including the null-terminator) and
+   * is expected to describe the source of the audio in US English. All
+   * characters must be printable (i.e.: isprint would return true for all
+   * characters in the name for the EN-US locale). The typical use of this field
+   * is for a nanoapp to log the name of the audio source that it is using.
+   *
+   * Example: "Camcorder Microphone"
+   */
+  const char *name;
+
+  /**
+   * The sampling rate in hertz of this mode. This value is rounded to the
+   * nearest integer. Typical values might include 16000, 44100 and 44800.
+   *
+   * If the requested audio source is preempted by another feature of the system
+   * (e.g. hotword), a gap may occur in received audio data. This is indicated
+   * to the client by posting a CHRE_EVENT_AUDIO_SAMPLING_CHANGE event. The
+   * nanoapp will then receive another CHRE_EVENT_AUDIO_SAMPLING_CHANGE event
+   * once the audio source is available again.
+   */
+  uint32_t sampleRate;
+
+  /**
+   * The minimum amount of time that this audio source can be buffered, in
+   * nanoseconds. Audio data is delivered to nanoapps in buffers. This specifies
+   * the minimum amount of data that can be delivered to a nanoapp without
+   * losing data. A request for a buffer that is smaller than this will fail.
+   */
+  uint64_t minBufferDuration;
+
+  /**
+   * The maximum amount of time that this audio source can be buffered, in
+   * nanoseconds. Audio data is delivered to nanoapps in buffers. This specifies
+   * the maximum amount of data that can be stored by the system in one event
+   * without losing data. A request for a buffer that is larger than this will
+   * fail.
+   */
+  uint64_t maxBufferDuration;
+
+  /**
+   * The format for data provided to the nanoapp. This will be assigned to one
+   * of the enum chreAudioDataFormat values.
+   */
+  uint8_t format;
+};
+
+/**
+ * The current status of an audio source.
+ */
+struct chreAudioSourceStatus {
+  /**
+   * Set to true if the audio source is currently enabled by this nanoapp. If
+   * this struct is provided by a CHRE_EVENT_AUDIO_SAMPLING_CHANGE event, it
+   * must necessarily be set to true because sampling change events are only
+   * sent for sources which this nanoapp has actively subscribed to. If this
+   * struct is obtained from the chreAudioGetStatus API, it may be set to true
+   * or false depending on if audio is currently enabled.
+   */
+  bool enabled;
+
+  /**
+   * Set to true if the audio source is currently suspended and no audio data
+   * will be received from this source.
+   */
+  bool suspended;
+};
+
+/**
+ * The nanoappHandleEvent argument for CHRE_EVENT_AUDIO_SAMPLING_CHANGE.
+ */
+struct chreAudioSourceStatusEvent {
+  /**
+   * The audio source which has completed a status change.
+   */
+  uint32_t handle;
+
+  /**
+   * The status of this audio source.
+   */
+  struct chreAudioSourceStatus status;
+};
+
+/**
+ * The nanoappHandleEvent argument for CHRE_EVENT_AUDIO_DATA.
+ *
+ * One example of the sequence of events for a nanoapp to receive audio data is:
+ *
+ * 1. CHRE_EVENT_AUDIO_SAMPLING_CHANGE - Indicates that audio data is not
+ *                                       suspended.
+ * 2. CHRE_EVENT_AUDIO_DATA - One buffer of audio samples. Potentially repeated.
+ * 3. CHRE_EVENT_AUDIO_SAMPLING_CHANGE - Indicates that audio data has suspended
+ *                                       which indicates a gap in the audio.
+ * 4. CHRE_EVENT_AUDIO_SAMPLING_CHANGE - Indicates that audio data has resumed
+ *                                       and that audio data may be delivered
+ *                                       again if enough samples are buffered.
+ * 5. CHRE_EVENT_AUDIO_DATA - One buffer of audio samples. Potentially repeated.
+ *                            The nanoapp must tolerate a gap in the timestamps.
+ *
+ * This process repeats for as long as an active request is made for an audio
+ * source. A CHRE_EVENT_AUDIO_SAMPLING_CHANGE does not guarantee that the next
+ * event will be a CHRE_EVENT_AUDIO_DATA event when suspended is set to false.
+ * It may happen that the audio source is suspended before a complete buffer can
+ * be captured. This will cause another CHRE_EVENT_AUDIO_SAMPLING_CHANGE event
+ * to be dispatched with suspended set to true before a buffer is delivered.
+ *
+ * Audio events must be delivered to a nanoapp in order.
+ */
+struct chreAudioDataEvent {
+  /**
+   * Indicates the version of the structure, for compatibility purposes. Clients
+   * do not normally need to worry about this field; the CHRE implementation
+   * guarantees that the client only receives the structure version it expects.
+   */
+  uint8_t version;
+
+  /**
+   * Additional bytes reserved for future use; must be set to 0.
+   */
+  uint8_t reserved[3];
+
+  /**
+   * The handle for which this audio data originated from.
+   */
+  uint32_t handle;
+
+  /**
+   * The base timestamp for this buffer of audio data, from the same time base
+   * as chreGetTime() (in nanoseconds). The audio API does not provide
+   * timestamps for each audio sample. This timestamp corresponds to the first
+   * sample of the buffer. Even though the value is expressed in nanoseconds,
+   * there is an expectation that the sample clock may drift and nanosecond
+   * level accuracy may not be possible. The goal is to be as accurate as
+   * possible within reasonable limitations of a given system.
+   */
+  uint64_t timestamp;
+
+  /**
+   * The sample rate for this buffer of data in hertz, rounded to the nearest
+   * integer. Fractional sampling rates are not supported. Typical values might
+   * include 16000, 44100 and 48000.
+   */
+  uint32_t sampleRate;
+
+  /**
+   * The number of samples provided with this buffer.
+   */
+  uint32_t sampleCount;
+
+  /**
+   * The format of this audio data. This enumeration and union of pointers below
+   * form a tagged struct. The consumer of this API must use this enum to
+   * determine which samples pointer below to dereference. This will be assigned
+   * to one of the enum chreAudioDataFormat values.
+   */
+  uint8_t format;
+
+  /**
+   * A union of pointers to various formats of sample data. These correspond to
+   * the valid chreAudioDataFormat values.
+   */
+  union {
+    const uint8_t *samplesULaw8;
+    const int16_t *samplesS16;
+  };
+};
+
+/**
+ * Retrieves information about an audio source supported by the current CHRE
+ * implementation. The source returned by the runtime must not change for the
+ * entire lifecycle of the Nanoapp and hot-pluggable audio sources are not
+ * supported.
+ *
+ * A simple example of iterating all available audio sources is provided here:
+ *
+ * struct chreAudioSource audioSource;
+ * for (uint32_t i = 0; chreAudioGetSource(i, &audioSource); i++) {
+ *     chreLog(CHRE_LOG_INFO, "Found audio source: %s", audioSource.name);
+ * }
+ *
+ * Handles provided to this API must be a stable value for the entire duration
+ * of a nanoapp. Handles for all audio sources must be zero-indexed and
+ * contiguous. The following are examples of handles that could be provided to
+ * this API:
+ *
+ *   Valid: 0
+ *   Valid: 0, 1, 2, 3
+ * Invalid: 1, 2, 3
+ * Invalid: 0, 2
+ *
+ * @param handle The handle for an audio source to obtain details for. The
+ *     range of acceptable handles must be zero-indexed and contiguous.
+ * @param audioSource A struct to populate with details of the audio source.
+ * @return true if the query was successful, false if the provided handle is
+ *     invalid or the supplied audioSource is NULL.
+ *
+ * @since v1.2
+ */
+bool chreAudioGetSource(uint32_t handle, struct chreAudioSource *audioSource);
+
+/**
+ * Nanoapps must define CHRE_NANOAPP_USES_AUDIO somewhere in their build
+ * system (e.g. Makefile) if the nanoapp needs to use the following audio APIs.
+ * In addition to allowing access to these APIs, defining this macro will also
+ * ensure CHRE enforces that all host clients this nanoapp talks to have the
+ * required Android permissions needed to listen to audio data by adding
+ * metadata to the nanoapp.
+ */
+#if defined(CHRE_NANOAPP_USES_AUDIO) || !defined(CHRE_IS_NANOAPP_BUILD)
+
+/**
+ * Configures delivery of audio data to the current nanoapp. Note that this may
+ * not fully disable the audio source if it is used by other clients in the
+ * system but it will halt data delivery to the nanoapp.
+ *
+ * The bufferDuration and deliveryInterval parameters as described below are
+ * used together to determine both how much and how often to deliver data to a
+ * nanoapp, respectively. A nanoapp will always be provided the requested
+ * amount of data at the requested interval, even if another nanoapp in CHRE
+ * requests larger/more frequent buffers or smaller/less frequent buffers.
+ * These two buffering parameters allow describing the duty cycle of captured
+ * audio data. If a nanoapp wishes to receive all available audio data, it will
+ * specify a bufferDuration and deliveryInterval that are equal. A 50% duty
+ * cycle would be achieved by specifying a deliveryInterval that is double the
+ * value of the bufferDuration provided. These parameters allow the audio
+ * subsystem to operate at less than 100% duty cycle and permits use of
+ * incomplete audio data without periodic reconfiguration of the source.
+ *
+ * Two examples are illustrated below:
+ *
+ * Target duty cycle: 50%
+ * bufferDuration:    2
+ * deliveryInterval:  4
+ *
+ * Time       0   1   2   3   4   5   6   7
+ * Batch                  A               B
+ * Sample    --  --  a1  a2  --  --  b1  b2
+ * Duration          [    ]          [    ]
+ * Interval  [            ]  [            ]
+ *
+ *
+ * Target duty cycle: 100%
+ * bufferDuration:    4
+ * deliveryInterval:  4
+ *
+ * Time       0   1   2   3   4   5   6   7
+ * Batch                  A               B
+ * Sample    a1  a2  a3  a4  b1  b2  b3  b4
+ * Duration  [            ]  [            ]
+ * Interval  [            ]  [            ]
+ *
+ *
+ * This is expected to reduce power overall.
+ *
+ * The first audio buffer supplied to the nanoapp may contain data captured
+ * prior to the request. This could happen if the microphone was already enabled
+ * and reading into a buffer prior to the nanoapp requesting audio data for
+ * itself. The nanoapp must tolerate this.
+ *
+ * It is important to note that multiple logical audio sources (e.g. different
+ * sample rate, format, etc.) may map to one physical audio source. It is
+ * possible for a nanoapp to request audio data from more than one logical
+ * source at a time. Audio data may be suspended for either the current or other
+ * requests. The CHRE_EVENT_AUDIO_SAMPLING_CHANGE will be posted to all clients
+ * if such a change occurs. It is also possible for the request to succeed and
+ * all audio sources are serviced simultaneously. This is implementation defined
+ * but at least one audio source must function correctly if it is advertised,
+ * under normal conditions (e.g. not required for some other system function,
+ * such as hotword).
+ *
+ * @param handle The handle for this audio source. The handle for the desired
+ *     audio source can be determined using chreAudioGetSource().
+ * @param enable true if enabling the source, false otherwise. When passed as
+ *     false, the bufferDuration and deliveryInterval parameters are ignored.
+ * @param bufferDuration The amount of time to capture audio samples from this
+ *     audio source, in nanoseconds per delivery interval. This value must be
+ *     in the range of minBufferDuration/maxBufferDuration for this source or
+ *     the request will fail. The number of samples captured per buffer will be
+ *     derived from the sample rate of the source and the requested duration and
+ *     rounded down to the nearest sample boundary.
+ * @param deliveryInterval Desired time between each CHRE_EVENT_AUDIO_DATA
+ *     event. This allows specifying the complete duty cycle of a request
+ *     for audio data, in nanoseconds. This value must be greater than or equal
+ *     to bufferDuration or the request will fail due to an invalid
+ *     configuration.
+ * @return true if the configuration was successful, false if invalid parameters
+ *     were provided (non-existent handle, invalid buffering configuration).
+ *
+ * @since v1.2
+ * @note Requires audio permission
+ */
+bool chreAudioConfigureSource(uint32_t handle, bool enable,
+                              uint64_t bufferDuration,
+                              uint64_t deliveryInterval);
+
+/**
+ * Gets the current chreAudioSourceStatus struct for a given audio handle.
+ *
+ * @param handle The handle for the audio source to query. The provided handle
+ *     is obtained from a chreAudioSource which is requested from the
+ *     chreAudioGetSource API.
+ * @param status The current status of the supplied audio source.
+ * @return true if the provided handle is valid and the status was obtained
+ *     successfully, false if the handle was invalid or status is NULL.
+ *
+ * @since v1.2
+ * @note Requires audio permission
+ */
+bool chreAudioGetStatus(uint32_t handle, struct chreAudioSourceStatus *status);
+
+#else  /* defined(CHRE_NANOAPP_USES_AUDIO) || !defined(CHRE_IS_NANOAPP_BUILD) */
+#define CHRE_AUDIO_PERM_ERROR_STRING \
+    "CHRE_NANOAPP_USES_AUDIO must be defined when building this nanoapp in " \
+    "order to refer to "
+#define chreAudioConfigureSource(...) \
+    CHRE_BUILD_ERROR(CHRE_AUDIO_PERM_ERROR_STRING "chreAudioConfigureSource")
+#define chreAudioGetStatus(...) \
+    CHRE_BUILD_ERROR(CHRE_AUDIO_PERM_ERROR_STRING "chreAudioGetStatus")
+#endif  /* defined(CHRE_NANOAPP_USES_AUDIO) || !defined(CHRE_IS_NANOAPP_BUILD) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_AUDIO_H_ */
diff --git a/chre_api/legacy/v1_10/chre/ble.h b/chre_api/legacy/v1_10/chre/ble.h
new file mode 100644
index 0000000..dc8e900
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/ble.h
@@ -0,0 +1,967 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef CHRE_BLE_H_
+#define CHRE_BLE_H_
+
+/**
+ * @file
+ * CHRE BLE (Bluetooth Low Energy, Bluetooth LE) API.
+ * The CHRE BLE API currently supports BLE scanning features.
+ *
+ * The features in the CHRE BLE API are a subset and adaptation of Android
+ * capabilities as described in the Android BLE API and HCI requirements.
+ * ref:
+ * https://developer.android.com/guide/topics/connectivity/bluetooth/ble-overview
+ * ref: https://source.android.com/devices/bluetooth/hci_requirements
+ *
+ * All byte arrays in the CHRE BLE API follow the byte order used OTA unless
+ * specified otherwise, and multi-byte types, for example uint16_t, follow the
+ * processor's native byte order. One notable exception is addresses. Address
+ * fields in both scan filters and advertising reports must be in big endian
+ * byte order to match the Android Bluetooth API (ref:
+ * https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#getRemoteDevice(byte[])).
+ */
+
+#include <chre/common.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The set of flags returned by chreBleGetCapabilities().
+ *
+ * @defgroup CHRE_BLE_CAPABILITIES
+ * @{
+ */
+//! No BLE APIs are supported
+#define CHRE_BLE_CAPABILITIES_NONE UINT32_C(0)
+
+//! CHRE supports BLE scanning
+#define CHRE_BLE_CAPABILITIES_SCAN UINT32_C(1 << 0)
+
+//! CHRE BLE supports batching of scan results, either through Android-specific
+//! HCI (OCF: 0x156), or by the CHRE framework, internally.
+//! @since v1.7 Platforms with this capability must also support flushing scan
+//! results during a batched scan.
+#define CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING UINT32_C(1 << 1)
+
+//! CHRE BLE scan supports best-effort hardware filtering. If filtering is
+//! available, chreBleGetFilterCapabilities() returns a bitmap indicating the
+//! specific filtering capabilities that are supported.
+//! To differentiate best-effort vs. no filtering, the following requirement
+//! must be met for this flag:
+//! If only one nanoapp is requesting BLE scans and there are no BLE scans from
+//! the AP, only filtered results will be provided to the nanoapp.
+#define CHRE_BLE_CAPABILITIES_SCAN_FILTER_BEST_EFFORT UINT32_C(1 << 2)
+
+//! CHRE BLE supports reading the RSSI of a specified LE-ACL connection handle.
+#define CHRE_BLE_CAPABILITIES_READ_RSSI UINT32_C(1 << 3)
+/** @} */
+
+/**
+ * The set of flags returned by chreBleGetFilterCapabilities().
+ *
+ * The representative bit for each filtering capability is based on the sub-OCF
+ * of the Android filtering HCI vendor-specific command (LE_APCF_Command, OCF:
+ * 0x0157) for that particular filtering capability, as found in
+ * https://source.android.com/devices/bluetooth/hci_requirements
+ *
+ * For example, the Service Data filter has a sub-command of 0x7; hence
+ * the filtering capability is indicated by (1 << 0x7).
+ *
+ * @defgroup CHRE_BLE_FILTER_CAPABILITIES
+ * @{
+ */
+//! No CHRE BLE filters are supported
+#define CHRE_BLE_FILTER_CAPABILITIES_NONE UINT32_C(0)
+
+//! CHRE BLE supports RSSI filters
+#define CHRE_BLE_FILTER_CAPABILITIES_RSSI UINT32_C(1 << 1)
+
+//! CHRE BLE supports Broadcaster Address filters (Corresponding HCI OCF:
+//! 0x0157, Sub-command: 0x02)
+//! @since v1.9
+#define CHRE_BLE_FILTER_CAPABILITIES_BROADCASTER_ADDRESS UINT32_C(1 << 2)
+
+//! CHRE BLE supports Manufacturer Data filters (Corresponding HCI OCF: 0x0157,
+//! Sub-command: 0x06)
+//! @since v1.8
+#define CHRE_BLE_FILTER_CAPABILITIES_MANUFACTURER_DATA UINT32_C(1 << 6)
+
+//! CHRE BLE supports Service Data filters (Corresponding HCI OCF: 0x0157,
+//! Sub-command: 0x07)
+#define CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA UINT32_C(1 << 7)
+/** @} */
+
+/**
+ * Produce an event ID in the block of IDs reserved for BLE.
+ *
+ * Valid input range is [0, 15]. Do not add new events with ID > 15
+ * (see chre/event.h)
+ *
+ * @param offset Index into BLE event ID block; valid range is [0, 15].
+ *
+ * @defgroup CHRE_BLE_EVENT_ID
+ * @{
+ */
+#define CHRE_BLE_EVENT_ID(offset) (CHRE_EVENT_BLE_FIRST_EVENT + (offset))
+
+/**
+ * nanoappHandleEvent argument: struct chreAsyncResult
+ *
+ * Communicates the asynchronous result of a request to the BLE API. The
+ * requestType field in {@link #chreAsyncResult} is set to a value from enum
+ * chreBleRequestType.
+ *
+ * This is used for results of async config operations which need to
+ * interop with lower level code (potentially in a different thread) or send an
+ * HCI command to the FW and wait on the response.
+ */
+#define CHRE_EVENT_BLE_ASYNC_RESULT CHRE_BLE_EVENT_ID(0)
+
+/**
+ * nanoappHandleEvent argument: struct chreBleAdvertisementEvent
+ *
+ * Provides results of a BLE scan.
+ */
+#define CHRE_EVENT_BLE_ADVERTISEMENT CHRE_BLE_EVENT_ID(1)
+
+/**
+ * nanoappHandleEvent argument: struct chreAsyncResult
+ *
+ * Indicates that a flush request made via chreBleFlushAsync() is complete, and
+ * all batched advertisements resulting from the flush have been delivered via
+ * preceding CHRE_EVENT_BLE_ADVERTISEMENT events.
+ *
+ * @since v1.7
+ */
+#define CHRE_EVENT_BLE_FLUSH_COMPLETE CHRE_BLE_EVENT_ID(2)
+
+/**
+ * nanoappHandleEvent argument: struct chreBleReadRssiEvent
+ *
+ * Provides the RSSI of an LE ACL connection following a call to
+ * chreBleReadRssiAsync().
+ *
+ * @since v1.8
+ */
+#define CHRE_EVENT_BLE_RSSI_READ CHRE_BLE_EVENT_ID(3)
+
+/**
+ * nanoappHandleEvent argument: struct chreBatchCompleteEvent
+ *
+ * This event is generated if the platform enabled batching, and when all
+ * events in a single batch has been delivered (for example, batching
+ * CHRE_EVENT_BLE_ADVERTISEMENT events if the platform has
+ * CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING enabled, and a non-zero
+ * reportDelayMs in chreBleStartScanAsync() was accepted).
+ *
+ * If the nanoapp receives a CHRE_EVENT_BLE_SCAN_STATUS_CHANGE with a non-zero
+ * reportDelayMs and enabled set to true, then this event must be generated.
+ *
+ * @since v1.8
+ */
+#define CHRE_EVENT_BLE_BATCH_COMPLETE CHRE_BLE_EVENT_ID(4)
+
+/**
+ * nanoappHandleEvent argument: struct chreBleScanStatus
+ *
+ * This event is generated when the values in chreBleScanStatus changes.
+ *
+ * @since v1.8
+ */
+#define CHRE_EVENT_BLE_SCAN_STATUS_CHANGE CHRE_BLE_EVENT_ID(5)
+
+// NOTE: Do not add new events with ID > 15
+/** @} */
+
+/**
+ * Maximum BLE (legacy) advertisement payload data length, in bytes
+ * This is calculated by subtracting 2 (type + len) from 31 (max payload).
+ */
+#define CHRE_BLE_DATA_LEN_MAX (29)
+
+/**
+ * BLE device address length, in bytes.
+ */
+#define CHRE_BLE_ADDRESS_LEN (6)
+
+/**
+ * RSSI value (int8_t) indicating no RSSI threshold.
+ */
+#define CHRE_BLE_RSSI_THRESHOLD_NONE (-128)
+
+/**
+ * RSSI value (int8_t) indicating no RSSI value available.
+ */
+#define CHRE_BLE_RSSI_NONE (127)
+
+/**
+ * Tx power value (int8_t) indicating no Tx power value available.
+ */
+#define CHRE_BLE_TX_POWER_NONE (127)
+
+/**
+ * Indicates ADI field was not provided in advertisement.
+ */
+#define CHRE_BLE_ADI_NONE (0xFF)
+
+/**
+ * The CHRE BLE advertising event type is based on the BT Core Spec v5.2,
+ * Vol 4, Part E, Section 7.7.65.13, LE Extended Advertising Report event,
+ * Event_Type.
+ *
+ * Note: helper functions are provided to avoid bugs, e.g. a nanoapp doing
+ * (eventTypeAndDataStatus == ADV_IND) instead of properly masking off reserved
+ * and irrelevant bits.
+ *
+ * @defgroup CHRE_BLE_EVENT
+ * @{
+ */
+// Extended event types
+#define CHRE_BLE_EVENT_MASK_TYPE (0x1f)
+#define CHRE_BLE_EVENT_TYPE_FLAG_CONNECTABLE (1 << 0)
+#define CHRE_BLE_EVENT_TYPE_FLAG_SCANNABLE (1 << 1)
+#define CHRE_BLE_EVENT_TYPE_FLAG_DIRECTED (1 << 2)
+#define CHRE_BLE_EVENT_TYPE_FLAG_SCAN_RSP (1 << 3)
+#define CHRE_BLE_EVENT_TYPE_FLAG_LEGACY (1 << 4)
+
+// Data status
+#define CHRE_BLE_EVENT_MASK_DATA_STATUS (0x3 << 5)
+#define CHRE_BLE_EVENT_DATA_STATUS_COMPLETE (0x0 << 5)
+#define CHRE_BLE_EVENT_DATA_STATUS_MORE_DATA_PENDING (0x1 << 5)
+#define CHRE_BLE_EVENT_DATA_STATUS_DATA_TRUNCATED (0x2 << 5)
+
+// Legacy event types
+#define CHRE_BLE_EVENT_TYPE_LEGACY_ADV_IND                                  \
+  (CHRE_BLE_EVENT_TYPE_FLAG_LEGACY | CHRE_BLE_EVENT_TYPE_FLAG_CONNECTABLE | \
+   CHRE_BLE_EVENT_TYPE_FLAG_SCANNABLE)
+#define CHRE_BLE_EVENT_TYPE_LEGACY_DIRECT_IND \
+  (CHRE_BLE_EVENT_TYPE_FLAG_LEGACY | CHRE_BLE_EVENT_TYPE_FLAG_CONNECTABLE)
+#define CHRE_BLE_EVENT_TYPE_LEGACY_ADV_SCAN_IND \
+  (CHRE_BLE_EVENT_TYPE_FLAG_LEGACY | CHRE_BLE_EVENT_TYPE_FLAG_SCANNABLE)
+#define CHRE_BLE_EVENT_TYPE_LEGACY_ADV_NONCONN_IND \
+  (CHRE_BLE_EVENT_TYPE_FLAG_LEGACY)
+#define CHRE_BLE_EVENT_TYPE_LEGACY_SCAN_RESP_ADV_IND \
+  (CHRE_BLE_EVENT_TYPE_FLAG_SCAN_RSP | CHRE_BLE_EVENT_TYPE_LEGACY_ADV_IND)
+#define CHRE_BLE_EVENT_TYPE_LEGACY_SCAN_RESP_ADV_SCAN_IND \
+  (CHRE_BLE_EVENT_TYPE_FLAG_SCAN_RSP | CHRE_BLE_EVENT_TYPE_LEGACY_ADV_SCAN_IND)
+/** @} */
+
+/**
+ * The maximum amount of time allowed to elapse between the call to
+ * chreBleFlushAsync() and when CHRE_EVENT_BLE_FLUSH_COMPLETE is delivered to
+ * the nanoapp on a successful flush.
+ */
+#define CHRE_BLE_FLUSH_COMPLETE_TIMEOUT_NS (5 * CHRE_NSEC_PER_SEC)
+
+/**
+ * Indicates a type of request made in this API. Used to populate the resultType
+ * field of struct chreAsyncResult sent with CHRE_EVENT_BLE_ASYNC_RESULT.
+ */
+enum chreBleRequestType {
+  CHRE_BLE_REQUEST_TYPE_START_SCAN = 1,
+  CHRE_BLE_REQUEST_TYPE_STOP_SCAN = 2,
+  CHRE_BLE_REQUEST_TYPE_FLUSH = 3,      //!< @since v1.7
+  CHRE_BLE_REQUEST_TYPE_READ_RSSI = 4,  //!< @since v1.8
+};
+
+/**
+ * CHRE BLE scan modes identify functional scan levels without specifying or
+ * guaranteeing particular scan parameters (e.g. duty cycle, interval, radio
+ * chain).
+ *
+ * The actual scan parameters may be platform dependent and may change without
+ * notice in real time based on contextual cues, etc.
+ *
+ * Scan modes should be selected based on use cases as described.
+ */
+enum chreBleScanMode {
+  //! A background scan level for always-running ambient applications.
+  //! A representative duty cycle may be between 3 - 10 % (tentative, and
+  //! with no guarantees).
+  CHRE_BLE_SCAN_MODE_BACKGROUND = 1,
+
+  //! A foreground scan level to be used for short periods.
+  //! A representative duty cycle may be between 10 - 20 % (tentative, and
+  //! with no guarantees).
+  CHRE_BLE_SCAN_MODE_FOREGROUND = 2,
+
+  //! A very high duty cycle scan level to be used for very short durations.
+  //! A representative duty cycle may be between 50 - 100 % (tentative, and
+  //! with no guarantees).
+  CHRE_BLE_SCAN_MODE_AGGRESSIVE = 3,
+};
+
+/**
+ * Selected AD Types are available among those defined in the Bluetooth spec.
+ * Assigned Numbers, Generic Access Profile.
+ * ref: https://www.bluetooth.com/specifications/assigned-numbers/
+ */
+enum chreBleAdType {
+  //! Service Data with 16-bit UUID
+  //! @since v1.8 CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16 was renamed
+  //! CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE to reflect that nanoapps
+  //! compiled against v1.8+ should use OTA format for service data filters.
+  CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE = 0x16,
+
+  //! Manufacturer Specific Data
+  //! @since v1.8
+  CHRE_BLE_AD_TYPE_MANUFACTURER_DATA = 0xff,
+};
+
+/**
+ * Generic filters are used to filter for the presence of AD structures in the
+ * data field of LE Extended Advertising Report events (ref: BT Core Spec v5.3,
+ * Vol 3, Part E, Section 11).
+ *
+ * The CHRE generic filter structure represents a generic filter on an AD Type
+ * as defined in the Bluetooth spec Assigned Numbers, Generic Access Profile
+ * (ref: https://www.bluetooth.com/specifications/assigned-numbers/). This
+ * generic structure is used by the Android HCI Advertising Packet Content
+ * Filter (APCF) AD Type sub-command 0x09 (ref:
+ * https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_apcf_command-ad_type_sub_cmd).
+ *
+ * The filter is matched when an advertisement event contains an AD structure in
+ * its data field that matches the following criteria:
+ *   AdStructure.type == type
+ *   AdStructure.data & dataMask == data & dataMask
+ *
+ * The maximum data length is limited to the maximum possible legacy
+ * advertisement payload data length (29 bytes). The data and dataMask must be
+ * in OTA format. For each zero bit of the dataMask, the corresponding
+ * data bit must also be zero.
+ *
+ * Note that the CHRE implementation may not support every kind of filter that
+ * can be represented by this structure. Use chreBleGetFilterCapabilities() to
+ * discover supported filtering capabilities at runtime.
+ *
+ * Example 1: To filter on a 16 bit service data UUID of 0xFE2C, the following
+ * settings would be used:
+ *   type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE
+ *   len = 2
+ *   data = {0x2C, 0xFE}
+ *   dataMask = {0xFF, 0xFF}
+ *
+ * Example 2: To filter for manufacturer data of 0x12, 0x34 from Google (0x00E0),
+ * the following settings would be used:
+ *   type = CHRE_BLE_AD_TYPE_MANUFACTURER_DATA
+ *   len = 4
+ *   data = {0xE0, 0x00, 0x12, 0x34}
+ *   dataMask = {0xFF, 0xFF, 0xFF, 0xFF}
+ *
+ * Refer to "Supplement to the Bluetooth Core Specification for details (v9,
+ * Part A, Section 1.4)" for details regarding the manufacturer data format.
+ */
+struct chreBleGenericFilter {
+  //! Acceptable values among enum chreBleAdType
+  uint8_t type;
+
+  /**
+   * Length of data and dataMask. AD payloads shorter than this length will not
+   * be matched by the filter. Length must be greater than 0.
+   */
+  uint8_t len;
+
+  //! Used in combination with dataMask to filter an advertisement
+  uint8_t data[CHRE_BLE_DATA_LEN_MAX];
+
+  //! Used in combination with data to filter an advertisement
+  uint8_t dataMask[CHRE_BLE_DATA_LEN_MAX];
+};
+
+/**
+ * Broadcaster address filters are used to filter by the address field of the LE
+ * Extended Advertising Report event which is defined in the BT Core Spec v5.3,
+ * Vol 4, Part E, Section 7.7.65.13.
+ *
+ * The CHRE broadcaster address filter structure is modeled after the
+ * Advertising Packet Content Filter (APCF) HCI broadcaster address sub-command
+ * 0x02 (ref:
+ * https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_apcf_command-broadcast_address_sub_cmd).
+ * However, it differs from this HCI command in two major ways:
+ *
+ * 1) The CHRE broadcaster address filter does not filter by address type at
+ *    this time. If a nanoapp wants to filter for a particular address type, it
+ *    must check the addressType field of the chreBleAdvertisingReport.
+ *
+ * 2) The broadcasterAddress must be in big endian byte order to match the
+ *    format of the Android Bluetooth API (ref:
+ *    https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#getRemoteDevice(byte[])).
+ *    This is intended to allow easier integration between nanoapp and Host
+ *    code.
+ *
+ * The filter is matched when an advertisement even meets the following
+ * criteria:
+ *   broadcasterAddress == chreBleAdvertisingReport.address.
+ *
+ * Example: To filter on the address (01:02:03:AB:CD:EF), the following
+ * settings would be used:
+ *   broadcasterAddress = {0x01, 0x02, 0x03, 0xAB, 0xCD, 0xEF}
+ *
+ * @since v1.9
+ */
+struct chreBleBroadcasterAddressFilter {
+  //! 6-byte Broadcaster address
+  uint8_t broadcasterAddress[CHRE_BLE_ADDRESS_LEN];
+};
+
+/**
+ * CHRE Bluetooth LE scan filters.
+ *
+ * @see chreBleScanFilterV1_9 for further details.
+ *
+ * @deprecated as of v1.9 due to the addition of the
+ * chreBleBroadcasterAddressFilter. New code should use chreBleScanFilterV1_9
+ * instead of this struct. This struct will be removed in a future version.
+ */
+struct chreBleScanFilter {
+  //! RSSI threshold filter (Corresponding HCI OCF: 0x0157, Sub: 0x01), where
+  //! advertisements with RSSI values below this threshold may be disregarded.
+  //! An rssiThreshold value of CHRE_BLE_RSSI_THRESHOLD_NONE indicates no RSSI
+  //! filtering.
+  int8_t rssiThreshold;
+
+  //! Number of generic scan filters provided in the scanFilters array.
+  //! A scanFilterCount value of 0 indicates no generic scan filters.
+  uint8_t scanFilterCount;
+
+  //! Pointer to an array of scan filters. If the array contains more than one
+  //! entry, advertisements matching any of the entries will be returned
+  //! (functional OR).
+  const struct chreBleGenericFilter *scanFilters;
+};
+
+/**
+ * CHRE Bluetooth LE scan filters are based on a combination of an RSSI
+ * threshold, generic filters, and broadcaster address filters.
+ *
+ * When multiple filters are specified, rssiThreshold is combined with the other
+ * filters via functional AND, and the other filters are all combined as
+ * functional OR. In other words, an advertisement matches the filter if:
+ *   rssi >= rssiThreshold
+ *   AND (matchAny(genericFilters) OR matchAny(broadcasterAddressFilters))
+ *
+ * CHRE-provided filters are implemented in a best-effort manner, depending on
+ * HW capabilities of the system and available resources. Therefore, provided
+ * scan results may be a superset of the specified filters. Nanoapps should try
+ * to take advantage of CHRE scan filters as much as possible, but must design
+ * their logic as to not depend on CHRE filtering.
+ *
+ * The syntax of CHRE scan filter definition is modeled after a combination of
+ * multiple Android HCI Advertising Packet Content Filter (APCF) sub commands
+ * including the RSSI threshold from the set filtering parameters sub command
+ * (ref:
+ * https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_apcf_command-set_filtering_parameters_sub_cmd).
+ * @see chreBleGenericFilter and chreBleBroadcasterAddressFilter for details
+ * about other APCF sub commands referenced.
+ *
+ * @since v1.9
+ */
+struct chreBleScanFilterV1_9 {
+  //! RSSI threshold filter (Corresponding HCI OCF: 0x0157, Sub: 0x01), where
+  //! advertisements with RSSI values below this threshold may be disregarded.
+  //! An rssiThreshold value of CHRE_BLE_RSSI_THRESHOLD_NONE indicates no RSSI
+  //! filtering.
+  int8_t rssiThreshold;
+
+  //! Number of generic filters provided in the scanFilters array. A
+  //! genericFilterCount value of 0 indicates no generic filters.
+  uint8_t genericFilterCount;
+
+  //! Pointer to an array of generic filters. If the array contains more than
+  //! one entry, advertisements matching any of the entries will be returned
+  //! (functional OR). This is expected to be null if genericFilterCount is 0.
+  const struct chreBleGenericFilter *genericFilters;
+
+  //! Number of broadcaster address filters provided in the
+  //! broadcasterAddressFilters array. A broadcasterAddressFilterCount value
+  //! of 0 indicates no broadcaster address filters.
+  uint8_t broadcasterAddressFilterCount;
+
+  //! Pointer to an array of broadcaster address filters. If the array contains
+  //! more than one entry, advertisements matching any of the entries will be
+  //! returned (functional OR). This is expected to be null if
+  //! broadcasterAddressFilterCount is 0.
+  const struct chreBleBroadcasterAddressFilter *broadcasterAddressFilters;
+};
+
+/**
+ * CHRE BLE advertising address type is based on the BT Core Spec v5.2, Vol 4,
+ * Part E, Section 7.7.65.13, LE Extended Advertising Report event,
+ * Address_Type.
+ */
+enum chreBleAddressType {
+  //! Public device address.
+  CHRE_BLE_ADDRESS_TYPE_PUBLIC = 0x00,
+
+  //! Random device address.
+  CHRE_BLE_ADDRESS_TYPE_RANDOM = 0x01,
+
+  //! Public identity address (corresponds to resolved private address).
+  CHRE_BLE_ADDRESS_TYPE_PUBLIC_IDENTITY = 0x02,
+
+  //! Random (static) Identity Address (corresponds to resolved private
+  //! address)
+  CHRE_BLE_ADDRESS_TYPE_RANDOM_IDENTITY = 0x03,
+
+  //! No address provided (anonymous advertisement).
+  CHRE_BLE_ADDRESS_TYPE_NONE = 0xff,
+};
+
+/**
+ * CHRE BLE physical (PHY) channel encoding type, if supported, is based on the
+ * BT Core Spec v5.2, Vol 4, Part E, Section 7.7.65.13, LE Extended Advertising
+ * Report event, entries Primary_PHY and Secondary_PHY.
+ */
+enum chreBlePhyType {
+  //! No packets on this PHY (only on the secondary channel), or feature not
+  //! supported.
+  CHRE_BLE_PHY_NONE = 0x00,
+
+  //! LE 1 MBPS PHY encoding.
+  CHRE_BLE_PHY_1M = 0x01,
+
+  //! LE 2 MBPS PHY encoding (only on the secondary channel).
+  CHRE_BLE_PHY_2M = 0x02,
+
+  //! LE long-range coded PHY encoding.
+  CHRE_BLE_PHY_CODED = 0x03,
+};
+
+/**
+ * The CHRE BLE Advertising Report event is based on the BT Core Spec v5.2,
+ * Vol 4, Part E, Section 7.7.65.13, LE Extended Advertising Report event, with
+ * the following differences:
+ *
+ * 1) A CHRE timestamp field, which can be useful if CHRE is batching results.
+ * 2) Reordering of the rssi and periodicAdvertisingInterval fields for memory
+ *    alignment (prevent padding).
+ * 3) Addition of four reserved bytes to reclaim padding.
+ * 4) The address fields are formatted in big endian byte order to match the
+ *    order specified for BluetoothDevices in the Android Bluetooth API (ref:
+ *    https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#getRemoteDevice(byte[])).
+ */
+struct chreBleAdvertisingReport {
+  //! The base timestamp, in nanoseconds, in the same time base as chreGetTime()
+  uint64_t timestamp;
+
+  //! @see CHRE_BLE_EVENT
+  uint8_t eventTypeAndDataStatus;
+
+  //! Advertising address type as defined in enum chreBleAddressType
+  uint8_t addressType;
+
+  //! Advertising device address. Formatted in big endian byte order.
+  uint8_t address[CHRE_BLE_ADDRESS_LEN];
+
+  //! Advertiser PHY on primary advertising physical channel, if supported, as
+  //! defined in enum chreBlePhyType.
+  uint8_t primaryPhy;
+
+  //! Advertiser PHY on secondary advertising physical channel, if supported, as
+  //! defined in enum chreBlePhyType.
+  uint8_t secondaryPhy;
+
+  //! Value of the Advertising SID subfield in the ADI field of the PDU among
+  //! the range of [0, 0x0f].
+  //! CHRE_BLE_ADI_NONE indicates no ADI field was provided.
+  //! Other values are reserved.
+  uint8_t advertisingSid;
+
+  //! Transmit (Tx) power in dBm. Typical values are [-127, 20].
+  //! CHRE_BLE_TX_POWER_NONE indicates Tx power not available.
+  int8_t txPower;
+
+  //! Interval of the periodic advertising in 1.25 ms intervals, i.e.
+  //! time = periodicAdvertisingInterval * 1.25 ms
+  //! 0 means no periodic advertising. Minimum value is otherwise 6 (7.5 ms).
+  uint16_t periodicAdvertisingInterval;
+
+  //! RSSI in dBm. Typical values are [-127, 20].
+  //! CHRE_BLE_RSSI_NONE indicates RSSI is not available.
+  int8_t rssi;
+
+  //! Direct address type (i.e. only accept connection requests from a known
+  //! peer device) as defined in enum chreBleAddressType.
+  uint8_t directAddressType;
+
+  //! Direct address (i.e. only accept connection requests from a known peer
+  //! device). Formatted in big endian byte order.
+  uint8_t directAddress[CHRE_BLE_ADDRESS_LEN];
+
+  //! Length of data field. Acceptable range is [0, 62] for legacy and
+  //! [0, 255] for extended advertisements.
+  uint16_t dataLength;
+
+  //! dataLength bytes of data, or null if dataLength is 0. This represents
+  //! the ADV_IND payload, optionally concatenated with SCAN_RSP, as indicated
+  //! by eventTypeAndDataStatus.
+  const uint8_t *data;
+
+  //! Reserved for future use; set to 0
+  uint32_t reserved;
+};
+
+/**
+ * A CHRE BLE Advertising Event can contain any number of CHRE BLE Advertising
+ * Reports (i.e. advertisements).
+ */
+struct chreBleAdvertisementEvent {
+  //! Reserved for future use; set to 0
+  uint16_t reserved;
+
+  //! Number of advertising reports in this event
+  uint16_t numReports;
+
+  //! Array of length numReports
+  const struct chreBleAdvertisingReport *reports;
+};
+
+/**
+ * The RSSI read on a particular LE connection handle, based on the parameters
+ * in BT Core Spec v5.3, Vol 4, Part E, Section 7.5.4, Read RSSI command
+ */
+struct chreBleReadRssiEvent {
+  //! Structure which contains the cookie associated with the original request,
+  //! along with an error code that indicates request success or failure.
+  struct chreAsyncResult result;
+
+  //! The handle upon which CHRE attempted to read RSSI.
+  uint16_t connectionHandle;
+
+  //! The RSSI of the last packet received on this connection, if valid
+  //! (-127 to 20)
+  int8_t rssi;
+};
+
+/**
+ * Describes the current status of the BLE request in the platform.
+ *
+ * @since v1.8
+ */
+struct chreBleScanStatus {
+  //! The currently configured report delay in the scan configuration.
+  //! If enabled is false, this value does not have meaning.
+  uint32_t reportDelayMs;
+
+  //! True if the BLE scan is currently enabled. This can be set to false
+  //! if BLE scan was temporarily disabled (e.g. BT subsystem is down,
+  //! or due to user settings).
+  bool enabled;
+
+  //! Reserved for future use - set to zero.
+  uint8_t reserved[3];
+};
+
+/**
+ * Retrieves a set of flags indicating the BLE features supported by the
+ * current CHRE implementation. The value returned by this function must be
+ * consistent for the entire duration of the nanoapp's execution.
+ *
+ * The client must allow for more flags to be set in this response than it knows
+ * about, for example if the implementation supports a newer version of the API
+ * than the client was compiled against.
+ *
+ * @return A bitmask with zero or more CHRE_BLE_CAPABILITIES_* flags set. @see
+ *         CHRE_BLE_CAPABILITIES
+ *
+ * @since v1.6
+ */
+uint32_t chreBleGetCapabilities(void);
+
+/**
+ * Retrieves a set of flags indicating the BLE filtering features supported by
+ * the current CHRE implementation. The value returned by this function must be
+ * consistent for the entire duration of the nanoapp's execution.
+ *
+ * The client must allow for more flags to be set in this response than it knows
+ * about, for example if the implementation supports a newer version of the API
+ * than the client was compiled against.
+ *
+ * @return A bitmask with zero or more CHRE_BLE_FILTER_CAPABILITIES_* flags set.
+ *         @see CHRE_BLE_FILTER_CAPABILITIES
+ *
+ * @since v1.6
+ */
+uint32_t chreBleGetFilterCapabilities(void);
+
+/**
+ * Helper function to extract event type from eventTypeAndDataStatus as defined
+ * in the BT Core Spec v5.2, Vol 4, Part E, Section 7.7.65.13, LE Extended
+ * Advertising Report event, entry Event_Type.
+ *
+ * @see CHRE_BLE_EVENT
+ *
+ * @param eventTypeAndDataStatus Combined event type and data status
+ *
+ * @return The event type portion of eventTypeAndDataStatus
+ */
+static inline uint8_t chreBleGetEventType(uint8_t eventTypeAndDataStatus) {
+  return (eventTypeAndDataStatus & CHRE_BLE_EVENT_MASK_TYPE);
+}
+
+/**
+ * Helper function to extract data status from eventTypeAndDataStatus as defined
+ * in the BT Core Spec v5.2, Vol 4, Part E, Section 7.7.65.13, LE Extended
+ * Advertising Report event, entry Event_Type.
+ *
+ * @see CHRE_BLE_EVENT
+ *
+ * @param eventTypeAndDataStatus Combined event type and data status
+ *
+ * @return The data status portion of eventTypeAndDataStatus
+ */
+static inline uint8_t chreBleGetDataStatus(uint8_t eventTypeAndDataStatus) {
+  return (eventTypeAndDataStatus & CHRE_BLE_EVENT_MASK_DATA_STATUS);
+}
+
+/**
+ * Helper function to to combine an event type with a data status to create
+ * eventTypeAndDataStatus as defined in the BT Core Spec v5.2, Vol 4, Part E,
+ * Section 7.7.65.13, LE Extended Advertising Report event, entry Event_Type.
+ *
+ * @see CHRE_BLE_EVENT
+ *
+ * @param eventType Event type
+ * @param dataStatus Data status
+ *
+ * @return A combined eventTypeAndDataStatus
+ */
+static inline uint8_t chreBleGetEventTypeAndDataStatus(uint8_t eventType,
+                                                       uint8_t dataStatus) {
+  return ((eventType & CHRE_BLE_EVENT_MASK_TYPE) |
+          (dataStatus & CHRE_BLE_EVENT_MASK_DATA_STATUS));
+}
+
+/**
+ * Nanoapps must define CHRE_NANOAPP_USES_BLE somewhere in their build
+ * system (e.g. Makefile) if the nanoapp needs to use the following BLE APIs.
+ * In addition to allowing access to these APIs, defining this macro will also
+ * ensure CHRE enforces that all host clients this nanoapp talks to have the
+ * required Android permissions needed to access BLE functionality by adding
+ * metadata to the nanoapp.
+ */
+#if defined(CHRE_NANOAPP_USES_BLE) || !defined(CHRE_IS_NANOAPP_BUILD)
+
+/**
+ * Start Bluetooth LE (BLE) scanning on CHRE.
+ *
+ * @see chreBleStartScanAsyncV1_9 for further details.
+ *
+ * @deprecated as of v1.9 due to the addition of the chreBleScanFilterV1_9
+ * struct and a cookie parameter. New code should use
+ * chreBleStartScanAsyncV1_9() instead of this function. This function will be
+ * removed in a future version.
+ */
+bool chreBleStartScanAsync(enum chreBleScanMode mode, uint32_t reportDelayMs,
+                           const struct chreBleScanFilter *filter);
+
+/**
+ * Start Bluetooth LE (BLE) scanning on CHRE.
+ *
+ * The result of the operation will be delivered asynchronously via the CHRE
+ * event CHRE_EVENT_BLE_ASYNC_RESULT.
+ *
+ * The scan results will be delivered asynchronously via the CHRE event
+ * CHRE_EVENT_BLE_ADVERTISEMENT.
+ *
+ * If CHRE_USER_SETTING_BLE_AVAILABLE is disabled, CHRE is expected to return an
+ * async result with error CHRE_ERROR_FUNCTION_DISABLED. If this setting is
+ * enabled, the Bluetooth subsystem may still be powered down in the scenario
+ * where the main Bluetooth toggle is disabled, but the Bluetooth scanning
+ * setting is enabled, and there is no request for BLE to be enabled at the
+ * Android level. In this scenario, CHRE will return an async result with error
+ * CHRE_ERROR_FUNCTION_DISABLED.
+ *
+ * To ensure that Bluetooth remains powered on in this settings configuration so
+ * that a nanoapp can scan, the nanoapp's Android host entity should use the
+ * BluetoothAdapter.enableBLE() API to register this request with the Android
+ * Bluetooth stack.
+ *
+ * If chreBleStartScanAsync() is called while a previous scan has been started,
+ * the previous scan will be stopped first and replaced with the new scan.
+ *
+ * Note that some corresponding Android parameters are missing from the CHRE
+ * API, where the following default or typical parameters are used:
+ * Callback type: CALLBACK_TYPE_ALL_MATCHES
+ * Result type: SCAN_RESULT_TYPE_FULL
+ * Match mode: MATCH_MODE_AGGRESSIVE
+ * Number of matches per filter: MATCH_NUM_MAX_ADVERTISEMENT
+ * Legacy-only: false
+ * PHY type: PHY_LE_ALL_SUPPORTED
+ *
+ * A CHRE_EVENT_BLE_SCAN_STATUS_CHANGE will be generated if the values in
+ * chreBleScanStatus changes as a result of this call.
+ *
+ * @param mode Scanning mode selected among enum chreBleScanMode
+ * @param reportDelayMs Maximum requested batching delay in ms. 0 indicates no
+ *                      batching. Note that the system may deliver results
+ *                      before the maximum specified delay is reached.
+ * @param filter Pointer to the requested best-effort filter configuration as
+ *               defined by struct chreBleScanFilter. The ownership of filter
+ *               and its nested elements remains with the caller, and the caller
+ *               may release it as soon as chreBleStartScanAsync() returns.
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *               sent as a response to this request.
+ *
+ * @return True to indicate that the request was accepted. False otherwise.
+ *
+ * @since v1.9
+ */
+bool chreBleStartScanAsyncV1_9(enum chreBleScanMode mode,
+                               uint32_t reportDelayMs,
+                               const struct chreBleScanFilterV1_9 *filter,
+                               const void *cookie);
+
+/**
+ * Stops a CHRE BLE scan.
+ *
+ * @see chreBleStopScanAsyncV1_9 for further details.
+ *
+ * @deprecated as of v1.9 due to the addition of the cookie parameter. New code
+ * should use chreBleStopScanAsyncV1_9() instead of this function. This function
+ * will be removed in a future version.
+ */
+bool chreBleStopScanAsync(void);
+
+/**
+ * Stops a CHRE BLE scan.
+ *
+ * The result of the operation will be delivered asynchronously via the CHRE
+ * event CHRE_EVENT_BLE_ASYNC_RESULT.
+ *
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *               sent as a response to this request.
+ *
+ * @return True to indicate that the request was accepted. False otherwise.
+ *
+ * @since v1.9
+ */
+bool chreBleStopScanAsyncV1_9(const void *cookie);
+
+/**
+ * Requests to immediately deliver batched scan results. The nanoapp must
+ * have an active BLE scan request. If a request is accepted, it will be treated
+ * as though the reportDelayMs has expired for a batched scan. Upon accepting
+ * the request, CHRE works to immediately deliver scan results currently kept in
+ * batching memory, if any, via regular CHRE_EVENT_BLE_ADVERTISEMENT events,
+ * followed by a CHRE_EVENT_BLE_FLUSH_COMPLETE event.
+ *
+ * If the underlying system fails to complete the flush operation within
+ * CHRE_BLE_FLUSH_COMPLETE_TIMEOUT_NS, CHRE will send a
+ * CHRE_EVENT_BLE_FLUSH_COMPLETE event with CHRE_ERROR_TIMEOUT.
+ *
+ * If multiple flush requests are made prior to flush completion, then the
+ * requesting nanoapp will receive all batched samples existing at the time of
+ * the latest flush request. In this case, the number of
+ * CHRE_EVENT_BLE_FLUSH_COMPLETE events received must equal the number of flush
+ * requests made.
+ *
+ * If chreBleStopScanAsync() is called while a flush operation is in progress,
+ * it is unspecified whether the flush operation will complete successfully or
+ * return an error, such as CHRE_ERROR_FUNCTION_DISABLED, but in any case,
+ * CHRE_EVENT_BLE_FLUSH_COMPLETE must still be delivered. The same applies if
+ * the Bluetooth user setting is disabled during a flush operation.
+ *
+ * If called while running on a CHRE API version below v1.7, this function
+ * returns false and has no effect.
+ *
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *               sent as a response to this request.
+ *
+ * @return True to indicate the request was accepted. False otherwise.
+ *
+ * @since v1.7
+ */
+bool chreBleFlushAsync(const void *cookie);
+
+/**
+ * Requests to read the RSSI of a peer device on the given LE connection
+ * handle.
+ *
+ * If the request is accepted, the response will be delivered in a
+ * CHRE_EVENT_BLE_RSSI_READ event with the same cookie.
+ *
+ * The request may be rejected if resources are not available to service the
+ * request (such as if too many outstanding requests already exist). If so, the
+ * client may retry later.
+ *
+ * Note that the connectionHandle is valid only while the connection remains
+ * active. If a peer device disconnects then reconnects, the handle may change.
+ * BluetoothDevice#getConnectionHandle() can be used from the Android framework
+ * to get the latest handle upon reconnection.
+ *
+ * @param connectionHandle
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *               embedded in the response to this request.
+ * @return True if the request has been accepted and dispatched to the
+ *         controller. False otherwise.
+ *
+ * @since v1.8
+ *
+ */
+bool chreBleReadRssiAsync(uint16_t connectionHandle, const void *cookie);
+
+/**
+ * Retrieves the current state of the BLE scan on the platform.
+ *
+ * @param status A non-null pointer to where the scan status will be
+ *               populated.
+ *
+ * @return True if the status was obtained successfully.
+ *
+ * @since v1.8
+ */
+bool chreBleGetScanStatus(struct chreBleScanStatus *status);
+
+/**
+ * Definitions for handling unsupported CHRE BLE scenarios.
+ */
+#else  // defined(CHRE_NANOAPP_USES_BLE) || !defined(CHRE_IS_NANOAPP_BUILD)
+
+#define CHRE_BLE_PERM_ERROR_STRING                                       \
+  "CHRE_NANOAPP_USES_BLE must be defined when building this nanoapp in " \
+  "order to refer to "
+
+#define chreBleStartScanAsync(...) \
+  CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleStartScanAsync")
+
+#define chreBleStopScanAsync(...) \
+  CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleStopScanAsync")
+
+#define chreBleFlushAsync(...) \
+  CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleFlushAsync")
+
+#define chreBleReadRssiAsync(...) \
+  CHRE_BUILD_ERROR(CHRE_BLE_PERM_ERROR_STRING "chreBleReadRssiAsync")
+
+#endif  // defined(CHRE_NANOAPP_USES_BLE) || !defined(CHRE_IS_NANOAPP_BUILD)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CHRE_BLE_H_ */
diff --git a/chre_api/legacy/v1_10/chre/common.h b/chre_api/legacy/v1_10/chre/common.h
new file mode 100644
index 0000000..beecc46
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/common.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_COMMON_H_
+#define _CHRE_COMMON_H_
+
+/**
+ * @file
+ * Definitions shared across multiple CHRE header files
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Mask of the 5 most significant bytes in a 64-bit nanoapp or CHRE platform
+ * identifier, which represents the vendor ID portion of the ID.
+ */
+#define CHRE_VENDOR_ID_MASK  UINT64_C(0xFFFFFFFFFF000000)
+
+/**
+ * Vendor ID "Googl".  Used in nanoapp IDs and CHRE platform IDs developed and
+ * released by Google.
+ */
+#define CHRE_VENDOR_ID_GOOGLE  UINT64_C(0x476F6F676C000000)
+
+/**
+ * Vendor ID "GoogT".  Used for nanoapp IDs associated with testing done by
+ * Google.
+ */
+#define CHRE_VENDOR_ID_GOOGLE_TEST  UINT64_C(0x476F6F6754000000)
+
+/**
+ * Helper macro to mask off all bytes other than the vendor ID (most significant
+ * 5 bytes) in 64-bit nanoapp and CHRE platform identifiers.
+ *
+ * @see chreGetNanoappInfo()
+ * @see chreGetPlatformId()
+ */
+#define CHRE_EXTRACT_VENDOR_ID(id)  ((id) & CHRE_VENDOR_ID_MASK)
+
+/**
+ * Number of nanoseconds in one second, represented as an unsigned 64-bit
+ * integer
+ */
+#define CHRE_NSEC_PER_SEC  UINT64_C(1000000000)
+
+/**
+ * General timeout for asynchronous API requests. Unless specified otherwise, a
+ * function call that returns data asynchronously via an event, such as
+ * CHRE_EVENT_ASYNC_GNSS_RESULT, must do so within this amount of time.
+ */
+#define CHRE_ASYNC_RESULT_TIMEOUT_NS  (5 * CHRE_NSEC_PER_SEC)
+
+/**
+ * A generic listing of error codes for use in {@link #chreAsyncResult} and
+ * elsewhere. In general, module-specific error codes may be added to this enum,
+ * but effort should be made to come up with a generic name that still captures
+ * the meaning of the error.
+ */
+// LINT.IfChange
+enum chreError {
+    //! No error occurred
+    CHRE_ERROR_NONE = 0,
+
+    //! An unspecified failure occurred
+    CHRE_ERROR = 1,
+
+    //! One or more supplied arguments are invalid
+    CHRE_ERROR_INVALID_ARGUMENT = 2,
+
+    //! Unable to satisfy request because the system is busy
+    CHRE_ERROR_BUSY = 3,
+
+    //! Unable to allocate memory
+    CHRE_ERROR_NO_MEMORY = 4,
+
+    //! The requested feature is not supported
+    CHRE_ERROR_NOT_SUPPORTED = 5,
+
+    //! A timeout occurred while processing the request
+    CHRE_ERROR_TIMEOUT = 6,
+
+    //! The relevant capability is disabled, for example due to a user
+    //! configuration that takes precedence over this request
+    CHRE_ERROR_FUNCTION_DISABLED = 7,
+
+    //! The request was rejected due to internal rate limiting of the requested
+    //! functionality - the client may try its request again after waiting an
+    //! unspecified amount of time
+    CHRE_ERROR_REJECTED_RATE_LIMIT = 8,
+
+    //! The requested functionality is not currently accessible from the CHRE,
+    //! because another client, such as the main applications processor, is
+    //! currently controlling it.
+    CHRE_ERROR_FUNCTION_RESTRICTED_TO_OTHER_MASTER = 9,
+    CHRE_ERROR_FUNCTION_RESTRICTED_TO_OTHER_CLIENT = 9,
+
+    //! This request is no longer valid. It may have been replaced by a newer
+    //! request before taking effect.
+    //! @since v1.6
+    CHRE_ERROR_OBSOLETE_REQUEST = 10,
+
+    //! A transient error occurred. The request can be retried.
+    //! @since v1.10
+    CHRE_ERROR_TRANSIENT = 11,
+
+    //! Unable to satisfy request because of missing permissions.
+    //! @since v1.10
+    CHRE_ERROR_PERMISSION_DENIED = 12,
+
+    //! Unable to satisfy request because the destination is not found.
+    //! @since v1.10
+    CHRE_ERROR_DESTINATION_NOT_FOUND = 13,
+
+    //!< Do not exceed this value when adding new error codes
+    CHRE_ERROR_LAST = UINT8_MAX,
+};
+// LINT.ThenChange(../../../../core/include/chre/core/api_manager_common.h)
+
+/**
+ * Generic data structure to indicate the result of an asynchronous operation.
+ *
+ * @note
+ * The general model followed by CHRE for asynchronous operations is that a
+ * request function returns a boolean value that indicates whether the request
+ * was accepted for further processing. The actual result of the operation is
+ * provided in a subsequent event sent with an event type that is defined in the
+ * specific API. Typically, a "cookie" parameter is supplied to allow the client
+ * to tie the response to a specific request, or pass data through, etc. The
+ * response is expected to be delivered within CHRE_ASYNC_RESULT_TIMEOUT_NS if
+ * not specified otherwise.
+ *
+ * The CHRE implementation must allow for multiple asynchronous requests to be
+ * outstanding at a given time, under reasonable resource constraints. Further,
+ * requests must be processed in the same order as supplied by the client of the
+ * API in order to maintain causality. Using GNSS as an example, if a client
+ * calls chreGnssLocationSessionStartAsync() and then immediately calls
+ * chreGnssLocationSessionStopAsync(), the final result must be that the
+ * location session is stopped. Whether requests always complete in the
+ * order that they are given is implementation-defined. For example, if a client
+ * calls chreGnssLocationSessionStart() and then immediately calls
+ * chreGnssMeasurementSessionStart(), it is possible for the
+ * CHRE_EVENT_GNSS_RESULT associated with the measurement session to be
+ * delivered before the one for the location session.
+ */
+struct chreAsyncResult {
+    //! Indicates the request associated with this result. The interpretation of
+    //! values in this field is dependent upon the event type provided when this
+    //! result was delivered.
+    uint8_t requestType;
+
+    //! Set to true if the request was successfully processed
+    bool success;
+
+    //! If the request failed (success is false), this is set to a value from
+    //! enum chreError (other than CHRE_ERROR_NONE), which may provide
+    //! additional information about the nature of the failure.
+    //! @see #chreError
+    uint8_t errorCode;
+
+    //! Reserved for future use, set to 0
+    uint8_t reserved;
+
+    //! Set to the cookie parameter given to the request function tied to this
+    //! result
+    const void *cookie;
+};
+
+/**
+ * A structure to store an event describing the end of batched events.
+ *
+ * @since v1.8
+ */
+struct chreBatchCompleteEvent {
+    //! Indicates the type of event (of type CHRE_EVENT_TYPE_*) that was
+    //! batched.
+    uint16_t eventType;
+
+    //! Reserved for future use, set to 0
+    uint8_t reserved[2];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CHRE_COMMON_H_ */
diff --git a/chre_api/legacy/v1_10/chre/event.h b/chre_api/legacy/v1_10/chre/event.h
new file mode 100644
index 0000000..d08b7d0
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/event.h
@@ -0,0 +1,1044 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_EVENT_H_
+#define _CHRE_EVENT_H_
+
+/**
+ * @file
+ * Context Hub Runtime Environment API dealing with events and messages.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <chre/toolchain.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The CHRE implementation is required to provide the following preprocessor
+ * defines via the build system.
+ *
+ * CHRE_MESSAGE_TO_HOST_MAX_SIZE: The maximum size, in bytes, allowed for
+ *     a message sent to chreSendMessageToHostEndpoint().  This must be at least
+ *     CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE. If the system supports a larger
+ *     maximum size, it will be defined as the return value of
+ *     chreGetMessageToHostMaxSize().
+ */
+#ifndef CHRE_MESSAGE_TO_HOST_MAX_SIZE
+#error CHRE_MESSAGE_TO_HOST_MAX_SIZE must be defined by the CHRE implementation
+#endif
+
+/**
+ * The minimum size, in bytes, any CHRE implementation will use for
+ * CHRE_MESSAGE_TO_HOST_MAX_SIZE is set to 1000 for v1.5+ CHRE implementations,
+ * and 128 for v1.0-v1.4 implementations (previously kept in
+ * CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE, which has been removed).
+ *
+ * All CHRE implementations supporting v1.5+ must support the raised limit of
+ * 1000 bytes, however a nanoapp compiled against v1.5 cannot assume this
+ * limit if there is a possibility their binary will run on a v1.4 or earlier
+ * implementation that had a lower limit. To allow for nanoapp compilation in
+ * these situations, CHRE_MESSAGE_TO_HOST_MAX_SIZE must be set to the minimum
+ * value the nanoapp may encounter, and CHRE_NANOAPP_SUPPORTS_PRE_V1_5 can be
+ * defined to skip the compile-time check.
+ */
+#if (!defined(CHRE_NANOAPP_SUPPORTS_PRE_V1_5) && \
+     CHRE_MESSAGE_TO_HOST_MAX_SIZE < 1000) ||    \
+    (defined(CHRE_NANOAPP_SUPPORTS_PRE_V1_5) &&  \
+     CHRE_MESSAGE_TO_HOST_MAX_SIZE < 128)
+#error CHRE_MESSAGE_TO_HOST_MAX_SIZE is too small.
+#endif
+
+/**
+ * CHRE_MESSAGE_TO_HOST_MAX_SIZE must be less than or equal to 4096. If the system
+ * supports a larger maximum size, it will be defined as the return value of
+ * chreGetMessageToHostMaxSize().
+ */
+#if CHRE_MESSAGE_TO_HOST_MAX_SIZE > 4096
+#error CHRE_MESSAGE_TO_HOST_MAX_SIZE must be <= 4096
+#endif
+
+/**
+ * The lowest numerical value legal for a user-defined event.
+ *
+ * The system reserves all event values from 0 to 0x7FFF, inclusive.
+ * User events may use any value in the range 0x8000 to 0xFFFF, inclusive.
+ *
+ * Note that the same event values might be used by different nanoapps
+ * for different meanings.  This is not a concern, as these values only
+ * have meaning when paired with the originating nanoapp.
+ */
+#define CHRE_EVENT_FIRST_USER_VALUE  UINT16_C(0x8000)
+
+/**
+ * nanoappHandleEvent argument: struct chreMessageFromHostData
+ *
+ * The format of the 'message' part of this structure is left undefined,
+ * and it's up to the nanoapp and host to have an established protocol
+ * beforehand.
+ */
+#define CHRE_EVENT_MESSAGE_FROM_HOST  UINT16_C(0x0001)
+
+/**
+ * nanoappHandleEvent argument: 'cookie' given to chreTimerSet() method.
+ *
+ * Indicates that a timer has elapsed, in accordance with how chreTimerSet() was
+ * invoked.
+ */
+#define CHRE_EVENT_TIMER  UINT16_C(0x0002)
+
+/**
+ * nanoappHandleEvent argument: struct chreNanoappInfo
+ *
+ * Indicates that a nanoapp has successfully started (its nanoappStart()
+ * function has been called, and it returned true) and is able to receive events
+ * sent via chreSendEvent().  Note that this event is not sent for nanoapps that
+ * were started prior to the current nanoapp - use chreGetNanoappInfo() to
+ * determine if another nanoapp is already running.
+ *
+ * @see chreConfigureNanoappInfoEvents
+ * @since v1.1
+ */
+#define CHRE_EVENT_NANOAPP_STARTED  UINT16_C(0x0003)
+
+/**
+ * nanoappHandleEvent argument: struct chreNanoappInfo
+ *
+ * Indicates that a nanoapp has stopped executing and is no longer able to
+ * receive events sent via chreSendEvent().  Any events sent prior to receiving
+ * this event are not guaranteed to have been delivered.
+ *
+ * @see chreConfigureNanoappInfoEvents
+ * @since v1.1
+ */
+#define CHRE_EVENT_NANOAPP_STOPPED  UINT16_C(0x0004)
+
+/**
+ * nanoappHandleEvent argument: NULL
+ *
+ * Indicates that CHRE has observed the host wake from low-power sleep state.
+ *
+ * @see chreConfigureHostSleepStateEvents
+ * @since v1.2
+ */
+#define CHRE_EVENT_HOST_AWAKE  UINT16_C(0x0005)
+
+/**
+ * nanoappHandleEvent argument: NULL
+ *
+ * Indicates that CHRE has observed the host enter low-power sleep state.
+ *
+ * @see chreConfigureHostSleepStateEvents
+ * @since v1.2
+ */
+#define CHRE_EVENT_HOST_ASLEEP  UINT16_C(0x0006)
+
+/**
+ * nanoappHandleEvent argument: NULL
+ *
+ * Indicates that CHRE is collecting debug dumps. Nanoapps can call
+ * chreDebugDumpLog() to log their debug data while handling this event.
+ *
+ * @see chreConfigureDebugDumpEvent
+ * @see chreDebugDumpLog
+ * @since v1.4
+ */
+#define CHRE_EVENT_DEBUG_DUMP  UINT16_C(0x0007)
+
+/**
+ * nanoappHandleEvent argument: struct chreHostEndpointNotification
+ *
+ * Notifications event regarding a host endpoint.
+ *
+ * @see chreConfigureHostEndpointNotifications
+ * @since v1.6
+ */
+#define CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION UINT16_C(0x0008)
+
+/**
+ * Indicates a RPC request from a nanoapp.
+ *
+ * @since v1.9
+ */
+#define CHRE_EVENT_RPC_REQUEST UINT16_C(0x00009)
+
+/**
+ * Indicates a RPC response from a nanoapp.
+ *
+ * @since v1.9
+ */
+#define CHRE_EVENT_RPC_RESPONSE UINT16_C(0x0000A)
+
+/**
+ * nanoappHandleEvent argument: struct chreAsyncResult
+ *
+ * Async status for reliable messages. The resultType field
+ * will be populated with a value of 0.
+ *
+ * @see chreSendReliableMessageAsync
+ * @since v1.10
+ */
+#define CHRE_EVENT_RELIABLE_MSG_ASYNC_RESULT UINT16_C(0x000B)
+
+/**
+ * First possible value for CHRE_EVENT_SENSOR events.
+ *
+ * This allows us to separately define our CHRE_EVENT_SENSOR_* events in
+ * chre/sensor.h, without fear of collision with other event values.
+ */
+#define CHRE_EVENT_SENSOR_FIRST_EVENT  UINT16_C(0x0100)
+
+/**
+ * Last possible value for CHRE_EVENT_SENSOR events.
+ *
+ * This allows us to separately define our CHRE_EVENT_SENSOR_* events in
+ * chre/sensor.h, without fear of collision with other event values.
+ */
+#define CHRE_EVENT_SENSOR_LAST_EVENT  UINT16_C(0x02FF)
+
+/**
+ * First event in the block reserved for GNSS. These events are defined in
+ * chre/gnss.h.
+ */
+#define CHRE_EVENT_GNSS_FIRST_EVENT  UINT16_C(0x0300)
+#define CHRE_EVENT_GNSS_LAST_EVENT   UINT16_C(0x030F)
+
+/**
+ * First event in the block reserved for WiFi. These events are defined in
+ * chre/wifi.h.
+ */
+#define CHRE_EVENT_WIFI_FIRST_EVENT  UINT16_C(0x0310)
+#define CHRE_EVENT_WIFI_LAST_EVENT   UINT16_C(0x031F)
+
+/**
+ * First event in the block reserved for WWAN. These events are defined in
+ * chre/wwan.h.
+ */
+#define CHRE_EVENT_WWAN_FIRST_EVENT  UINT16_C(0x0320)
+#define CHRE_EVENT_WWAN_LAST_EVENT   UINT16_C(0x032F)
+
+/**
+ * First event in the block reserved for audio. These events are defined in
+ * chre/audio.h.
+ */
+#define CHRE_EVENT_AUDIO_FIRST_EVENT UINT16_C(0x0330)
+#define CHRE_EVENT_AUDIO_LAST_EVENT  UINT16_C(0x033F)
+
+/**
+ * First event in the block reserved for settings changed notifications.
+ * These events are defined in chre/user_settings.h
+ *
+ * @since v1.5
+ */
+#define CHRE_EVENT_SETTING_CHANGED_FIRST_EVENT UINT16_C(0x340)
+#define CHRE_EVENT_SETTING_CHANGED_LAST_EVENT  UINT16_C(0x34F)
+
+/**
+ * First event in the block reserved for Bluetooth LE. These events are defined
+ * in chre/ble.h.
+ */
+#define CHRE_EVENT_BLE_FIRST_EVENT UINT16_C(0x0350)
+#define CHRE_EVENT_BLE_LAST_EVENT  UINT16_C(0x035F)
+
+/**
+ * First in the extended range of values dedicated for internal CHRE
+ * implementation usage.
+ *
+ * This range is semantically the same as the internal event range defined
+ * below, but has been extended to allow for more implementation-specific events
+ * to be used.
+ *
+ * @since v1.1
+ */
+#define CHRE_EVENT_INTERNAL_EXTENDED_FIRST_EVENT  UINT16_C(0x7000)
+
+/**
+ * First in a range of values dedicated for internal CHRE implementation usage.
+ *
+ * If a CHRE wishes to use events internally, any values within this range
+ * are assured not to be taken by future CHRE API additions.
+ */
+#define CHRE_EVENT_INTERNAL_FIRST_EVENT  UINT16_C(0x7E00)
+
+/**
+ * Last in a range of values dedicated for internal CHRE implementation usage.
+ *
+ * If a CHRE wishes to use events internally, any values within this range
+ * are assured not to be taken by future CHRE API additions.
+ */
+#define CHRE_EVENT_INTERNAL_LAST_EVENT  UINT16_C(0x7FFF)
+
+/**
+ * A special value for the hostEndpoint argument in
+ * chreSendMessageToHostEndpoint() that indicates that the message should be
+ * delivered to all host endpoints.  This value will not be used in the
+ * hostEndpoint field of struct chreMessageFromHostData supplied with
+ * CHRE_EVENT_MESSAGE_FROM_HOST.
+ *
+ * @since v1.1
+ */
+#define CHRE_HOST_ENDPOINT_BROADCAST  UINT16_C(0xFFFF)
+
+/**
+ * A special value for hostEndpoint in struct chreMessageFromHostData that
+ * indicates that a host endpoint is unknown or otherwise unspecified.  This
+ * value may be received in CHRE_EVENT_MESSAGE_FROM_HOST, but it is not valid to
+ * provide it to chreSendMessageToHostEndpoint().
+ *
+ * @since v1.1
+ */
+#define CHRE_HOST_ENDPOINT_UNSPECIFIED  UINT16_C(0xFFFE)
+
+/**
+ * Bitmask values that can be given as input to the messagePermissions parameter
+ * of chreSendMessageWithPermissions(). These values are typically used by
+ * nanoapps when they used data from the corresponding CHRE APIs to produce the
+ * message contents being sent and is used to attribute permissions usage on
+ * the Android side. See chreSendMessageWithPermissions() for more details on
+ * how these values are used when sending a message.
+ *
+ * Values in the range
+ * [CHRE_MESSAGE_PERMISSION_VENDOR_START, CHRE_MESSAGE_PERMISSION_VENDOR_END]
+ * are reserved for vendors to use when adding support for permission-gated APIs
+ * in their implementations.
+ *
+ * On the Android side, CHRE permissions are mapped as follows:
+ * - CHRE_MESSAGE_PERMISSION_AUDIO: android.permission.RECORD_AUDIO
+ * - CHRE_MESSAGE_PERMISSION_GNSS, CHRE_MESSAGE_PERMISSION_WIFI, and
+ *   CHRE_MESSAGE_PERMISSION_WWAN: android.permission.ACCESS_FINE_LOCATION, and
+ *   android.permissions.ACCESS_BACKGROUND_LOCATION
+ *
+ * @since v1.5
+ *
+ * @defgroup CHRE_MESSAGE_PERMISSION
+ * @{
+ */
+
+#define CHRE_MESSAGE_PERMISSION_NONE UINT32_C(0)
+#define CHRE_MESSAGE_PERMISSION_AUDIO UINT32_C(1)
+#define CHRE_MESSAGE_PERMISSION_GNSS (UINT32_C(1) << 1)
+#define CHRE_MESSAGE_PERMISSION_WIFI (UINT32_C(1) << 2)
+#define CHRE_MESSAGE_PERMISSION_WWAN (UINT32_C(1) << 3)
+#define CHRE_MESSAGE_PERMISSION_BLE (UINT32_C(1) << 4)
+#define CHRE_MESSAGE_PERMISSION_VENDOR_START (UINT32_C(1) << 24)
+#define CHRE_MESSAGE_PERMISSION_VENDOR_END (UINT32_C(1) << 31)
+
+/** @} */
+
+/**
+ * Reserved message type for RPC messages.
+ *
+ * @see chreSendMessageWithPermissions
+ *
+ * @since v1.9
+ */
+#define CHRE_MESSAGE_TYPE_RPC UINT32_C(0x7FFFFFF5)
+
+/**
+ * @see chrePublishRpcServices
+ *
+ * @since v1.8
+ */
+#define CHRE_MINIMUM_RPC_SERVICE_LIMIT UINT8_C(4)
+
+/**
+ * Data provided with CHRE_EVENT_MESSAGE_FROM_HOST.
+ */
+struct chreMessageFromHostData {
+    /**
+     * Message type supplied by the host.
+     *
+     * @note In CHRE API v1.0, support for forwarding this field from the host
+     * was not strictly required, and some implementations did not support it.
+     * However, its support is mandatory as of v1.1.
+     */
+    union {
+        /**
+         * The preferred name to use when referencing this field.
+         *
+         * @since v1.1
+         */
+        uint32_t messageType;
+
+        /**
+         * @deprecated This is the name for the messageType field used in v1.0.
+         * Left to allow code to compile against both v1.0 and v1.1 of the API
+         * definition without needing to use #ifdefs. This will be removed in a
+         * future API update - use messageType instead.
+         */
+        uint32_t reservedMessageType;
+    };
+
+    /**
+     * The size, in bytes of the following 'message'.
+     *
+     * This can be 0.
+     */
+    uint32_t messageSize;
+
+    /**
+     * The message from the host.
+     *
+     * These contents are of a format that the host and nanoapp must have
+     * established beforehand.
+     *
+     * This data is 'messageSize' bytes in length.  Note that if 'messageSize'
+     * is 0, this might be NULL.
+     */
+    const void *message;
+
+    /**
+     * An identifier for the host-side entity that sent this message.  Unless
+     * this is set to CHRE_HOST_ENDPOINT_UNSPECIFIED, it can be used in
+     * chreSendMessageToHostEndpoint() to send a directed reply that will only
+     * be received by the given entity on the host.  Endpoint identifiers are
+     * opaque values assigned at runtime, so they cannot be assumed to always
+     * describe a specific entity across restarts.
+     *
+     * If running on a CHRE API v1.0 implementation, this field will always be
+     * set to CHRE_HOST_ENDPOINT_UNSPECIFIED.
+     *
+     * @since v1.1
+     */
+    uint16_t hostEndpoint;
+};
+
+/**
+ * Provides metadata for a nanoapp in the system.
+ */
+struct chreNanoappInfo {
+    /**
+     * Nanoapp identifier. The convention for populating this value is to set
+     * the most significant 5 bytes to a value that uniquely identifies the
+     * vendor, and the lower 3 bytes identify the nanoapp.
+     */
+    uint64_t appId;
+
+    /**
+     * Nanoapp version.  The semantics of this field are defined by the nanoapp,
+     * however nanoapps are recommended to follow the same scheme used for the
+     * CHRE version exposed in chreGetVersion().  That is, the most significant
+     * byte represents the major version, the next byte the minor version, and
+     * the lower two bytes the patch version.
+     */
+    uint32_t version;
+
+    /**
+     * The instance ID of this nanoapp, which can be used in chreSendEvent() to
+     * address an event specifically to this nanoapp.  This identifier is
+     * guaranteed to be unique among all nanoapps in the system.
+     *
+     * As of CHRE API v1.6, instance ID is guaranteed to never be greater than
+     * UINT16_MAX. This allows for the instance ID be packed with other data
+     * inside a 32-bit integer (useful for RPC routing).
+     */
+    uint32_t instanceId;
+
+    /**
+     * Reserved for future use.
+     * Always set to 0.
+     */
+    uint8_t reserved[3];
+
+    /**
+     * The number of RPC services exposed by this nanoapp.
+     * The service details are available in the rpcServices array.
+     * Must always be set to 0 when running on a CHRE implementation prior to
+     * v1.8
+     *
+     * @since v1.8
+     */
+    uint8_t rpcServiceCount;
+
+    /*
+     * Array of RPC services published by this nanoapp.
+     * Services are published via chrePublishRpcServices.
+     * The array contains rpcServiceCount entries.
+     *
+     * The pointer is only valid when rpcServiceCount is greater than 0.
+     *
+     * @since v1.8
+     */
+    const struct chreNanoappRpcService *rpcServices;
+};
+
+/**
+ * The types of notification events that can be included in struct
+ * chreHostEndpointNotification.
+ *
+ * @defgroup HOST_ENDPOINT_NOTIFICATION_TYPE
+ * @{
+ */
+#define HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT UINT8_C(0)
+/** @} */
+
+/**
+ * Data provided in CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION.
+ */
+struct chreHostEndpointNotification {
+    /**
+     * The ID of the host endpoint that this notification is for.
+     */
+    uint16_t hostEndpointId;
+
+    /**
+     * The type of notification this event represents, which should be
+     * one of the HOST_ENDPOINT_NOTIFICATION_TYPE_* values.
+     */
+    uint8_t notificationType;
+
+    /**
+     * Reserved for future use, must be zero.
+     */
+    uint8_t reserved;
+};
+
+//! The maximum length of a host endpoint's name.
+#define CHRE_MAX_ENDPOINT_NAME_LEN (51)
+
+//! The maximum length of a host endpoint's tag.
+#define CHRE_MAX_ENDPOINT_TAG_LEN (51)
+
+/**
+ * The type of host endpoint that can be used in the hostEndpointType field
+ * of chreHostEndpointInfo.
+ *
+ * @since v1.6
+ *
+ * @defgroup CHRE_HOST_ENDPOINT_TYPE_
+ * @{
+ */
+
+//! The host endpoint is part of the Android system framework.
+#define CHRE_HOST_ENDPOINT_TYPE_FRAMEWORK UINT8_C(0x00)
+
+//! The host endpoint is an Android app.
+#define CHRE_HOST_ENDPOINT_TYPE_APP UINT8_C(0x01)
+
+//! The host endpoint is an Android native program.
+#define CHRE_HOST_ENDPOINT_TYPE_NATIVE UINT8_C(0x02)
+
+//! Values in the range [CHRE_HOST_ENDPOINT_TYPE_VENDOR_START,
+//! CHRE_HOST_ENDPOINT_TYPE_VENDOR_END] can be a custom defined host endpoint
+//! type for platform-specific vendor use.
+#define CHRE_HOST_ENDPOINT_TYPE_VENDOR_START UINT8_C(0x80)
+#define CHRE_HOST_ENDPOINT_TYPE_VENDOR_END UINT8_C(0xFF)
+
+/** @} */
+
+/**
+ * Provides metadata for a host endpoint.
+ *
+ * @since v1.6
+ */
+struct chreHostEndpointInfo {
+    //! The endpoint ID of this host.
+    uint16_t hostEndpointId;
+
+    //! The type of host endpoint, which must be set to one of the
+    //! CHRE_HOST_ENDPOINT_TYPE_* values or a value in the vendor-reserved
+    //! range.
+    uint8_t hostEndpointType;
+
+    //! Flag indicating if the packageName/endpointName field is valid.
+    uint8_t isNameValid : 1;
+
+    //! Flag indicating if the attributionTag/endpointTag field is valid.
+    uint8_t isTagValid : 1;
+
+    //! A union of null-terminated host name strings.
+    union {
+        //! The Android package name associated with this host, valid if the
+        //! hostEndpointType is CHRE_HOST_ENDPOINT_TYPE_APP or
+        //! CHRE_HOST_ENDPOINT_TYPE_FRAMEWORK. Refer to the Android documentation
+        //! for the package attribute in the app manifest.
+        char packageName[CHRE_MAX_ENDPOINT_NAME_LEN];
+
+        //! A generic endpoint name that can be used for endpoints that
+        //! may not have a package name.
+        char endpointName[CHRE_MAX_ENDPOINT_NAME_LEN];
+    };
+
+    //! A union of null-terminated host tag strings for further identification.
+    union {
+        //! The attribution tag associated with this host that is used to audit
+        //! access to data, which can be valid if the hostEndpointType is
+        //! CHRE_HOST_ENDPOINT_TYPE_APP. Refer to the Android documentation
+        //! regarding data audit using attribution tags.
+        char attributionTag[CHRE_MAX_ENDPOINT_TAG_LEN];
+
+        //! A generic endpoint tag that can be used for endpoints that
+        //! may not have an attribution tag.
+        char endpointTag[CHRE_MAX_ENDPOINT_TAG_LEN];
+    };
+};
+
+/**
+ * An RPC service exposed by a nanoapp.
+ *
+ * The implementation of the RPC interface is not defined by the HAL, and is written
+ * at the messaging endpoint layers (Android app and/or CHRE nanoapp). NanoappRpcService
+ * contains the informational metadata to be consumed by the RPC interface layer.
+ */
+struct chreNanoappRpcService {
+    /**
+     * The unique 64-bit ID of an RPC service exposed by a nanoapp. Note that
+     * the uniqueness is only required within the nanoapp's domain (i.e. the
+     * combination of the nanoapp ID and service id must be unique).
+     */
+    uint64_t id;
+
+    /**
+     * The software version of this service, which follows the sematic
+     * versioning scheme (see semver.org). It follows the format
+     * major.minor.patch, where major and minor versions take up one byte
+     * each, and the patch version takes up the final 2 bytes.
+     */
+    uint32_t version;
+};
+
+/**
+ * Callback which frees data associated with an event.
+ *
+ * This callback is (optionally) provided to the chreSendEvent() method as
+ * a means for freeing the event data and performing any other cleanup
+ * necessary when the event is completed.  When this callback is invoked,
+ * 'eventData' is no longer needed and can be released.
+ *
+ * @param eventType  The 'eventType' argument from chreSendEvent().
+ * @param eventData  The 'eventData' argument from chreSendEvent().
+ *
+ * @see chreSendEvent
+ */
+typedef void (chreEventCompleteFunction)(uint16_t eventType, void *eventData);
+
+/**
+ * Callback which frees a message.
+ *
+ * This callback is (optionally) provided to the chreSendMessageToHostEndpoint()
+ * method as a means for freeing the message.  When this callback is invoked,
+ * 'message' is no longer needed and can be released.  Note that this in
+ * no way assures that said message did or did not make it to the host, simply
+ * that this memory is no longer needed.
+ *
+ * @param message  The 'message' argument from chreSendMessageToHostEndpoint().
+ * @param messageSize  The 'messageSize' argument from
+ *     chreSendMessageToHostEndpoint().
+ *
+ * @see chreSendMessageToHostEndpoint
+ */
+typedef void (chreMessageFreeFunction)(void *message, size_t messageSize);
+
+
+/**
+ * Enqueue an event to be sent to another nanoapp.
+ *
+ * @param eventType  This is a user-defined event type, of at least the
+ *     value CHRE_EVENT_FIRST_USER_VALUE.  It is illegal to attempt to use any
+ *     of the CHRE_EVENT_* values reserved for the CHRE.
+ * @param eventData  A pointer value that will be understood by the receiving
+ *     app.  Note that NULL is perfectly acceptable.  It also is not required
+ *     that this be a valid pointer, although if this nanoapp is intended to
+ *     work on arbitrary CHRE implementations, then the size of a
+ *     pointer cannot be assumed to be a certain size.  Note that the caller
+ *     no longer owns this memory after the call.
+ * @param freeCallback  A pointer to a callback function.  After the lifetime
+ *     of 'eventData' is over (either through successful delivery or the event
+ *     being dropped), this callback will be invoked.  This argument is allowed
+ *     to be NULL, in which case no callback will be invoked.
+ * @param targetInstanceId  The ID of the instance we're delivering this event
+ *     to.  Note that this is allowed to be our own instance.  The instance ID
+ *     of a nanoapp can be retrieved by using chreGetNanoappInfoByInstanceId().
+ * @return true if the event was enqueued, false otherwise.  Note that even
+ *     if this method returns 'false', the 'freeCallback' will be invoked,
+ *     if non-NULL.  Note in the 'false' case, the 'freeCallback' may be
+ *     invoked directly from within chreSendEvent(), so it's necessary
+ *     for nanoapp authors to avoid possible recursion with this.
+ *
+ * @see chreEventDataFreeFunction
+ */
+bool chreSendEvent(uint16_t eventType, void *eventData,
+                   chreEventCompleteFunction *freeCallback,
+                   uint32_t targetInstanceId);
+
+/**
+ * Send a message to the host, using the broadcast endpoint
+ * CHRE_HOST_ENDPOINT_BROADCAST.  Refer to chreSendMessageToHostEndpoint() for
+ * further details.
+ *
+ * @see chreSendMessageToHostEndpoint
+ *
+ * @deprecated New code should use chreSendMessageToHostEndpoint() instead of
+ * this function.  A future update to the API may cause references to this
+ * function to produce a compiler warning.
+ */
+bool chreSendMessageToHost(void *message, uint32_t messageSize,
+                           uint32_t messageType,
+                           chreMessageFreeFunction *freeCallback)
+    CHRE_DEPRECATED("Use chreSendMessageToHostEndpoint instead");
+
+/**
+ * Send a message to the host, using CHRE_MESSAGE_PERMISSION_NONE for the
+ * associated message permissions. This method must only be used if no data
+ * provided by CHRE's audio, GNSS, WiFi, and WWAN APIs was used to produce the
+ * contents of the message being sent. Refer to chreSendMessageWithPermissions()
+ * for further details.
+ *
+ * @see chreSendMessageWithPermissions
+ *
+ * @since v1.1
+ */
+bool chreSendMessageToHostEndpoint(void *message, size_t messageSize,
+                                   uint32_t messageType, uint16_t hostEndpoint,
+                                   chreMessageFreeFunction *freeCallback);
+
+/**
+ * Send a message to the host, waking it up if it is currently asleep.
+ *
+ * This message is by definition arbitrarily defined.  Since we're not
+ * just a passing a pointer to memory around the system, but need to copy
+ * this into various buffers to send it to the host, the CHRE
+ * implementation cannot be asked to support an arbitrarily large message
+ * size.  As a result, we have the CHRE implementation define
+ * CHRE_MESSAGE_TO_HOST_MAX_SIZE.
+ *
+ * CHRE_MESSAGE_TO_HOST_MAX_SIZE is not given a value by the Platform API.  The
+ * Platform API does define CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE, and requires
+ * that CHRE_MESSAGE_TO_HOST_MAX_SIZE is at least that value.
+ *
+ * As a result, if your message sizes are all less than
+ * CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE, then you have no concerns on any
+ * CHRE implementation.  If your message sizes are larger, you'll need to
+ * come up with a strategy for splitting your message across several calls
+ * to this method.  As long as that strategy works for
+ * CHRE_MESSAGE_TO_HOST_MINIMUM_MAX_SIZE, it will work across all CHRE
+ * implementations (although on some implementations less calls to this
+ * method may be necessary).
+ *
+ * When sending a message to the host, the ContextHub service will enforce
+ * the host client has been granted Android-level permissions corresponding to
+ * the ones the nanoapp declares it uses through CHRE_NANOAPP_USES_AUDIO, etc.
+ * In addition to this, the permissions bitmask provided as input to this method
+ * results in the Android framework using app-ops to verify and log access upon
+ * message delivery to an application. This is primarily useful for ensuring
+ * accurate attribution for messages generated using permission-controlled data.
+ * The bitmask declared by the nanoapp for this message must be a
+ * subset of the permissions it declared it would use at build time or the
+ * message will be rejected.
+ *
+ * Nanoapps must use this method if the data they are sending contains or was
+ * derived from any data sampled through CHRE's audio, GNSS, WiFi, or WWAN APIs.
+ * Additionally, if vendors add APIs to expose data that would be guarded by a
+ * permission in Android, vendors must support declaring a message permission
+ * through this method.
+ *
+ * @param message  Pointer to a block of memory to send to the host.
+ *     NULL is acceptable only if messageSize is 0.  If non-NULL, this
+ *     must be a legitimate pointer (that is, unlike chreSendEvent(), a small
+ *     integral value cannot be cast to a pointer for this).  Note that the
+ *     caller no longer owns this memory after the call.
+ * @param messageSize  The size, in bytes, of the given message. If this exceeds
+ *     CHRE_MESSAGE_TO_HOST_MAX_SIZE, the message will be rejected.
+ * @param messageType  Message type sent to the app on the host.
+ *     NOTE: In CHRE API v1.0, support for forwarding this field to the host was
+ *     not strictly required, and some implementations did not support it.
+ *     However, its support is mandatory as of v1.1.
+ *     NOTE: The value CHRE_MESSAGE_TYPE_RPC is reserved for usage by RPC
+ *     libraries and normally should not be directly used by nanoapps.
+ * @param hostEndpoint  An identifier for the intended recipient of the message,
+ *     or CHRE_HOST_ENDPOINT_BROADCAST if all registered endpoints on the host
+ *     should receive the message.  Endpoint identifiers are assigned on the
+ *     host side, and nanoapps may learn of the host endpoint ID of an intended
+ *     recipient via an initial message sent by the host.  This parameter is
+ *     always treated as CHRE_HOST_ENDPOINT_BROADCAST if running on a CHRE API
+ *     v1.0 implementation. CHRE_HOST_ENDPOINT_BROADCAST isn't allowed to be
+ *     specified if anything other than CHRE_MESSAGE_PERMISSION_NONE is given
+ *     as messagePermissions since doing so would potentially attribute
+ *     permissions usage to host clients that don't intend to consume the data.
+ * @param messagePermissions Bitmasked CHRE_MESSAGE_PERMISSION_ values that will
+ *     be converted to corresponding Android-level permissions and attributed
+ *     the host endpoint upon consumption of the message.
+ * @param freeCallback  A pointer to a callback function.  After the lifetime
+ *     of 'message' is over (which does not assure that 'message' made it to
+ *     the host, just that the transport layer no longer needs this memory),
+ *     this callback will be invoked.  This argument is allowed
+ *     to be NULL, in which case no callback will be invoked.
+ * @return true if the message was accepted for transmission, false otherwise.
+ *     Note that even if this method returns 'false', the 'freeCallback' will
+ *     be invoked, if non-NULL.  In either case, the 'freeCallback' may be
+ *     invoked directly from within chreSendMessageToHostEndpoint(), so it's
+ *     necessary for nanoapp authors to avoid possible recursion with this.
+ *
+ * @see chreMessageFreeFunction
+ *
+ * @since v1.5
+ */
+bool chreSendMessageWithPermissions(void *message, size_t messageSize,
+                                    uint32_t messageType, uint16_t hostEndpoint,
+                                    uint32_t messagePermissions,
+                                    chreMessageFreeFunction *freeCallback);
+
+/**
+ * Send a reliable message to the host.
+ *
+ * A reliable message is similar to a message sent by
+ * chreSendMessageWithPermissions() with the difference that the host
+ * acknowledges the message by sending a status back to the nanoapp, and the
+ * CHRE implementation takes care of retries to help mitigate transient
+ * failures. The final result of attempting to deliver the message is given
+ * via a CHRE_EVENT_RELIABLE_MSG_ASYNC_RESULT event. The maximum time until the
+ * nanoapp will receive the result is CHRE_ASYNC_RESULT_TIMEOUT_NS.
+ *
+ * The free callback is invoked before the async status is delivered to the
+ * nanoapp via the CHRE_EVENT_RELIABLE_MSG_ASYNC_RESULT event and does not
+ * indicate successful delivery of the message.
+ *
+ * The API is similar to chreSendMessageWithPermissions() with a few
+ * differences:
+ * - chreSendReliableMessageAsync() takes an extra cookie that is part of the
+ *   async result
+ * - When the message is accepted for transmission (the function returns true)
+ *   then an async status is delivered to the nanoapp when the transmission
+ *   completes either successfully or in error via the
+ *   CHRE_EVENT_RELIABLE_MSG_ASYNC_RESULT event.
+ * - For any reliable messages pending completion at nanoapp unload:
+ *   - At least one delivery attempt will be made.
+ *   - The free callback will be invoked.
+ *   - The async result event will not be delivered.
+ * - The error codes received are:
+ *   - CHRE_ERROR_DESTINATION_NOT_FOUND if the destination was not found.
+ *   - CHRE_ERROR if there was a permanent error.
+ *   - CHRE_ERROR_TIMEOUT if there was no response from the recipient
+ *                        (a timeout).
+ *
+ * This is an optional feature, and this function will always return
+ * false if CHRE_CAPABILITIES_RELIABLE_MESSAGES is not indicated by
+ * chreGetCapabilities().
+ *
+ * @see chreSendMessageWithPermissions
+ *
+ * @since v1.10
+ */
+bool chreSendReliableMessageAsync(void *message, size_t messageSize,
+                                  uint32_t messageType, uint16_t hostEndpoint,
+                                  uint32_t messagePermissions,
+                                  chreMessageFreeFunction *freeCallback,
+                                  const void *cookie);
+
+/**
+ * Queries for information about a nanoapp running in the system.
+ *
+ * In the current API, appId is required to be unique, i.e. there cannot be two
+ * nanoapps running concurrently with the same appId.  If this restriction is
+ * removed in a future API version and multiple instances of the same appId are
+ * present, this function must always return the first app to start.
+ *
+ * @param appId Identifier for the nanoapp that the caller is requesting
+ *     information about.
+ * @param info Output parameter.  If this function returns true, this structure
+ *     will be populated with details of the specified nanoapp.
+ * @return true if a nanoapp with the given ID is currently running, and the
+ *     supplied info parameter was populated with its information.
+ *
+ * @since v1.1
+ */
+bool chreGetNanoappInfoByAppId(uint64_t appId, struct chreNanoappInfo *info);
+
+/**
+ * Queries for information about a nanoapp running in the system, using the
+ * runtime unique identifier.  This method can be used to get information about
+ * the sender of an event.
+ *
+ * @param instanceId
+ * @param info Output parameter.  If this function returns true, this structure
+ *     will be populated with details of the specified nanoapp.
+ * @return true if a nanoapp with the given instance ID is currently running,
+ *     and the supplied info parameter was populated with its information.
+ *
+ * @since v1.1
+ */
+bool chreGetNanoappInfoByInstanceId(uint32_t instanceId,
+                                    struct chreNanoappInfo *info);
+
+/**
+ * Configures whether this nanoapp will be notified when other nanoapps in the
+ * system start and stop, via CHRE_EVENT_NANOAPP_STARTED and
+ * CHRE_EVENT_NANOAPP_STOPPED.  These events are disabled by default, and if a
+ * nanoapp is not interested in interacting with other nanoapps, then it does
+ * not need to register for them.  However, if inter-nanoapp communication is
+ * desired, nanoapps are recommended to call this function from nanoappStart().
+ *
+ * If running on a CHRE platform that only supports v1.0 of the CHRE API, this
+ * function has no effect.
+ *
+ * @param enable true to enable these events, false to disable
+ *
+ * @see CHRE_EVENT_NANOAPP_STARTED
+ * @see CHRE_EVENT_NANOAPP_STOPPED
+ *
+ * @since v1.1
+ */
+void chreConfigureNanoappInfoEvents(bool enable);
+
+/**
+ * Configures whether this nanoapp will be notified when the host (applications
+ * processor) transitions between wake and sleep, via CHRE_EVENT_HOST_AWAKE and
+ * CHRE_EVENT_HOST_ASLEEP.  As chreSendMessageToHostEndpoint() wakes the host if
+ * it is asleep, these events can be used to opportunistically send data to the
+ * host only when it wakes up for some other reason.  Note that this event is
+ * not instantaneous - there is an inherent delay in CHRE observing power state
+ * changes of the host processor, which may be significant depending on the
+ * implementation, especially in the wake to sleep direction.  Therefore,
+ * nanoapps are not guaranteed that messages sent to the host between AWAKE and
+ * ASLEEP events will not trigger a host wakeup.  However, implementations must
+ * ensure that the nominal wake-up notification latency is strictly less than
+ * the minimum wake-sleep time of the host processor.  Implementations are also
+ * encouraged to minimize this and related latencies where possible, to avoid
+ * unnecessary host wake-ups.
+ *
+ * These events are only sent on transitions, so the initial state will not be
+ * sent to the nanoapp as an event - use chreIsHostAwake().
+ *
+ * @param enable true to enable these events, false to disable
+ *
+ * @see CHRE_EVENT_HOST_AWAKE
+ * @see CHRE_EVENT_HOST_ASLEEP
+ *
+ * @since v1.2
+ */
+void chreConfigureHostSleepStateEvents(bool enable);
+
+/**
+ * Retrieves the current sleep/wake state of the host (applications processor).
+ * Note that, as with the CHRE_EVENT_HOST_AWAKE and CHRE_EVENT_HOST_ASLEEP
+ * events, there is no guarantee that CHRE's view of the host processor's sleep
+ * state is instantaneous, and it may also change between querying the state and
+ * performing a host-waking action like sending a message to the host.
+ *
+ * @return true if by CHRE's own estimation the host is currently awake,
+ *     false otherwise
+ *
+ * @since v1.2
+ */
+bool chreIsHostAwake(void);
+
+/**
+ * Configures whether this nanoapp will be notified when CHRE is collecting
+ * debug dumps, via CHRE_EVENT_DEBUG_DUMP. This event is disabled by default,
+ * and if a nanoapp is not interested in logging its debug data, then it does
+ * not need to register for it.
+ *
+ * @param enable true to enable receipt of this event, false to disable.
+ *
+ * @see CHRE_EVENT_DEBUG_DUMP
+ * @see chreDebugDumpLog
+ *
+ * @since v1.4
+ */
+void chreConfigureDebugDumpEvent(bool enable);
+
+/**
+ * Configures whether this nanoapp will receive updates regarding a host
+ * endpoint that is connected with the Context Hub.
+ *
+ * If this API succeeds, the nanoapp will receive disconnection notifications,
+ * via the CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION event with an eventData of type
+ * chreHostEndpointNotification with its notificationType set to
+ * HOST_ENDPOINT_NOTIFICATION_TYPE_DISCONNECT, which can be invoked if the host
+ * has disconnected from the Context Hub either explicitly or implicitly (e.g.
+ * crashes). Nanoapps can use this notifications to clean up any resources
+ * associated with this host endpoint.
+ *
+ * @param hostEndpointId The host endpoint ID to configure notifications for.
+ * @param enable true to enable notifications.
+ *
+ * @return true on success
+ *
+ * @see chreMessageFromHostData
+ * @see chreHostEndpointNotification
+ * @see CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION
+ *
+ * @since v1.6
+ */
+bool chreConfigureHostEndpointNotifications(uint16_t hostEndpointId,
+                                            bool enable);
+
+/**
+ * Publishes RPC services from this nanoapp.
+ *
+ * When this API is invoked, the list of RPC services will be provided to
+ * host applications interacting with the nanoapp.
+ *
+ * This function must be invoked from nanoappStart(), to guarantee stable output
+ * of the list of RPC services supported by the nanoapp.
+ *
+ * Although nanoapps are recommended to only call this API once with all
+ * services it intends to publish, if it is called multiple times, each
+ * call will append to the list of published services.
+ *
+ * Starting in CHRE API v1.8, the implementation must allow for a nanoapp to
+ * publish at least CHRE_MINIMUM_RPC_SERVICE_LIMIT services and at most
+ * UINT8_MAX services. If calling this function would result in exceeding
+ * the limit, the services must not be published and it must return false.
+ *
+ * @param services A non-null pointer to the list of RPC services to publish.
+ * @param numServices The number of services to publish, i.e. the length of the
+ *   services array.
+ *
+ * @return true if the publishing is successful.
+ *
+ * @since v1.6
+ */
+bool chrePublishRpcServices(struct chreNanoappRpcService *services,
+                            size_t numServices);
+
+/**
+ * Retrieves metadata for a given host endpoint ID.
+ *
+ * This API will provide metadata regarding an endpoint associated with a
+ * host endpoint ID. The nanoapp should use this API to determine more
+ * information about a host endpoint that has sent a message to the nanoapp,
+ * after receiving a chreMessageFromHostData (which includes the endpoint ID).
+ *
+ * If the given host endpoint ID is not associated with a valid host (or if the
+ * client has disconnected from the Android or CHRE framework, i.e. no longer
+ * able to send messages to CHRE), this method will return false and info will
+ * not be populated.
+ *
+ * @param hostEndpointId The endpoint ID of the host to get info for.
+ * @param info The non-null pointer to where the metadata will be stored.
+ *
+ * @return true if info has been successfully populated.
+ *
+ * @since v1.6
+ */
+bool chreGetHostEndpointInfo(uint16_t hostEndpointId,
+                             struct chreHostEndpointInfo *info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_EVENT_H_ */
+
diff --git a/chre_api/legacy/v1_10/chre/gnss.h b/chre_api/legacy/v1_10/chre/gnss.h
new file mode 100644
index 0000000..a326e85
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/gnss.h
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_GNSS_H_
+#define _CHRE_GNSS_H_
+
+/**
+ * @file
+ * Global Navigation Satellite System (GNSS) API.
+ *
+ * These structures and definitions are based on the Android N GPS HAL.
+ * Refer to that header file (located at this path as of the time of this
+ * comment: hardware/libhardware/include/hardware/gps.h) and associated
+ * documentation for further details and explanations for these fields.
+ * References in comments like "(ref: GnssAccumulatedDeltaRangeState)" map to
+ * the relevant element in the GPS HAL where additional information can be
+ * found.
+ *
+ * In general, the parts of this API that are taken from the GPS HAL follow the
+ * naming conventions established in that interface rather than the CHRE API
+ * conventions, in order to avoid confusion and enable code re-use where
+ * applicable.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <chre/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The set of flags that may be returned by chreGnssGetCapabilities()
+ * @defgroup CHRE_GNSS_CAPABILITIES
+ * @{
+ */
+
+//! A lack of flags indicates that GNSS is not supported in this CHRE
+#define CHRE_GNSS_CAPABILITIES_NONE          UINT32_C(0)
+
+//! GNSS position fixes are supported via chreGnssLocationSessionStartAsync()
+#define CHRE_GNSS_CAPABILITIES_LOCATION      UINT32_C(1 << 0)
+
+//! GNSS raw measurements are supported via
+//! chreGnssMeasurementSessionStartAsync()
+#define CHRE_GNSS_CAPABILITIES_MEASUREMENTS  UINT32_C(1 << 1)
+
+//! Location fixes supplied from chreGnssConfigurePassiveLocationListener()
+//! are tapped in at the GNSS engine level, so they include additional fixes
+//! such as those requested by the AP, and not just those requested by other
+//! nanoapps within CHRE (which is the case when this flag is not set)
+#define CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER \
+                                             UINT32_C(1 << 2)
+
+/** @} */
+
+/**
+ * The current version of struct chreGnssDataEvent associated with this API
+ */
+#define CHRE_GNSS_DATA_EVENT_VERSION  UINT8_C(0)
+
+/**
+ * The maximum time the CHRE implementation is allowed to elapse before sending
+ * an event with the result of an asynchronous request, unless specified
+ * otherwise
+ */
+#define CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS  (5 * CHRE_NSEC_PER_SEC)
+
+/**
+ * Produce an event ID in the block of IDs reserved for GNSS
+ * @param offset  Index into GNSS event ID block; valid range [0,15]
+ */
+#define CHRE_GNSS_EVENT_ID(offset)  (CHRE_EVENT_GNSS_FIRST_EVENT + (offset))
+
+/**
+ * nanoappHandleEvent argument: struct chreAsyncResult
+ *
+ * Communicates the asynchronous result of a request to the GNSS API, such as
+ * starting a location session via chreGnssLocationSessionStartAsync(). The
+ * requestType field in chreAsyncResult is set to a value from enum
+ * chreGnssRequestType.
+ */
+#define CHRE_EVENT_GNSS_ASYNC_RESULT  CHRE_GNSS_EVENT_ID(0)
+
+/**
+ * nanoappHandleEvent argument: struct chreGnssLocationEvent
+ *
+ * Represents a location fix provided by the GNSS subsystem.
+ */
+#define CHRE_EVENT_GNSS_LOCATION      CHRE_GNSS_EVENT_ID(1)
+
+/**
+ * nanoappHandleEvent argument: struct chreGnssDataEvent
+ *
+ * Represents a set of GNSS measurements with associated clock data.
+ */
+#define CHRE_EVENT_GNSS_DATA          CHRE_GNSS_EVENT_ID(2)
+
+// NOTE: Do not add new events with ID > 15; only values 0-15 are reserved
+// (see chre/event.h)
+
+// Flags indicating the Accumulated Delta Range's states
+// (ref: GnssAccumulatedDeltaRangeState)
+#define CHRE_GNSS_ADR_STATE_UNKNOWN     UINT16_C(0)
+#define CHRE_GNSS_ADR_STATE_VALID       UINT16_C(1 << 0)
+#define CHRE_GNSS_ADR_STATE_RESET       UINT16_C(1 << 1)
+#define CHRE_GNSS_ADR_STATE_CYCLE_SLIP  UINT16_C(1 << 2)
+
+// Flags to indicate what fields in chreGnssClock are valid (ref: GnssClockFlags)
+#define CHRE_GNSS_CLOCK_HAS_LEAP_SECOND        UINT16_C(1 << 0)
+#define CHRE_GNSS_CLOCK_HAS_TIME_UNCERTAINTY   UINT16_C(1 << 1)
+#define CHRE_GNSS_CLOCK_HAS_FULL_BIAS          UINT16_C(1 << 2)
+#define CHRE_GNSS_CLOCK_HAS_BIAS               UINT16_C(1 << 3)
+#define CHRE_GNSS_CLOCK_HAS_BIAS_UNCERTAINTY   UINT16_C(1 << 4)
+#define CHRE_GNSS_CLOCK_HAS_DRIFT              UINT16_C(1 << 5)
+#define CHRE_GNSS_CLOCK_HAS_DRIFT_UNCERTAINTY  UINT16_C(1 << 6)
+
+// Flags to indicate which values are valid in a GpsLocation
+// (ref: GpsLocationFlags)
+#define CHRE_GPS_LOCATION_HAS_LAT_LONG           UINT16_C(1 << 0)
+#define CHRE_GPS_LOCATION_HAS_ALTITUDE           UINT16_C(1 << 1)
+#define CHRE_GPS_LOCATION_HAS_SPEED              UINT16_C(1 << 2)
+#define CHRE_GPS_LOCATION_HAS_BEARING            UINT16_C(1 << 3)
+#define CHRE_GPS_LOCATION_HAS_ACCURACY           UINT16_C(1 << 4)
+
+//! @since v1.3
+#define CHRE_GPS_LOCATION_HAS_ALTITUDE_ACCURACY  UINT16_C(1 << 5)
+//! @since v1.3
+#define CHRE_GPS_LOCATION_HAS_SPEED_ACCURACY     UINT16_C(1 << 6)
+//! @since v1.3
+#define CHRE_GPS_LOCATION_HAS_BEARING_ACCURACY   UINT16_C(1 << 7)
+
+/**
+ * The maximum number of instances of struct chreGnssMeasurement that may be
+ * included in a single struct chreGnssDataEvent.
+ *
+ * The value of this struct was increased from 64 to 128 in CHRE v1.5. For
+ * nanoapps targeting CHRE v1.4 or lower, the measurement_count will be capped
+ * at 64.
+ */
+#define CHRE_GNSS_MAX_MEASUREMENT  UINT8_C(128)
+#define CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5  UINT8_C(64)
+
+// Flags indicating the GNSS measurement state (ref: GnssMeasurementState)
+#define CHRE_GNSS_MEASUREMENT_STATE_UNKNOWN                UINT16_C(0)
+#define CHRE_GNSS_MEASUREMENT_STATE_CODE_LOCK              UINT16_C(1 << 0)
+#define CHRE_GNSS_MEASUREMENT_STATE_BIT_SYNC               UINT16_C(1 << 1)
+#define CHRE_GNSS_MEASUREMENT_STATE_SUBFRAME_SYNC          UINT16_C(1 << 2)
+#define CHRE_GNSS_MEASUREMENT_STATE_TOW_DECODED            UINT16_C(1 << 3)
+#define CHRE_GNSS_MEASUREMENT_STATE_MSEC_AMBIGUOUS         UINT16_C(1 << 4)
+#define CHRE_GNSS_MEASUREMENT_STATE_SYMBOL_SYNC            UINT16_C(1 << 5)
+#define CHRE_GNSS_MEASUREMENT_STATE_GLO_STRING_SYNC        UINT16_C(1 << 6)
+#define CHRE_GNSS_MEASUREMENT_STATE_GLO_TOD_DECODED        UINT16_C(1 << 7)
+#define CHRE_GNSS_MEASUREMENT_STATE_BDS_D2_BIT_SYNC        UINT16_C(1 << 8)
+#define CHRE_GNSS_MEASUREMENT_STATE_BDS_D2_SUBFRAME_SYNC   UINT16_C(1 << 9)
+#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1BC_CODE_LOCK     UINT16_C(1 << 10)
+#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1C_2ND_CODE_LOCK  UINT16_C(1 << 11)
+#define CHRE_GNSS_MEASUREMENT_STATE_GAL_E1B_PAGE_SYNC      UINT16_C(1 << 12)
+#define CHRE_GNSS_MEASUREMENT_STATE_SBAS_SYNC              UINT16_C(1 << 13)
+
+#define CHRE_GNSS_MEASUREMENT_CARRIER_FREQUENCY_UNKNOWN    0.f
+
+/**
+ * Indicates a type of request made in this API. Used to populate the resultType
+ * field of struct chreAsyncResult sent with CHRE_EVENT_GNSS_ASYNC_RESULT.
+ */
+enum chreGnssRequestType {
+    CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START    = 1,
+    CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP     = 2,
+    CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START = 3,
+    CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP  = 4,
+};
+
+/**
+ * Constellation type associated with an SV
+ */
+enum chreGnssConstellationType {
+    CHRE_GNSS_CONSTELLATION_UNKNOWN = 0,
+    CHRE_GNSS_CONSTELLATION_GPS     = 1,
+    CHRE_GNSS_CONSTELLATION_SBAS    = 2,
+    CHRE_GNSS_CONSTELLATION_GLONASS = 3,
+    CHRE_GNSS_CONSTELLATION_QZSS    = 4,
+    CHRE_GNSS_CONSTELLATION_BEIDOU  = 5,
+    CHRE_GNSS_CONSTELLATION_GALILEO = 6,
+};
+
+/**
+ * Enumeration of available values for the chreGnssMeasurement multipath indicator
+ */
+enum chreGnssMultipathIndicator {
+    //! The indicator is not available or unknown
+    CHRE_GNSS_MULTIPATH_INDICATOR_UNKNOWN     = 0,
+    //! The measurement is indicated to be affected by multipath
+    CHRE_GNSS_MULTIPATH_INDICATOR_PRESENT     = 1,
+    //! The measurement is indicated to be not affected by multipath
+    CHRE_GNSS_MULTIPATH_INDICATOR_NOT_PRESENT = 2,
+};
+
+/**
+ * Represents an estimate of the GNSS clock time (see the Android GPS HAL for
+ * more detailed information)
+ */
+struct chreGnssClock {
+    //! The GNSS receiver hardware clock value in nanoseconds, including
+    //! uncertainty
+    int64_t time_ns;
+
+    //! The difference between hardware clock inside GNSS receiver and the
+    //! estimated GNSS time in nanoseconds; contains bias uncertainty
+    int64_t full_bias_ns;
+
+    //! Sub-nanosecond bias, adds to full_bias_ns
+    float bias_ns;
+
+    //! The clock's drift in nanoseconds per second
+    float drift_nsps;
+
+    //! 1-sigma uncertainty associated with the clock's bias in nanoseconds
+    float bias_uncertainty_ns;
+
+    //! 1-sigma uncertainty associated with the clock's drift in nanoseconds
+    //! per second
+    float drift_uncertainty_nsps;
+
+    //! While this number stays the same, timeNs should flow continuously
+    uint32_t hw_clock_discontinuity_count;
+
+    //! A set of flags indicating the validity of the fields in this data
+    //! structure (see GNSS_CLOCK_HAS_*)
+    uint16_t flags;
+
+    //! Reserved for future use; set to 0
+    uint8_t reserved[2];
+};
+
+/**
+ * Represents a GNSS measurement; contains raw and computed information (see the
+ * Android GPS HAL for more detailed information)
+ */
+struct chreGnssMeasurement {
+    //! Hardware time offset from time_ns for this measurement, in nanoseconds
+    int64_t time_offset_ns;
+
+    //! Accumulated delta range since the last channel reset in micro-meters
+    int64_t accumulated_delta_range_um;
+
+    //! Received GNSS satellite time at the time of measurement, in nanoseconds
+    int64_t received_sv_time_in_ns;
+
+    //! 1-sigma uncertainty of received GNSS satellite time, in nanoseconds
+    int64_t received_sv_time_uncertainty_in_ns;
+
+    //! Pseudorange rate at the timestamp in meters per second (uncorrected)
+    float pseudorange_rate_mps;
+
+    //! 1-sigma uncertainty of pseudorange rate in meters per second
+    float pseudorange_rate_uncertainty_mps;
+
+    //! 1-sigma uncertainty of the accumulated delta range in meters
+    float accumulated_delta_range_uncertainty_m;
+
+    //! Carrier-to-noise density in dB-Hz, in the range of [0, 63]
+    float c_n0_dbhz;
+
+    //! Signal to noise ratio (dB), power above observed noise at correlators
+    float snr_db;
+
+    //! Satellite sync state flags (GNSS_MEASUREMENT_STATE_*) - sets modulus for
+    //! received_sv_time_in_ns
+    uint16_t state;
+
+    //! Set of ADR state flags (GNSS_ADR_STATE_*)
+    uint16_t accumulated_delta_range_state;
+
+    //! Satellite vehicle ID number
+    int16_t svid;
+
+    //! Constellation of the given satellite vehicle
+    //! @see #chreGnssConstellationType
+    uint8_t constellation;
+
+    //! @see #chreGnssMultipathIndicator
+    uint8_t multipath_indicator;
+
+    //! Carrier frequency of the signal tracked in Hz.
+    //! For example, it can be the GPS central frequency for L1 = 1575.45 MHz,
+    //! or L2 = 1227.60 MHz, L5 = 1176.45 MHz, various GLO channels, etc.
+    //!
+    //! Set to CHRE_GNSS_MEASUREMENT_CARRIER_FREQUENCY_UNKNOWN if not reported.
+    //!
+    //! For an L1, L5 receiver tracking a satellite on L1 and L5 at the same
+    //! time, two chreGnssMeasurement structs must be reported for this same
+    //! satellite, in one of the measurement structs, all the values related to
+    //! L1 must be filled, and in the other all of the values related to L5
+    //! must be filled.
+    //! @since v1.4
+    float carrier_frequency_hz;
+};
+
+/**
+ * Data structure sent with events associated with CHRE_EVENT_GNSS_DATA, enabled
+ * via chreGnssMeasurementSessionStartAsync()
+ */
+struct chreGnssDataEvent {
+    //! Indicates the version of the structure, for compatibility purposes.
+    //! Clients do not normally need to worry about this field; the CHRE
+    //! implementation guarantees that it only sends the client the structure
+    //! version it expects.
+    uint8_t version;
+
+    //! Number of chreGnssMeasurement entries included in this event. Must be in
+    //! the range [0, CHRE_GNSS_MAX_MEASUREMENT]
+    uint8_t measurement_count;
+
+    //! Reserved for future use; set to 0
+    uint8_t reserved[6];
+
+    struct chreGnssClock clock;
+
+    //! Pointer to an array containing measurement_count measurements
+    const struct chreGnssMeasurement *measurements;
+};
+
+/**
+ * Data structure sent with events of type CHRE_EVENT_GNSS_LOCATION, enabled via
+ * chreGnssLocationSessionStartAsync(). This is modeled after GpsLocation in the
+ * GPS HAL, but does not use the double data type.
+ */
+struct chreGnssLocationEvent {
+    //! UTC timestamp for location fix in milliseconds since January 1, 1970
+    uint64_t timestamp;
+
+    //! Fixed point latitude, degrees times 10^7 (roughly centimeter resolution)
+    int32_t latitude_deg_e7;
+
+    //! Fixed point longitude, degrees times 10^7 (roughly centimeter
+    //! resolution)
+    int32_t longitude_deg_e7;
+
+    //! Altitude in meters above the WGS 84 reference ellipsoid
+    float altitude;
+
+    //! Horizontal speed in meters per second
+    float speed;
+
+    //! Clockwise angle between north and current heading, in degrees; range
+    //! [0, 360)
+    float bearing;
+
+    //! Expected horizontal accuracy in meters such that a circle with a radius
+    //! of length 'accuracy' from the latitude and longitude has a 68%
+    //! probability of including the true location.
+    float accuracy;
+
+    //! A set of flags indicating which fields in this structure are valid.
+    //! If any fields are not available, the flag must not be set and the field
+    //! must be initialized to 0.
+    //! @see #GpsLocationFlags
+    uint16_t flags;
+
+    //! Reserved for future use; set to 0
+    //! @since v1.3
+    uint8_t reserved[2];
+
+    //! Expected vertical accuracy in meters such that a range of
+    //! 2 * altitude_accuracy centered around altitude has a 68% probability of
+    //! including the true altitude.
+    //! @since v1.3
+    float altitude_accuracy;
+
+    //! Expected speed accuracy in meters per second such that a range of
+    //! 2 * speed_accuracy centered around speed has a 68% probability of
+    //! including the true speed.
+    //! @since v1.3
+    float speed_accuracy;
+
+    //! Expected bearing accuracy in degrees such that a range of
+    //! 2 * bearing_accuracy centered around bearing has a 68% probability of
+    //! including the true bearing.
+    //! @since v1.3
+    float bearing_accuracy;
+};
+
+
+/**
+ * Retrieves a set of flags indicating the GNSS features supported by the
+ * current CHRE implementation. The value returned by this function must be
+ * consistent for the entire duration of the Nanoapp's execution.
+ *
+ * The client must allow for more flags to be set in this response than it knows
+ * about, for example if the implementation supports a newer version of the API
+ * than the client was compiled against.
+ *
+ * @return A bitmask with zero or more CHRE_GNSS_CAPABILITIES_* flags set
+ *
+ * @since v1.1
+ */
+uint32_t chreGnssGetCapabilities(void);
+
+/**
+ * Nanoapps must define CHRE_NANOAPP_USES_GNSS somewhere in their build
+ * system (e.g. Makefile) if the nanoapp needs to use the following GNSS APIs.
+ * In addition to allowing access to these APIs, defining this macro will also
+ * ensure CHRE enforces that all host clients this nanoapp talks to have the
+ * required Android permissions needed to listen to GNSS data by adding metadata
+ * to the nanoapp.
+ */
+#if defined(CHRE_NANOAPP_USES_GNSS) || !defined(CHRE_IS_NANOAPP_BUILD)
+
+/**
+ * Initiates a GNSS positioning session, or changes the requested interval of an
+ * existing session. If starting or modifying the session was successful, then
+ * the GNSS engine will work on determining the device's position.
+ *
+ * This result of this request is delivered asynchronously via an event of type
+ * CHRE_EVENT_GNSS_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult}
+ * for more details. If the "Location" setting is disabled at the Android level,
+ * the CHRE implementation is expected to return a result with
+ * CHRE_ERROR_FUNCTION_DISABLED.
+ *
+ * If chreGnssGetCapabilities() returns a value that does not have the
+ * CHRE_GNSS_CAPABILITIES_LOCATION flag set, then this method will return false.
+ *
+ * @param minIntervalMs The desired minimum interval between location fixes
+ *        delivered to the client via CHRE_EVENT_GNSS_LOCATION, in milliseconds.
+ *        The requesting client must allow for fixes to be delivered at shorter
+ *        or longer interval than requested. For example, adverse RF conditions
+ *        may result in fixes arriving at a longer interval, etc.
+ * @param minTimeToNextFixMs The desired minimum time to the next location fix.
+ *        If this is 0, the GNSS engine should start working on the next fix
+ *        immediately. If greater than 0, the GNSS engine should not spend
+ *        measurable power to produce a location fix until this amount of time
+ *        has elapsed.
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ *
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.1
+ * @note Requires GNSS permission
+ */
+bool chreGnssLocationSessionStartAsync(uint32_t minIntervalMs,
+                                       uint32_t minTimeToNextFixMs,
+                                       const void *cookie);
+
+/**
+ * Terminates an existing GNSS positioning session. If no positioning session
+ * is active at the time of this request, it is treated as if an active session
+ * was successfully ended.
+ *
+ * This result of this request is delivered asynchronously via an event of type
+ * CHRE_EVENT_GNSS_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult}
+ * for more details.
+ *
+ * After CHRE_EVENT_GNSS_ASYNC_RESULT is delivered to the client, no more
+ * CHRE_EVENT_GNSS_LOCATION events will be delievered until a new location
+ * session is started.
+ *
+ * If chreGnssGetCapabilities() returns a value that does not have the
+ * CHRE_GNSS_CAPABILITIES_LOCATION flag set, then this method will return false.
+ *
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ *
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.1
+ * @note Requires GNSS permission
+ */
+bool chreGnssLocationSessionStopAsync(const void *cookie);
+
+/**
+ * Initiates a request to receive raw GNSS measurements. A GNSS measurement
+ * session can exist independently of location sessions. In other words, a
+ * Nanoapp is able to receive measurements at its requested interval both with
+ * and without an active location session.
+ *
+ * This result of this request is delivered asynchronously via an event of type
+ * CHRE_EVENT_GNSS_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult}
+ * for more details. If the "Location" setting is disabled at the Android level,
+ * the CHRE implementation is expected to return a result with
+ * CHRE_ERROR_FUNCTION_DISABLED.
+ *
+ * If chreGnssGetCapabilities() returns a value that does not have the
+ * CHRE_GNSS_CAPABILITIES_MEASUREMENTS flag set, then this method will return
+ * false.
+ *
+ * @param minIntervalMs The desired minimum interval between measurement reports
+ *        delivered via CHRE_EVENT_GNSS_DATA. When requested at 1000ms or
+ *        faster, and GNSS measurements are tracked, device should report
+ *        measurements as fast as requested, and shall report no slower than
+ *        once every 1000ms, on average.
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ *
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.1
+ * @note Requires GNSS permission
+ */
+bool chreGnssMeasurementSessionStartAsync(uint32_t minIntervalMs,
+                                          const void *cookie);
+
+/**
+ * Terminates an existing raw GNSS measurement session. If no measurement
+ * session is active at the time of this request, it is treated as if an active
+ * session was successfully ended.
+ *
+ * This result of this request is delivered asynchronously via an event of type
+ * CHRE_EVENT_GNSS_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult}
+ * for more details.
+ *
+ * If chreGnssGetCapabilities() returns a value that does not have the
+ * CHRE_GNSS_CAPABILITIES_MEASUREMENTS flag set, then this method will return
+ * false.
+ *
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ *
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.1
+ * @note Requires GNSS permission
+ */
+bool chreGnssMeasurementSessionStopAsync(const void *cookie);
+
+/**
+ * Controls whether this nanoapp will passively receive GNSS-based location
+ * fixes produced as a result of location sessions initiated by other entities.
+ * This function allows a nanoapp to opportunistically receive location fixes
+ * via CHRE_EVENT_GNSS_LOCATION events without imposing additional power cost,
+ * though with no guarantees as to when or how often those events will arrive.
+ * There will be no duplication of events if a passive location listener and
+ * location session are enabled in parallel.
+ *
+ * Enabling passive location listening is not required to receive events for an
+ * active location session started via chreGnssLocationSessionStartAsync(). This
+ * setting is independent of the active location session, so modifying one does
+ * not have an effect on the other.
+ *
+ * If chreGnssGetCapabilities() returns a value that does not have the
+ * CHRE_GNSS_CAPABILITIES_LOCATION flag set or the value returned by
+ * chreGetApiVersion() is less than CHRE_API_VERSION_1_2, then this method will
+ * return false.
+ *
+ * If chreGnssGetCapabilities() includes
+ * CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER, the passive
+ * registration is recorded at the GNSS engine level, so events include fixes
+ * requested by the applications processor and potentially other non-CHRE
+ * clients. If this flag is not set, then only fixes requested by other nanoapps
+ * within CHRE are provided.
+ *
+ * @param enable true to receive opportunistic location fixes, false to disable
+ *
+ * @return true if the configuration was processed successfully, false on error
+ *     or if this feature is not supported
+ *
+ * @since v1.2
+ * @note Requires GNSS permission
+ */
+bool chreGnssConfigurePassiveLocationListener(bool enable);
+
+#else  /* defined(CHRE_NANOAPP_USES_GNSS) || !defined(CHRE_IS_NANOAPP_BUILD) */
+#define CHRE_GNSS_PERM_ERROR_STRING \
+    "CHRE_NANOAPP_USES_GNSS must be defined when building this nanoapp in " \
+    "order to refer to "
+#define chreGnssLocationSessionStartAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \
+                     "chreGnssLocationSessionStartAsync")
+#define chreGnssLocationSessionStopAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \
+                     "chreGnssLocationSessionStopAsync")
+#define chreGnssMeasurementSessionStartAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \
+                     "chreGnssMeasurementSessionStartAsync")
+#define chreGnssMeasurementSessionStopAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \
+                     "chreGnssMeasurementSessionStopAsync")
+#define chreGnssConfigurePassiveLocationListener(...) \
+    CHRE_BUILD_ERROR(CHRE_GNSS_PERM_ERROR_STRING \
+                     "chreGnssConfigurePassiveLocationListener")
+#endif  /* defined(CHRE_NANOAPP_USES_GNSS) || !defined(CHRE_IS_NANOAPP_BUILD) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_GNSS_H_ */
diff --git a/chre_api/legacy/v1_10/chre/nanoapp.h b/chre_api/legacy/v1_10/chre/nanoapp.h
new file mode 100644
index 0000000..3a1c362
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/nanoapp.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_NANOAPP_H_
+#define _CHRE_NANOAPP_H_
+
+/**
+ * @file
+ * Methods in the Context Hub Runtime Environment which must be implemented
+ * by the nanoapp.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Method invoked by the CHRE when loading the nanoapp.
+ *
+ * Every CHRE method is legal to call from this method.
+ *
+ * @return  'true' if the nanoapp successfully started.  'false' if the nanoapp
+ *     failed to properly initialize itself (for example, could not obtain
+ *     sufficient memory from the heap).  If this method returns 'false', the
+ *     nanoapp will be unloaded by the CHRE (and nanoappEnd will
+ *     _not_ be invoked in that case).
+ * @see nanoappEnd
+ */
+bool nanoappStart(void);
+
+/**
+ * Method invoked by the CHRE when there is an event for this nanoapp.
+ *
+ * Every CHRE method is legal to call from this method.
+ *
+ * @param senderInstanceId  The Instance ID for the source of this event.
+ *     Note that this may be CHRE_INSTANCE_ID, indicating that the event
+ *     was generated by the CHRE.
+ * @param eventType  The event type.  This might be one of the CHRE_EVENT_*
+ *     types defined in this API.  But it might also be a user-defined event.
+ * @param eventData  The associated data, if any, for this specific type of
+ *     event.  From the nanoapp's perspective, this eventData's lifetime ends
+ *     when this method returns, and thus any data the nanoapp wishes to
+ *     retain must be copied.  Note that interpretation of event data is
+ *     given by the event type, and for some events may not be a valid
+ *     pointer.  See documentation of the specific CHRE_EVENT_* types for how to
+ *     interpret this data for those.  Note that for user events, you will
+ *     need to establish what this data means.
+ */
+void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
+                        const void *eventData);
+
+/**
+ * Method invoked by the CHRE when unloading the nanoapp.
+ *
+ * It is not valid to attempt to send events or messages, or to invoke functions
+ * which will generate events to this app, within the nanoapp implementation of
+ * this function.  That means it is illegal for the nanoapp invoke any of the
+ * following:
+ *
+ * - chreSendEvent()
+ * - chreSendMessageToHost()
+ * - chreSensorConfigure()
+ * - chreSensorConfigureModeOnly()
+ * - chreTimerSet()
+ * - etc.
+ *
+ * @see nanoappStart
+ */
+void nanoappEnd(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_NANOAPP_H_ */
diff --git a/chre_api/legacy/v1_10/chre/re.h b/chre_api/legacy/v1_10/chre/re.h
new file mode 100644
index 0000000..b523a41
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/re.h
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_RE_H_
+#define _CHRE_RE_H_
+
+/**
+ * @file
+ * Some of the core Runtime Environment utilities of the Context Hub
+ * Runtime Environment.
+ *
+ * This includes functions for memory allocation, logging, and timers.
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <chre/toolchain.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The instance ID for the CHRE.
+ *
+ * This ID is used to identify events generated by the CHRE (as
+ * opposed to events generated by another nanoapp).
+ */
+#define CHRE_INSTANCE_ID  UINT32_C(0)
+
+/**
+ * A timer ID representing an invalid timer.
+ *
+ * This valid is returned by chreTimerSet() if a timer cannot be
+ * started.
+ */
+#define CHRE_TIMER_INVALID  UINT32_C(-1)
+
+
+/**
+ * The maximum size, in characters including null terminator, guaranteed for
+ * logging debug data with one call of chreDebugDumpLog() without getting
+ * truncated.
+ *
+ * @see chreDebugDumpLog
+ * @since v1.4
+ */
+#define CHRE_DEBUG_DUMP_MINIMUM_MAX_SIZE 1000
+
+/**
+ * The set of flags that may be returned by chreGetCapabilities()
+ * @defgroup CHRE_CAPABILITIES
+ * @{
+ */
+
+//! None of the optional capabilities are supported
+#define CHRE_CAPABILITIES_NONE                 UINT32_C(0)
+
+//! Support for reliable messages.
+//! @see chreSendReliableMessageAsync()
+#define CHRE_CAPABILITIES_RELIABLE_MESSAGES    UINT32_C(1 << 0)
+
+/** @} */
+
+/**
+ * Logging levels used to indicate severity level of logging messages.
+ *
+ * CHRE_LOG_ERROR: Something fatal has happened, i.e. something that will have
+ *     user-visible consequences and won't be recoverable without explicitly
+ *     deleting some data, uninstalling applications, wiping the data
+ *     partitions or reflashing the entire phone (or worse).
+ * CHRE_LOG_WARN: Something that will have user-visible consequences but is
+ *     likely to be recoverable without data loss by performing some explicit
+ *     action, ranging from waiting or restarting an app all the way to
+ *     re-downloading a new version of an application or rebooting the device.
+ * CHRE_LOG_INFO: Something interesting to most people happened, i.e. when a
+ *     situation is detected that is likely to have widespread impact, though
+ *     isn't necessarily an error.
+ * CHRE_LOG_DEBUG: Used to further note what is happening on the device that
+ *     could be relevant to investigate and debug unexpected behaviors. You
+ *     should log only what is needed to gather enough information about what
+ *     is going on about your component.
+ *
+ * There is currently no API to turn on/off logging by level, but we anticipate
+ * adding such in future releases.
+ *
+ * @see chreLog
+ */
+enum chreLogLevel {
+    CHRE_LOG_ERROR,
+    CHRE_LOG_WARN,
+    CHRE_LOG_INFO,
+    CHRE_LOG_DEBUG
+};
+
+/**
+ * Retrieves a set of flags indicating the CHRE optional features supported by
+ * the current implementation. The value returned by this function must be
+ * consistent for the entire duration of the nanoapp's execution.
+ *
+ * The client must allow for more flags to be set in this response than it knows
+ * about, for example if the implementation supports a newer version of the API
+ * than the client was compiled against.
+ *
+ * @return A bitmask with zero or more CHRE_CAPABILITIES_* flags set.
+ *
+ * @since v1.10
+ */
+uint32_t chreGetCapabilities(void);
+
+/**
+ * Returns the maximum size in bytes of a message sent to the host.
+ * This function will always return a value greater than or equal to
+ * CHRE_MESSAGE_TO_HOST_MAX_SIZE. If the capability
+ * CHRE_CAPABILITIES_RELIABLE_MESSAGES is enabled, this function will
+ * return a value greater than or equal to 32000.
+ *
+ * On v1.9 or earlier platforms, this will always return CHRE_MESSAGE_TO_HOST_MAX_SIZE.
+ *
+ * @return The maximum message size in bytes.
+ *
+ * @since v1.10
+ */
+uint32_t chreGetMessageToHostMaxSize(void);
+
+/**
+ * Get the application ID.
+ *
+ * The application ID is set by the loader of the nanoapp.  This is not
+ * assured to be unique among all nanoapps running in the system.
+ *
+ * @return The application ID.
+ */
+uint64_t chreGetAppId(void);
+
+/**
+ * Get the instance ID.
+ *
+ * The instance ID is the CHRE handle to this nanoapp.  This is assured
+ * to be unique among all nanoapps running in the system, and to be
+ * different from the CHRE_INSTANCE_ID.  This is the ID used to communicate
+ * between nanoapps.
+ *
+ * @return The instance ID
+ */
+uint32_t chreGetInstanceId(void);
+
+/**
+ * A method for logging information about the system.
+ *
+ * The chreLog logging activity alone must not cause host wake-ups. For
+ * example, logs could be buffered in internal memory when the host is asleep,
+ * and delivered when appropriate (e.g. the host wakes up). If done this way,
+ * the internal buffer is recommended to be large enough (at least a few KB), so
+ * that multiple messages can be buffered. When these logs are sent to the host,
+ * they are strongly recommended to be made visible under the tag 'CHRE' in
+ * logcat - a future version of the CHRE API may make this a hard requirement.
+ *
+ * A log entry can have a variety of levels (@see LogLevel).  This function
+ * allows a variable number of arguments, in a printf-style format.
+ *
+ * A nanoapp needs to be able to rely upon consistent printf format
+ * recognition across any platform, and thus we establish formats which
+ * are required to be handled by every CHRE implementation.  Some of the
+ * integral formats may seem obscure, but this API heavily uses types like
+ * uint32_t and uint16_t.  The platform independent macros for those printf
+ * formats, like PRId32 or PRIx16, end up using some of these "obscure"
+ * formats on some platforms, and thus are required.
+ *
+ * For the initial N release, our emphasis is on correctly getting information
+ * into the log, and minimizing the requirements for CHRE implementations
+ * beyond that.  We're not as concerned about how the information is visually
+ * displayed.  As a result, there are a number of format sub-specifiers which
+ * are "OPTIONAL" for the N implementation.  "OPTIONAL" in this context means
+ * that a CHRE implementation is allowed to essentially ignore the specifier,
+ * but it must understand the specifier enough in order to properly skip it.
+ *
+ * For a nanoapp author, an OPTIONAL format means you might not get exactly
+ * what you want on every CHRE implementation, but you will always get
+ * something valid.
+ *
+ * To be clearer, here's an example with the OPTIONAL 0-padding for integers
+ * for different hypothetical CHRE implementations.
+ * Compliant, chose to implement OPTIONAL format:
+ *   chreLog(level, "%04x", 20) ==> "0014"
+ * Compliant, chose not to implement OPTIONAL format:
+ *   chreLog(level, "%04x", 20) ==> "14"
+ * Non-compliant, discarded format because the '0' was assumed to be incorrect:
+ *   chreLog(level, "%04x", 20) ==> ""
+ *
+ * Note that some of the OPTIONAL specifiers will probably become
+ * required in future APIs.
+ *
+ * We also have NOT_SUPPORTED specifiers.  Nanoapp authors should not use any
+ * NOT_SUPPORTED specifiers, as unexpected things could happen on any given
+ * CHRE implementation.  A CHRE implementation is allowed to support this
+ * (for example, when using shared code which already supports this), but
+ * nanoapp authors need to avoid these.
+ *
+ * Unless specifically noted as OPTIONAL or NOT_SUPPORTED, format
+ * (sub-)specifiers listed below are required.
+ *
+ * While all CHRE implementations must support chreLog(), some platform
+ * implementations may support enhanced logging functionality only possible
+ * through a macro. This improved functionality is supported through
+ * platform-specific customization of the log macros provided in
+ * chre/util/nanoapp/log.h. All nanoapps are recommended to use these log
+ * macros where possible, as they will fall back to chreLog() as needed.
+ *
+ * OPTIONAL format sub-specifiers:
+ * - '-' (left-justify within the given field width)
+ * - '+' (precede the result with a '+' sign if it is positive)
+ * - ' ' (precede the result with a blank space if no sign is going to be
+ *        output)
+ * - '#' (For 'o', 'x' or 'X', precede output with "0", "0x" or "0X",
+ *        respectively.  For floating point, unconditionally output a decimal
+ *        point.)
+ * - '0' (left pad the number with zeroes instead of spaces when <width>
+ *        needs padding)
+ * - <width> (A number representing the minimum number of characters to be
+ *            output, left-padding with blank spaces if needed to meet the
+ *            minimum)
+ * - '.'<precision> (A number which has different meaning depending on context.)
+ *    - Integer context: Minimum number of digits to output, padding with
+ *          leading zeros if needed to meet the minimum.
+ *    - 'f' context: Number of digits to output after the decimal
+ *          point (to the right of it).
+ *    - 's' context: Maximum number of characters to output.
+ *
+ * Integral format specifiers:
+ * - 'd' (signed)
+ * - 'u' (unsigned)
+ * - 'o' (octal)
+ * - 'x' (hexadecimal, lower case)
+ * - 'X' (hexadecimal, upper case)
+ *
+ * Integral format sub-specifiers (as prefixes to an above integral format):
+ * - 'hh' (char)
+ * - 'h' (short)
+ * - 'l' (long)
+ * - 'll' (long long)
+ * - 'z' (size_t)
+ * - 't' (ptrdiff_t)
+ *
+ * Other format specifiers:
+ * - 'f' (floating point)
+ * - 'c' (character)
+ * - 's' (character string, terminated by '\0')
+ * - 'p' (pointer)
+ * - '%' (escaping the percent sign (i.e. "%%" becomes "%"))
+ *
+ * NOT_SUPPORTED specifiers:
+ * - 'n' (output nothing, but fill in a given pointer with the number
+ *        of characters written so far)
+ * - '*' (indicates that the width/precision value comes from one of the
+ *        arguments to the function)
+ * - 'e', 'E' (scientific notation output)
+ * - 'g', 'G' (Shortest floating point representation)
+ *
+ * @param level  The severity level for this message.
+ * @param formatStr  Either the entirety of the message, or a printf-style
+ *     format string of the format documented above.
+ * @param ...  A variable number of arguments necessary for the given
+ *     'formatStr' (there may be no additional arguments for some 'formatStr's).
+ */
+CHRE_PRINTF_ATTR(2, 3)
+void chreLog(enum chreLogLevel level, const char *formatStr, ...);
+
+/**
+ * Get the system time.
+ *
+ * This returns a time in nanoseconds in reference to some arbitrary
+ * time in the past.  This method is only useful for determining timing
+ * between events on the system, and is not useful for determining
+ * any sort of absolute time.
+ *
+ * This value must always increase (and must never roll over).  This
+ * value has no meaning across CHRE reboots.
+ *
+ * @return The system time, in nanoseconds.
+ */
+uint64_t chreGetTime(void);
+
+/**
+ * Retrieves CHRE's current estimated offset between the local CHRE clock
+ * exposed in chreGetTime(), and the host-side clock exposed in the Android API
+ * SystemClock.elapsedRealtimeNanos().  This offset is formed as host time minus
+ * CHRE time, so that it can be added to the value returned by chreGetTime() to
+ * determine the current estimate of the host time.
+ *
+ * A call to this function must not require waking up the host and should return
+ * quickly.
+ *
+ * This function must always return a valid value from the earliest point that
+ * it can be called by a nanoapp.  In other words, it is not valid to return
+ * some fixed/invalid value while waiting for the initial offset estimate to be
+ * determined - this initial offset must be ready before nanoapps are started.
+ *
+ * @return An estimate of the offset between CHRE's time returned in
+ *     chreGetTime() and the time on the host given in the Android API
+ *     SystemClock.elapsedRealtimeNanos(), accurate to within +/- 10
+ *     milliseconds, such that adding this offset to chreGetTime() produces the
+ *     estimated current time on the host.  This value may change over time to
+ *     account for drift, etc., so multiple calls to this API may produce
+ *     different results.
+ *
+ * @since v1.1
+ */
+int64_t chreGetEstimatedHostTimeOffset(void);
+
+/**
+ * Convenience function to retrieve CHRE's estimate of the current time on the
+ * host, corresponding to the Android API SystemClock.elapsedRealtimeNanos().
+ *
+ * @return An estimate of the current time on the host, accurate to within
+ *     +/- 10 milliseconds.  This estimate is *not* guaranteed to be
+ *     monotonically increasing, and may move backwards as a result of receiving
+ *     new information from the host.
+ *
+ * @since v1.1
+ */
+static inline uint64_t chreGetEstimatedHostTime(void) {
+    int64_t offset = chreGetEstimatedHostTimeOffset();
+    uint64_t time = chreGetTime();
+
+    // Just casting time to int64_t and adding the (potentially negative) offset
+    // should be OK under most conditions, but this way avoids issues if
+    // time >= 2^63, which is technically allowed since we don't specify a start
+    // value for chreGetTime(), though one would assume 0 is roughly boot time.
+    if (offset >= 0) {
+        time += (uint64_t) offset;
+    } else {
+        // Assuming chreGetEstimatedHostTimeOffset() is implemented properly,
+        // this will never underflow, because offset = hostTime - chreTime,
+        // and both times are monotonically increasing (e.g. when determining
+        // the offset, if hostTime is 0 and chreTime is 100 we'll have
+        // offset = -100, but chreGetTime() will always return >= 100 after that
+        // point).
+        time -= (uint64_t) (offset * -1);
+    }
+
+    return time;
+}
+
+/**
+ * Set a timer.
+ *
+ * When the timer fires, nanoappHandleEvent will be invoked with
+ * CHRE_EVENT_TIMER and with the given 'cookie'.
+ *
+ * A CHRE implementation is required to provide at least 32
+ * timers.  However, there's no assurance there will be any available
+ * for any given nanoapp (if it's loaded late, etc).
+ *
+ * @param duration  Time, in nanoseconds, before the timer fires.
+ * @param cookie  Argument that will be sent to nanoappHandleEvent upon the
+ *     timer firing.  This is allowed to be NULL and does not need to be
+ *     a valid pointer (assuming the nanoappHandleEvent code is expecting such).
+ * @param oneShot  If true, the timer will just fire once.  If false, the
+ *     timer will continue to refire every 'duration', until this timer is
+ *     canceled (@see chreTimerCancel).
+ *
+ * @return  The timer ID.  If the system is unable to set a timer
+ *     (no more available timers, etc.) then CHRE_TIMER_INVALID will
+ *     be returned.
+ *
+ * @see nanoappHandleEvent
+ */
+uint32_t chreTimerSet(uint64_t duration, const void *cookie, bool oneShot);
+
+/**
+ * Cancel a timer.
+ *
+ * After this method returns, the CHRE assures there will be no more
+ * events sent from this timer, and any enqueued events from this timer
+ * will need to be evicted from the queue by the CHRE.
+ *
+ * @param timerId  A timer ID obtained by this nanoapp via chreTimerSet().
+ * @return true if the timer was cancelled, false otherwise.  We may
+ *     fail to cancel the timer if it's a one shot which (just) fired,
+ *     or if the given timer ID is not owned by the calling app.
+ */
+bool chreTimerCancel(uint32_t timerId);
+
+/**
+ * Terminate this nanoapp.
+ *
+ * This takes effect immediately.
+ *
+ * The CHRE will no longer execute this nanoapp.  The CHRE will not invoke
+ * nanoappEnd(), nor will it call any memory free callbacks in the nanoapp.
+ *
+ * The CHRE will unload/evict this nanoapp's code.
+ *
+ * @param abortCode  A value indicating the reason for aborting.  (Note that
+ *    in this version of the API, there is no way for anyone to access this
+ *    code, but future APIs may expose it.)
+ * @return Never.  This method does not return, as the CHRE stops nanoapp
+ *    execution immediately.
+ */
+void chreAbort(uint32_t abortCode) CHRE_NO_RETURN;
+
+/**
+ * Allocate a given number of bytes from the system heap.
+ *
+ * The nanoapp is required to free this memory via chreHeapFree() prior to
+ * the nanoapp ending.
+ *
+ * While the CHRE implementation is required to free up heap resources of
+ * a nanoapp when unloading it, future requirements and tests focused on
+ * nanoapps themselves may check for memory leaks, and will require nanoapps
+ * to properly manage their heap resources.
+ *
+ * @param bytes  The number of bytes requested.
+ * @return  A pointer to 'bytes' contiguous bytes of heap memory, or NULL
+ *     if the allocation could not be performed.  This pointer must be suitably
+ *     aligned for any kind of variable.
+ *
+ * @see chreHeapFree.
+ */
+CHRE_MALLOC_ATTR
+void *chreHeapAlloc(uint32_t bytes);
+
+/**
+ * Free a heap allocation.
+ *
+ * This allocation must be from a value returned from a chreHeapAlloc() call
+ * made by this nanoapp.  In other words, it is illegal to free memory
+ * allocated by another nanoapp (or the CHRE).
+ *
+ * @param ptr  'ptr' is required to be a value returned from chreHeapAlloc().
+ *     Note that since chreHeapAlloc can return NULL, CHRE
+ *     implementations must safely handle 'ptr' being NULL.
+ *
+ * @see chreHeapAlloc.
+ */
+void chreHeapFree(void *ptr);
+
+/**
+ * Logs the nanoapp's debug data into debug dumps.
+ *
+ * A debug dump is a string representation of information that can be used to
+ * diagnose and debug issues. While chreLog() is useful for logging events as
+ * they happen, the debug dump is a complementary function typically used to
+ * output a snapshot of a nanoapp's state, history, vital statistics, etc. The
+ * CHRE framework is required to pass this information to the debug method in
+ * the Context Hub HAL, where it can be captured in Android bugreports, etc.
+ *
+ * This function must only be called while handling CHRE_DEBUG_DUMP_EVENT,
+ * otherwise it will have no effect. A nanoapp can call this function multiple
+ * times while handling the event. If the resulting formatted string from a
+ * single call to this function is longer than CHRE_DEBUG_DUMP_MINIMUM_MAX_SIZE
+ * characters, it may get truncated.
+ *
+ * @param formatStr A printf-style format string of the format documented in
+ *     chreLog().
+ * @param ... A variable number of arguments necessary for the given 'formatStr'
+ *     (there may be no additional arguments for some 'formatStr's).
+ *
+ * @see chreConfigureDebugDumpEvent
+ * @see chreLog
+ *
+ * @since v1.4
+ */
+CHRE_PRINTF_ATTR(1, 2)
+void chreDebugDumpLog(const char *formatStr, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_RE_H_ */
+
diff --git a/chre_api/legacy/v1_10/chre/sensor.h b/chre_api/legacy/v1_10/chre/sensor.h
new file mode 100644
index 0000000..551803e
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/sensor.h
@@ -0,0 +1,1132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_SENSOR_H_
+#define _CHRE_SENSOR_H_
+
+/**
+ * @file
+ * API dealing with sensor interaction in the Context Hub Runtime
+ * Environment.
+ *
+ * This includes the definition of our sensor types and the ability to
+ * configure them for receiving events.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <chre/common.h>
+#include <chre/event.h>
+#include <chre/sensor_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Base value for all of the data events for sensors.
+ *
+ * The value for a data event FOO is
+ * CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_FOO
+ *
+ * This allows for easy mapping, and also explains why there are gaps
+ * in our values since we don't have all possible sensor types assigned.
+ */
+#define CHRE_EVENT_SENSOR_DATA_EVENT_BASE  CHRE_EVENT_SENSOR_FIRST_EVENT
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x', 'y', and 'z' fields within
+ * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z).
+ *
+ * All values are in SI units (m/s^2) and measure the acceleration applied to
+ * the device.
+ */
+#define CHRE_EVENT_SENSOR_ACCELEROMETER_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_ACCELEROMETER)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorOccurrenceData
+ *
+ * Since this is a one-shot sensor, after this event is delivered to the
+ * nanoapp, the sensor automatically goes into DONE mode.  Sensors of this
+ * type must be configured with a ONE_SHOT mode.
+ */
+#define CHRE_EVENT_SENSOR_INSTANT_MOTION_DETECT_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorOccurrenceData
+ *
+ * Since this is a one-shot sensor, after this event is delivered to the
+ * nanoapp, the sensor automatically goes into DONE mode.  Sensors of this
+ * type must be configured with a ONE_SHOT mode.
+ */
+#define CHRE_EVENT_SENSOR_STATIONARY_DETECT_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_STATIONARY_DETECT)
+
+/**
+ * nanoappHandleEvent argument: struct struct chreSensorOccurrenceData
+ *
+ * Since this is a one-shot sensor, after this event is delivered to the
+ * nanoapp, the sensor automatically goes into DONE mode.  Sensors of this
+ * type must be configured with a ONE_SHOT mode.
+ */
+#define CHRE_EVENT_SENSOR_SIGNIFICANT_MOTION_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_SIGNIFICANT_MOTION)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x', 'y', and 'z' fields within
+ * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z).
+ *
+ * All values are in radians/second and measure the rate of rotation
+ * around the X, Y and Z axis.
+ */
+#define CHRE_EVENT_SENSOR_GYROSCOPE_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_GYROSCOPE)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x', 'y', and 'z' fields within
+ * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z).
+ *
+ * All values are in micro-Tesla (uT) and measure the geomagnetic
+ * field in the X, Y and Z axis.
+ */
+#define CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorFloatData
+ *
+ * The data can be interpreted using the 'pressure' field within 'readings'.
+ * This value is in hectopascals (hPa).
+ */
+#define CHRE_EVENT_SENSOR_PRESSURE_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_PRESSURE)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorFloatData
+ *
+ * The data can be interpreted using the 'light' field within 'readings'.
+ * This value is in SI lux units.
+ */
+#define CHRE_EVENT_SENSOR_LIGHT_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_LIGHT)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorByteData
+ *
+ * The data is interpreted from the following fields in 'readings':
+ * o 'isNear': If set to 1, we are nearby (on the order of centimeters);
+ *       if set to 0, we are far. The meaning of near/far in this field must be
+ *       consistent with the Android definition.
+ * o 'invalid': If set to 1, this is not a valid reading of this data.
+ *       As of CHRE API v1.2, this field is deprecated and must always be set to
+ *       0.  If an invalid reading is generated by the sensor hardware, it must
+ *       be dropped and not delivered to any nanoapp.
+ *
+ * In prior versions of the CHRE API, there can be an invalid event generated
+ * upon configuring this sensor.  Thus, the 'invalid' field must be checked on
+ * the first event before interpreting 'isNear'.
+ */
+#define CHRE_EVENT_SENSOR_PROXIMITY_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_PROXIMITY)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorOccurrenceData
+ *
+ * This data is generated every time a step is taken by the user.
+ *
+ * This is backed by the same algorithm that feeds Android's
+ * SENSOR_TYPE_STEP_DETECTOR, and therefore sacrifices some accuracy to target
+ * an update latency of under 2 seconds.
+ *
+ * @since v1.3
+ */
+#define CHRE_EVENT_SENSOR_STEP_DETECT_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_STEP_DETECT)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorUint64Data
+ *
+ * The value of the data is the cumulative number of steps taken by the user
+ * since the last reboot while the sensor is active. This data is generated
+ * every time a step is taken by the user.
+ *
+ * This is backed by the same algorithm that feeds Android's
+ * SENSOR_TYPE_STEP_COUNTER, and therefore targets high accuracy with under
+ * 10 seconds of update latency.
+ *
+ * @since v1.5
+ */
+#define CHRE_EVENT_SENSOR_STEP_COUNTER_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_STEP_COUNTER)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorFloatData
+ *
+ * The value of the data is the measured hinge angle between 0 and 360 degrees
+ * inclusive.
+ *
+ * This is backed by the same algorithm that feeds Android's
+ * SENSOR_TYPE_HINGE_ANGLE.
+ *
+ * @since v1.5
+ */
+#define CHRE_EVENT_SENSOR_HINGE_ANGLE_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_HINGE_ANGLE)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x', 'y', and 'z' fields within
+ * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z).
+ *
+ * All values are in SI units (m/s^2) and measure the acceleration applied to
+ * the device.
+ */
+#define CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x', 'y', and 'z' fields within
+ * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z).
+ *
+ * All values are in radians/second and measure the rate of rotation
+ * around the X, Y and Z axis.
+ */
+#define CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x', 'y', and 'z' fields within
+ * 'readings', or by the 3D array 'v' (v[0] == x; v[1] == y; v[2] == z).
+ *
+ * All values are in micro-Tesla (uT) and measure the geomagnetic
+ * field in the X, Y and Z axis.
+ */
+#define CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorFloatData
+ *
+ * The data can be interpreted using the 'temperature' field within 'readings'.
+ * This value is in degrees Celsius.
+ */
+#define CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorFloatData
+ *
+ * The data can be interpreted using the 'temperature' field within 'readings'.
+ * This value is in degrees Celsius.
+ */
+#define CHRE_EVENT_SENSOR_GYROSCOPE_TEMPERATURE_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorFloatData
+ *
+ * The data can be interpreted using the 'temperature' field within 'readings'.
+ * This value is in degrees Celsius.
+ */
+#define CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_TEMPERATURE_DATA \
+    (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE)
+
+/**
+ * First value for sensor events which are not data from the sensor.
+ *
+ * Unlike the data event values, these other event values don't have any
+ * mapping to sensor types.
+ */
+#define CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE \
+    (CHRE_EVENT_SENSOR_FIRST_EVENT + 0x0100)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorSamplingStatusEvent
+ *
+ * Indicates that the interval and/or the latency which this sensor is
+ * sampling at has changed.
+ */
+#define CHRE_EVENT_SENSOR_SAMPLING_CHANGE \
+    (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 0)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x_bias', 'y_bias', and 'z_bias'
+ * field within 'readings', or by the 3D array 'bias' (bias[0] == x_bias;
+ * bias[1] == y_bias; bias[2] == z_bias). Bias is subtracted from uncalibrated
+ * data to generate calibrated data.
+ *
+ * All values are in radians/second and measure the rate of rotation
+ * around the X, Y and Z axis.
+ *
+ * If bias delivery is supported, this event is generated by default when
+ * chreSensorConfigure is called to enable for the sensor of type
+ * CHRE_SENSOR_TYPE_GYROSCOPE, or if bias delivery is explicitly enabled
+ * through chreSensorConfigureBiasEvents() for the sensor.
+ */
+#define CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO \
+    (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 1)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x_bias', 'y_bias', and 'z_bias'
+ * field within 'readings', or by the 3D array 'bias' (bias[0] == x_bias;
+ * bias[1] == y_bias; bias[2] == z_bias). Bias is subtracted from uncalibrated
+ * data to generate calibrated data.
+ *
+ * All values are in micro-Tesla (uT) and measure the geomagnetic
+ * field in the X, Y and Z axis.
+ *
+ * If bias delivery is supported, this event is generated by default when
+ * chreSensorConfigure is called to enable for the sensor of type
+ * CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD, or if bias delivery is explicitly enabled
+ * through chreSensorConfigureBiasEvents() for the sensor.
+ */
+#define CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO \
+    (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 2)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data can be interpreted using the 'x_bias', 'y_bias', and 'z_bias'
+ * field within 'readings', or by the 3D array 'bias' (bias[0] == x_bias;
+ * bias[1] == y_bias; bias[2] == z_bias). Bias is subtracted from uncalibrated
+ * data to generate calibrated data.
+ *
+ * All values are in SI units (m/s^2) and measure the acceleration applied to
+ * the device.
+ *
+ * If bias delivery is supported, this event is generated by default when
+ * chreSensorConfigure is called to enable for the sensor of type
+ * CHRE_SENSOR_TYPE_ACCELEROMETER, or if bias delivery is explicitly enabled
+ * through chreSensorConfigureBiasEvents() for the sensor.
+ *
+ * @since v1.3
+ */
+#define CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO \
+    (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 3)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorFlushCompleteEvent
+ *
+ * An event indicating that a flush request made by chreSensorFlushAsync has
+ * completed.
+ *
+ * @see chreSensorFlushAsync
+ *
+ * @since v1.3
+ */
+#define CHRE_EVENT_SENSOR_FLUSH_COMPLETE \
+    (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 4)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data of this event is the same as that of
+ * CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO, except the sensorHandle field of
+ * chreSensorDataHeader contains the handle of the sensor of type
+ * CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE.
+ *
+ * This event is only generated if the bias reporting is explicitly enabled
+ * for a nanoapp through chreSensorConfigureBiasEvents() for the sensor of type
+ * CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE.
+ *
+ * @see CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO
+ *
+ * @since v1.3
+ */
+#define CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_BIAS_INFO \
+    (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 5)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data of this event is the same as that of
+ * CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO, except the sensorHandle field
+ * of chreSensorDataHeader contains the handle of the sensor of type
+ * CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD.
+ *
+ * This event is only generated if the bias reporting is explicitly enabled
+ * for a nanoapp through chreSensorConfigureBiasEvents() for the sensor of type
+ * CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD.
+ *
+ * @see CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO
+ *
+ * @since v1.3
+ */
+#define CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_BIAS_INFO \
+    (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 6)
+
+/**
+ * nanoappHandleEvent argument: struct chreSensorThreeAxisData
+ *
+ * The data of this event is the same as that of
+ * CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO, except the sensorHandle field
+ * of chreSensorDataHeader contains the handle of the sensor of type
+ * CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER.
+ *
+ * This event is only generated if the bias reporting is explicitly enabled
+ * for a nanoapp through chreSensorConfigureBiasEvents for the sensor of type
+ * CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER.
+ *
+ * @see CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO
+ *
+ * @since v1.3
+ */
+#define CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_BIAS_INFO \
+    (CHRE_EVENT_SENSOR_OTHER_EVENTS_BASE + 7)
+
+#if CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_BIAS_INFO > \
+    CHRE_EVENT_SENSOR_LAST_EVENT
+#error Too many sensor events.
+#endif
+
+/**
+ * Value indicating we want the smallest possible latency for a sensor.
+ *
+ * This literally translates to 0 nanoseconds for the chreSensorConfigure()
+ * argument.  While we won't get exactly 0 nanoseconds, the CHRE will
+ * queue up this event As Soon As Possible.
+ */
+#define CHRE_SENSOR_LATENCY_ASAP  UINT64_C(0)
+
+/**
+ * Special value indicating non-importance, or non-applicability of the sampling
+ * interval.
+ *
+ * @see chreSensorConfigure
+ * @see chreSensorSamplingStatus
+ */
+#define CHRE_SENSOR_INTERVAL_DEFAULT  UINT64_C(-1)
+
+/**
+ * Special value indicating non-importance of the latency.
+ *
+ * @see chreSensorConfigure
+ * @see chreSensorSamplingStatus
+ */
+#define CHRE_SENSOR_LATENCY_DEFAULT  UINT64_C(-1)
+
+/**
+ * A sensor index value indicating that it is the default sensor.
+ *
+ * @see chreSensorFind
+ */
+#define CHRE_SENSOR_INDEX_DEFAULT  UINT8_C(0)
+
+/**
+ * Special value indicating non-importance of the batch interval.
+ *
+ * @see chreSensorConfigureWithBatchInterval
+ */
+#define CHRE_SENSOR_BATCH_INTERVAL_DEFAULT  UINT64_C(-1)
+
+// This is used to define elements of enum chreSensorConfigureMode.
+#define CHRE_SENSOR_CONFIGURE_RAW_POWER_ON           (1 << 0)
+
+// This is used to define elements of enum chreSensorConfigureMode.
+#define CHRE_SENSOR_CONFIGURE_RAW_REPORT_CONTINUOUS  (1 << 1)
+
+// This is used to define elements of enum chreSensorConfigureMode.
+#define CHRE_SENSOR_CONFIGURE_RAW_REPORT_ONE_SHOT    (2 << 1)
+
+/**
+ * The maximum amount of time allowed to elapse between the call to
+ * chreSensorFlushAsync() and when CHRE_EVENT_SENSOR_FLUSH_COMPLETE is delivered
+ * to the nanoapp on a successful flush.
+ */
+#define CHRE_SENSOR_FLUSH_COMPLETE_TIMEOUT_NS  (5 * CHRE_NSEC_PER_SEC)
+
+/**
+ * Modes we can configure a sensor to use.
+ *
+ * Our mode will affect not only how/if we receive events, but
+ * also whether or not the sensor will be powered on our behalf.
+ *
+ * @see chreSensorConfigure
+ */
+enum chreSensorConfigureMode {
+    /**
+     * Get events from the sensor.
+     *
+     * Power: Turn on if not already on.
+     * Reporting: Continuous.  Send each new event as it comes (subject to
+     *     batching and latency).
+     */
+    CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS =
+        (CHRE_SENSOR_CONFIGURE_RAW_POWER_ON |
+         CHRE_SENSOR_CONFIGURE_RAW_REPORT_CONTINUOUS),
+
+    /**
+     * Get a single event from the sensor and then become DONE.
+     *
+     * Once the event is sent, the sensor automatically
+     * changes to CHRE_SENSOR_CONFIGURE_MODE_DONE mode.
+     *
+     * Power: Turn on if not already on.
+     * Reporting: One shot.  Send the next event and then be DONE.
+     */
+    CHRE_SENSOR_CONFIGURE_MODE_ONE_SHOT =
+        (CHRE_SENSOR_CONFIGURE_RAW_POWER_ON |
+         CHRE_SENSOR_CONFIGURE_RAW_REPORT_ONE_SHOT),
+
+    /**
+     * Get events from a sensor that are generated for any client in the system.
+     *
+     * This is considered passive because the sensor will not be powered on for
+     * the sake of our nanoapp.  If and only if another client in the system has
+     * requested this sensor power on will we get events.
+     *
+     * This can be useful for something which is interested in seeing data, but
+     * not interested enough to be responsible for powering on the sensor.
+     *
+     * Power: Do not power the sensor on our behalf.
+     * Reporting: Continuous.  Send each event as it comes.
+     */
+    CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_CONTINUOUS =
+        CHRE_SENSOR_CONFIGURE_RAW_REPORT_CONTINUOUS,
+
+    /**
+     * Get a single event from a sensor that is generated for any client in the
+     * system.
+     *
+     * See CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_CONTINUOUS for more details on
+     * what the "passive" means.
+     *
+     * Power: Do not power the sensor on our behalf.
+     * Reporting: One shot.  Send only the next event and then be DONE.
+     */
+    CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_ONE_SHOT =
+        CHRE_SENSOR_CONFIGURE_RAW_REPORT_ONE_SHOT,
+
+    /**
+     * Indicate we are done using this sensor and no longer interested in it.
+     *
+     * See chreSensorConfigure for more details on expressing interest or
+     * lack of interest in a sensor.
+     *
+     * Power: Do not power the sensor on our behalf.
+     * Reporting: None.
+     */
+    CHRE_SENSOR_CONFIGURE_MODE_DONE = 0,
+};
+
+/**
+ * A structure containing information about a Sensor.
+ *
+ * See documentation of individual fields below.
+ */
+struct chreSensorInfo {
+    /**
+     * The name of the sensor.
+     *
+     * A text name, useful for logging/debugging, describing the Sensor.  This
+     * is not assured to be unique (i.e. there could be multiple sensors with
+     * the name "Temperature").
+     *
+     * CHRE implementations may not set this as NULL.  An empty
+     * string, while discouraged, is legal.
+     */
+    const char *sensorName;
+
+    /**
+     * One of the CHRE_SENSOR_TYPE_* defines above.
+     */
+    uint8_t sensorType;
+
+    /**
+     * Flag indicating if this sensor is on-change.
+     *
+     * An on-change sensor only generates events when underlying state
+     * changes.  This has the same meaning as on-change does in the Android
+     * Sensors HAL.  See sensors.h for much more details.
+     *
+     * A value of 1 indicates this is on-change.  0 indicates this is not
+     * on-change.
+     */
+    uint8_t isOnChange          : 1;
+
+    /**
+     * Flag indicating if this sensor is one-shot.
+     *
+     * A one-shot sensor only triggers a single event, and then automatically
+     * disables itself.
+     *
+     * A value of 1 indicates this is one-shot.  0 indicates this is not
+     * on-change.
+     */
+    uint8_t isOneShot           : 1;
+
+    /**
+     * Flag indicating if this sensor supports reporting bias info events.
+     *
+     * This field will be set to 0 when running on CHRE API versions prior to
+     * v1.3, but must be ignored (i.e. does not mean bias info event is not
+     * supported).
+     *
+     * @see chreSensorConfigureBiasEvents
+     *
+     * @since v1.3
+     */
+    uint8_t reportsBiasEvents   : 1;
+
+    /**
+     * Flag indicating if this sensor supports passive mode requests.
+     *
+     * This field will be set to 0 when running on CHRE API versions prior to
+     * v1.4, and must be ignored (i.e. does not mean passive mode requests are
+     * not supported).
+     *
+     * @see chreSensorConfigure
+     *
+     * @since v1.4
+     */
+    uint8_t supportsPassiveMode : 1;
+
+    uint8_t unusedFlags         : 4;
+
+    /**
+     * The minimum sampling interval supported by this sensor, in nanoseconds.
+     *
+     * Requests to chreSensorConfigure with a lower interval than this will
+     * fail.  If the sampling interval is not applicable to this sensor, this
+     * will be set to CHRE_SENSOR_INTERVAL_DEFAULT.
+     *
+     * This field will be set to 0 when running on CHRE API versions prior to
+     * v1.1, indicating that the minimum interval is not known.
+     *
+     * @since v1.1
+     */
+    uint64_t minInterval;
+
+    /**
+     * Uniquely identifies the sensor for a given type. A value of 0 indicates
+     * that this is the "default" sensor, which is returned by
+     * chreSensorFindDefault().
+     *
+     * The sensor index of a given type must be stable across boots (i.e. must
+     * not change), and a different sensor of the same type must have different
+     * sensor index values, and the set of sensorIndex values for a given sensor
+     * type must be continuguous.
+     *
+     * @since v1.5
+     */
+    uint8_t sensorIndex;
+};
+
+/**
+ * The status of a sensor's sampling configuration.
+ */
+struct chreSensorSamplingStatus {
+    /**
+     * The interval, in nanoseconds, at which sensor data is being sampled at.
+     * This should be used by nanoapps to determine the rate at which samples
+     * will be generated and not to indicate what the sensor is truly sampling
+     * at since resampling may occur to limit incoming data.
+     *
+     * If this is CHRE_SENSOR_INTERVAL_DEFAULT, then a sampling interval
+     * isn't meaningful for this sensor.
+     *
+     * Note that if 'enabled' is false, this value is not meaningful.
+     */
+    uint64_t interval;
+
+    /**
+     * The latency, in nanoseconds, at which the sensor is now reporting.
+     *
+     * If this is CHRE_SENSOR_LATENCY_DEFAULT, then a latency
+     * isn't meaningful for this sensor.
+     *
+     * The effective batch interval can be derived from this value by
+     * adding the current sampling interval.
+     *
+     * Note that if 'enabled' is false, this value is not meaningful.
+     */
+    uint64_t latency;
+
+    /**
+     * True if the sensor is actively powered and sampling; false otherwise.
+     */
+    bool enabled;
+};
+
+/**
+ * The nanoappHandleEvent argument for CHRE_EVENT_SENSOR_SAMPLING_CHANGE.
+ *
+ * Note that only at least one of 'interval' or 'latency' must be
+ * different than it was prior to this event.  Thus, one of these
+ * fields may be (but doesn't need to be) the same as before.
+ */
+struct chreSensorSamplingStatusEvent {
+    /**
+     * The handle of the sensor which has experienced a change in sampling.
+     */
+    uint32_t sensorHandle;
+
+    /**
+     * The new sampling status.
+     *
+     * At least one of the field in this struct will be different from
+     * the previous sampling status event.
+     */
+    struct chreSensorSamplingStatus status;
+};
+
+/**
+ * The nanoappHandleEvent argument for CHRE_EVENT_SENSOR_FLUSH_COMPLETE.
+ *
+ * @see chreSensorFlushAsync
+ *
+ * @since v1.3
+ */
+struct chreSensorFlushCompleteEvent {
+    /**
+     * The handle of the sensor which a flush was completed.
+     */
+    uint32_t sensorHandle;
+
+    /**
+     * Populated with a value from enum {@link #chreError}, indicating whether
+     * the flush failed, and if so, provides the cause of the failure.
+     */
+    uint8_t errorCode;
+
+    /**
+     * Reserved for future use. Set to 0.
+     */
+    uint8_t reserved[3];
+
+    /**
+     * Set to the cookie parameter given to chreSensorFlushAsync.
+     */
+    const void *cookie;
+};
+
+/**
+ * Find the default sensor for a given sensor type.
+ *
+ * @param sensorType One of the CHRE_SENSOR_TYPE_* constants.
+ * @param handle  If a sensor is found, then the memory will be filled with
+ *     the value for the sensor's handle.  This argument must be non-NULL.
+ * @return true if a sensor was found, false otherwise.
+ */
+bool chreSensorFindDefault(uint8_t sensorType, uint32_t *handle);
+
+/**
+ * Finds a sensor of a given index and sensor type.
+ *
+ * For CHRE implementations that support multiple sensors of the same sensor
+ * type, this method can be used to get the non-default sensor(s). The default
+ * sensor, as defined in the chreSensorFindDefault(), will be returned if
+ * a sensor index of zero is specified.
+ *
+ * A simple example of iterating all available sensors of a given type is
+ * provided here:
+ *
+ * uint32_t handle;
+ * for (uint8_t i = 0; chreSensorFind(sensorType, i, &handle); i++) {
+ *   chreLog(CHRE_LOG_INFO,
+ *           "Found sensor index %" PRIu8 ", which has handle %" PRIu32,
+ *           i, handle);
+ * }
+ *
+ * If this method is invoked for CHRE versions prior to v1.5, invocations with
+ * sensorIndex value of 0 will be equivalent to using chreSensorFindDefault, and
+ * if sensorIndex is non-zero will return false.
+ *
+ * In cases where multiple sensors are supported in both the Android sensors
+ * framework and CHRE, the sensorName of the chreSensorInfo struct for a given
+ * sensor instance must match exactly with that of the
+ * android.hardware.Sensor#getName() return value. This can be used to match a
+ * sensor instance between the Android and CHRE sensors APIs.
+ *
+ * @param sensorType One of the CHRE_SENSOR_TYPE_* constants.
+ * @param sensorIndex The index of the desired sensor.
+ * @param handle  If a sensor is found, then the memory will be filled with
+ *     the value for the sensor's handle.  This argument must be non-NULL.
+ * @return true if a sensor was found, false otherwise.
+ *
+ * @since v1.5
+ */
+bool chreSensorFind(uint8_t sensorType, uint8_t sensorIndex, uint32_t *handle);
+
+/**
+ * Get the chreSensorInfo struct for a given sensor.
+ *
+ * @param sensorHandle  The sensor handle, as obtained from
+ *     chreSensorFindDefault() or passed to nanoappHandleEvent().
+ * @param info  If the sensor is valid, then this memory will be filled with
+ *     the SensorInfo contents for this sensor.  This argument must be
+ *     non-NULL.
+ * @return true if the senor handle is valid and 'info' was filled in;
+ *     false otherwise.
+ */
+bool chreGetSensorInfo(uint32_t sensorHandle, struct chreSensorInfo *info);
+
+/**
+ * Get the chreSensorSamplingStatus struct for a given sensor.
+ *
+ * Note that this may be different from what was requested in
+ * chreSensorConfigure(), for multiple reasons.  It's possible that the sensor
+ * does not exactly support the interval requested in chreSensorConfigure(), so
+ * a faster one was chosen.
+ *
+ * It's also possible that there is another user of this sensor who has
+ * requested a faster interval and/or lower latency.  This latter scenario
+ * should be noted, because it means the sensor rate can change due to no
+ * interaction from this nanoapp.  Note that the
+ * CHRE_EVENT_SENSOR_SAMPLING_CHANGE event will trigger in this case, so it's
+ * not necessary to poll for such a change.
+ *
+ * This function must return a valid status if the provided sensor is being
+ * actively sampled by a nanoapp and a CHRE_EVENT_SENSOR_SAMPLING_CHANGE has
+ * been delivered indicating their request has taken effect. It is not required
+ * to return a valid status if no nanoapp is actively sampling the sensor.
+ *
+ * @param sensorHandle  The sensor handle, as obtained from
+ *     chreSensorFindDefault() or passed to nanoappHandleEvent().
+ * @param status  If the sensor is actively enabled by a nanoapp, then this
+ *     memory must be filled with the sampling status contents for this sensor.
+ *     This argument must be non-NULL.
+ * @return true if the sensor handle is valid and 'status' was filled in;
+ *     false otherwise.
+ */
+bool chreGetSensorSamplingStatus(uint32_t sensorHandle,
+                                 struct chreSensorSamplingStatus *status);
+
+/**
+ * Configures a given sensor at a specific interval and latency and mode.
+ *
+ * If this sensor's chreSensorInfo has isOneShot set to 1,
+ * then the mode must be one of the ONE_SHOT modes, or this method will fail.
+ *
+ * The CHRE wants to power as few sensors as possible, in keeping with its
+ * low power design.  As such, it only turns on sensors when there are clients
+ * actively interested in that sensor data, and turns off sensors as soon as
+ * there are no clients interested in them.  Calling this method generally
+ * indicates an interest, and using CHRE_SENSOR_CONFIGURE_MODE_DONE shows
+ * when we are no longer interested.
+ *
+ * Thus, each initial Configure of a sensor (per nanoapp) needs to eventually
+ * have a DONE call made, either directly or on its behalf.  Subsequent calls
+ * to a Configure method within the same nanoapp, when there has been no DONE
+ * in between, still only require a single DONE call.
+ *
+ * For example, the following is valid usage:
+ * <code>
+ *   chreSensorConfigure(myHandle, mode, interval0, latency0);
+ *   [...]
+ *   chreSensorConfigure(myHandle, mode, interval1, latency0);
+ *   [...]
+ *   chreSensorConfigure(myHandle, mode, interval1, latency1);
+ *   [...]
+ *   chreSensorConfigureModeOnly(myHandle, CHRE_SENSOR_CONFIGURE_MODE_DONE);
+ * </code>
+ *
+ * The first call to Configure is the one which creates the requirement
+ * to eventually call with DONE.  The subsequent calls are just changing the
+ * interval/latency.  They have not changed the fact that this nanoapp is
+ * still interested in output from the sensor 'myHandle'.  Thus, only one
+ * single call for DONE is needed.
+ *
+ * There is a special case.  One-shot sensors, sensors which
+ * just trigger a single event and never trigger again, implicitly go into
+ * DONE mode after that single event triggers.  Thus, the
+ * following are legitimate usages:
+ * <code>
+ *   chreSensorConfigure(myHandle, MODE_ONE_SHOT, interval, latency);
+ *   [...]
+ *   [myHandle triggers an event]
+ *   [no need to configure to DONE].
+ * </code>
+ *
+ * And:
+ * <code>
+ *   chreSensorConfigure(myHandle, MODE_ONE_SHOT, interval, latency);
+ *   [...]
+ *   chreSensorConfigureModeOnly(myHandle, MODE_DONE);
+ *   [we cancelled myHandle before it ever triggered an event]
+ * </code>
+ *
+ * Note that while PASSIVE modes, by definition, don't express an interest in
+ * powering the sensor, DONE is still necessary to silence the event reporting.
+ * Starting with CHRE API v1.4, for sensors that do not support passive mode, a
+ * request with mode set to CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_CONTINUOUS or
+ * CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_ONE_SHOT will be rejected. CHRE API
+ * versions 1.3 and older implicitly assume that passive mode is supported
+ * across all sensors, however this is not necessarily the case. Clients can
+ * call chreSensorInfo to identify whether a sensor supports passive mode.
+ *
+ * When a calibrated sensor (e.g. CHRE_SENSOR_TYPE_ACCELEROMETER) is
+ * successfully enabled through this method and if bias delivery is supported,
+ * by default CHRE will start delivering bias events for the sensor
+ * (e.g. CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO) to the nanoapp. If the
+ * nanoapp does not wish to receive these events, they can be disabled through
+ * chreSensorConfigureBiasEvents after enabling the sensor.
+ *
+ * @param sensorHandle  The handle to the sensor, as obtained from
+ *     chreSensorFindDefault().
+ * @param mode  The mode to use.  See descriptions within the
+ *     chreSensorConfigureMode enum.
+ * @param interval  The interval, in nanoseconds, at which we want events from
+ *     the sensor.  On success, the sensor will be set to 'interval', or a value
+ *     less than 'interval'.  There is a special value
+ *     CHRE_SENSOR_INTERVAL_DEFAULT, in which we don't express a preference for
+ *     the interval, and allow the sensor to choose what it wants.  Note that
+ *     due to batching, we may receive events less frequently than
+ *     'interval'.
+ * @param latency  The maximum latency, in nanoseconds, allowed before the
+ *     CHRE begins delivery of an event.  This will control how many events
+ *     can be queued by the sensor before requiring a delivery event.
+ *     Latency is defined as the "timestamp when event is queued by the CHRE"
+ *     minus "timestamp of oldest unsent data reading".
+ *     There is a special value CHRE_SENSOR_LATENCY_DEFAULT, in which we don't
+ *     express a preference for the latency, and allow the sensor to choose what
+ *     it wants.
+ *     Note that there is no assurance of how long it will take an event to
+ *     get through a CHRE's queueing system, and thus there is no ability to
+ *     request a minimum time from the occurrence of a phenomenon to when the
+ *     nanoapp receives the information.  The current CHRE API has no
+ *     real-time elements, although future versions may introduce some to
+ *     help with this issue.
+ * @return true if the configuration succeeded, false otherwise.
+ *
+ * @see chreSensorConfigureMode
+ * @see chreSensorFindDefault
+ * @see chreSensorInfo
+ * @see chreSensorConfigureBiasEvents
+ */
+bool chreSensorConfigure(uint32_t sensorHandle,
+                         enum chreSensorConfigureMode mode,
+                         uint64_t interval, uint64_t latency);
+
+/**
+ * Short cut for chreSensorConfigure where we only want to configure the mode
+ * and do not care about interval/latency.
+ *
+ * @see chreSensorConfigure
+ */
+static inline bool chreSensorConfigureModeOnly(
+        uint32_t sensorHandle, enum chreSensorConfigureMode mode) {
+    return chreSensorConfigure(sensorHandle,
+                               mode,
+                               CHRE_SENSOR_INTERVAL_DEFAULT,
+                               CHRE_SENSOR_LATENCY_DEFAULT);
+}
+
+/**
+ * Convenience function that wraps chreSensorConfigure but enables batching to
+ * be controlled by specifying the desired maximum batch interval rather
+ * than maximum sample latency.  Users may find the batch interval to be a more
+ * intuitive method of expressing the desired batching behavior.
+ *
+ * Batch interval is different from latency as the batch interval time is
+ * counted starting when the prior event containing a batch of sensor samples is
+ * delivered, while latency starts counting when the first sample is deferred to
+ * start collecting a batch.  In other words, latency ignores the time between
+ * the last sample in a batch to the first sample of the next batch, while it's
+ * included in the batch interval, as illustrated below.
+ *
+ *  Time      0   1   2   3   4   5   6   7   8
+ *  Batch             A           B           C
+ *  Sample   a1  a2  a3  b1  b2  b3  c1  c2  c3
+ *  Latency  [        ]  [        ]  [        ]
+ *  BatchInt          |           |           |
+ *
+ * In the diagram, the effective sample interval is 1 time unit, latency is 2
+ * time units, and batch interval is 3 time units.
+ *
+ * @param sensorHandle See chreSensorConfigure#sensorHandle
+ * @param mode See chreSensorConfigure#mode
+ * @param sampleInterval See chreSensorConfigure#interval, but note that
+ *     CHRE_SENSOR_INTERVAL_DEFAULT is not a supported input to this method.
+ * @param batchInterval The desired maximum interval, in nanoseconds, between
+ *     CHRE enqueuing each batch of sensor samples.
+ * @return Same as chreSensorConfigure
+ *
+ * @see chreSensorConfigure
+ *
+ * @since v1.1
+ */
+static inline bool chreSensorConfigureWithBatchInterval(
+        uint32_t sensorHandle, enum chreSensorConfigureMode mode,
+        uint64_t sampleInterval, uint64_t batchInterval) {
+    bool result = false;
+
+    if (sampleInterval != CHRE_SENSOR_INTERVAL_DEFAULT) {
+        uint64_t latency;
+        if (batchInterval == CHRE_SENSOR_BATCH_INTERVAL_DEFAULT) {
+            latency = CHRE_SENSOR_LATENCY_DEFAULT;
+        } else if (batchInterval > sampleInterval) {
+            latency = batchInterval - sampleInterval;
+        } else {
+            latency = CHRE_SENSOR_LATENCY_ASAP;
+        }
+        result = chreSensorConfigure(sensorHandle, mode, sampleInterval,
+                                     latency);
+    }
+
+    return result;
+}
+
+/**
+ * Configures the reception of bias events for a specific sensor.
+ *
+ * If bias event delivery is supported for a sensor, the sensor's chreSensorInfo
+ * has reportsBiasEvents set to 1. If supported, it must be supported for both
+ * calibrated and uncalibrated versions of the sensor. If supported, CHRE must
+ * provide bias events to the nanoapp by default when chreSensorConfigure is
+ * called to enable the calibrated version of the sensor (for backwards
+ * compatibility reasons, as this is the defined behavior for CHRE API v1.0).
+ * When configuring uncalibrated sensors, nanoapps must explicitly configure an
+ * enable request through this method to receive bias events. If bias event
+ * delivery is not supported for the sensor, this method will return false and
+ * no bias events will be generated.
+ *
+ * To enable bias event delivery (enable=true), the nanoapp must be registered
+ * to the sensor through chreSensorConfigure, and bias events will only be
+ * generated when the sensor is powered on. To disable the bias event delivery,
+ * this method can be invoked with enable=false.
+ *
+ * If an enable configuration is successful, the calling nanoapp will receive
+ * bias info events, e.g. CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO, when the
+ * bias status changes (or first becomes available). Calibrated data
+ * (e.g. CHRE_SENSOR_TYPE_ACCELEROMETER) is generated by subracting bias from
+ * uncalibrated data (e.g. CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER).
+ * Calibrated sensor events are generated by applying the most recent bias
+ * available (i.e. timestamp of calibrated data are greater than or equal to the
+ * timestamp of the bias data that has been applied to it). The configuration of
+ * bias event delivery persists until the sensor is unregistered by the nanoapp
+ * through chreSensorConfigure or modified through this method.
+ *
+ * To get an initial bias before new bias events, the nanoapp should get the
+ * bias synchronously after this method is invoked, e.g.:
+ *
+ * if (chreSensorConfigure(handle, ...)) {
+ *   chreSensorConfigureBiasEvents(handle, true);
+ *   chreSensorGetThreeAxisBias(handle, &bias);
+ * }
+ *
+ * Note that chreSensorGetThreeAxisBias() should be called after
+ * chreSensorConfigureBiasEvents() to ensure that no bias events are lost.
+ *
+ * If called while running on a CHRE API version below v1.3, this function
+ * returns false and has no effect. The default behavior regarding bias events
+ * is unchanged, meaning that the implementation may still send bias events
+ * when a calibrated sensor is registered (if supported), and will not send bias
+ * events when an uncalibrated sensor is registered.
+ *
+ * @param sensorHandle The handle to the sensor, as obtained from
+ *     chreSensorFindDefault().
+ * @param enable true to receive bias events, false otherwise
+ *
+ * @return true if the configuration succeeded, false otherwise
+ *
+ * @since v1.3
+ */
+bool chreSensorConfigureBiasEvents(uint32_t sensorHandle, bool enable);
+
+/**
+ * Synchronously provides the most recent bias info available for a sensor. The
+ * bias will only be provided for a sensor that supports bias event delivery
+ * using the chreSensorThreeAxisData type. If the bias is not yet available
+ * (but is supported), this method will store data with a bias of 0 and the
+ * accuracy field in chreSensorDataHeader set to CHRE_SENSOR_ACCURACY_UNKNOWN.
+ *
+ * If called while running on a CHRE API version below v1.3, this function
+ * returns false.
+ *
+ * @param sensorHandle The handle to the sensor, as obtained from
+ *     chreSensorFindDefault().
+ * @param bias A pointer to where the bias will be stored.
+ *
+ * @return true if the bias was successfully stored, false if sensorHandle was
+ *     invalid or the sensor does not support three axis bias delivery
+ *
+ * @since v1.3
+ *
+ * @see chreSensorConfigureBiasEvents
+ */
+bool chreSensorGetThreeAxisBias(uint32_t sensorHandle,
+                                struct chreSensorThreeAxisData *bias);
+
+/**
+ * Makes a request to flush all samples stored for batching. The nanoapp must be
+ * registered to the sensor through chreSensorConfigure, and the sensor must be
+ * powered on. If the request is accepted, all batched samples of the sensor
+ * are sent to nanoapps registered to the sensor. During a flush, it is treated
+ * as though the latency as given in chreSensorConfigure has expired. When all
+ * batched samples have been flushed (or the flush fails), the nanoapp will
+ * receive a unicast CHRE_EVENT_SENSOR_FLUSH_COMPLETE event. The time to deliver
+ * this event must not exceed CHRE_SENSOR_FLUSH_COMPLETE_TIMEOUT_NS after this
+ * method is invoked. If there are no samples in the batch buffer (either in
+ * hardware FIFO or software), then this method will return true and a
+ * CHRE_EVENT_SENSOR_FLUSH_COMPLETE event is delivered immediately.
+ *
+ * If a flush request is invalid (e.g. the sensor refers to a one-shot sensor,
+ * or the sensor was not enabled), and this API will return false and no
+ * CHRE_EVENT_SENSOR_FLUSH_COMPLETE event will be delivered.
+ *
+ * If multiple flush requests are made for a sensor prior to flush completion,
+ * then the requesting nanoapp will receive all batched samples existing at the
+ * time of the latest flush request. In this case, the number of
+ * CHRE_EVENT_SENSOR_FLUSH_COMPLETE events received must equal the number of
+ * flush requests made.
+ *
+ * If a sensor request is disabled after a flush request is made through this
+ * method but before the flush operation is completed, the nanoapp will receive
+ * a CHRE_EVENT_SENSOR_FLUSH_COMPLETE with the error code
+ * CHRE_ERROR_FUNCTION_DISABLED for any pending flush requests.
+ *
+ * Starting with CHRE API v1.3, implementations must support this capability
+ * across all exposed sensor types.
+ *
+ * @param sensorHandle  The handle to the sensor, as obtained from
+ *     chreSensorFindDefault().
+ * @param cookie  An opaque value that will be included in the
+ *     chreSensorFlushCompleteEvent sent in relation to this request.
+ *
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.3
+ */
+bool chreSensorFlushAsync(uint32_t sensorHandle, const void *cookie);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_SENSOR_H_ */
diff --git a/chre_api/legacy/v1_10/chre/sensor_types.h b/chre_api/legacy/v1_10/chre/sensor_types.h
new file mode 100644
index 0000000..6b46a22
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/sensor_types.h
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_SENSOR_TYPES_H_
+#define _CHRE_SENSOR_TYPES_H_
+
+/**
+ * @file
+ * Standalone definition of sensor types, and the data structures of the sample
+ * events they emit.
+ */
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * The CHRE_SENSOR_TYPE_* defines are the sensor types supported.
+ *
+ * Unless otherwise noted, each of these sensor types is based off of a
+ * corresponding sensor type in the Android API's sensors.h interface.
+ * For a given CHRE_SENSOR_TYPE_FOO, it corresponds to the SENSOR_TYPE_FOO in
+ * hardware/libhardware/include/hardware/sensors.h of the Android code base.
+ *
+ * Unless otherwise noted below, a CHRE_SENSOR_TYPE_FOO should be assumed
+ * to work the same as the Android SENSOR_TYPE_FOO, as documented in the
+ * sensors.h documentation and as detailed within the Android Compatibility
+ * Definition Document.
+ *
+ * Note that every sensor will generate CHRE_EVENT_SENSOR_SAMPLING_CHANGE
+ * events, so it is not listed with each individual sensor.
+ */
+
+/**
+ * Start value for all of the vendor-defined private sensors.
+ *
+ * @since v1.2
+ */
+#define CHRE_SENSOR_TYPE_VENDOR_START  UINT8_C(0xC0)
+
+/**
+ * Accelerometer.
+ *
+ * Generates: CHRE_EVENT_SENSOR_ACCELEROMETER_DATA and
+ *     optionally CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO
+ *
+ * Note that the ACCELEROMETER_DATA is always the fully calibrated data,
+ * including factory calibration and runtime calibration if available.
+ *
+ * @see chreConfigureSensorBiasEvents
+ */
+#define CHRE_SENSOR_TYPE_ACCELEROMETER  UINT8_C(0x01)
+
+/**
+ * Instantaneous motion detection.
+ *
+ * Generates: CHRE_EVENT_SENSOR_INSTANT_MOTION_DETECT_DATA
+ *
+ * This is a one-shot sensor.
+ *
+ * This does not have a direct analogy within sensors.h.  This is similar
+ * to SENSOR_TYPE_MOTION_DETECT, but this triggers instantly upon any
+ * motion, instead of waiting for a period of continuous motion.
+ */
+#define CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT  UINT8_C(0x02)
+
+/**
+ * Stationary detection.
+ *
+ * Generates: CHRE_EVENT_SENSOR_STATIONARY_DETECT_DATA
+ *
+ * This is a one-shot sensor.
+ */
+#define CHRE_SENSOR_TYPE_STATIONARY_DETECT  UINT8_C(0x03)
+
+/**
+ * Gyroscope.
+ *
+ * Generates: CHRE_EVENT_SENSOR_GYROSCOPE_DATA and
+ *     optionally CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO
+ *
+ * Note that the GYROSCOPE_DATA is always the fully calibrated data, including
+ * factory calibration and runtime calibration if available.
+ *
+ * @see chreConfigureSensorBiasEvents
+ */
+#define CHRE_SENSOR_TYPE_GYROSCOPE  UINT8_C(0x06)
+
+/**
+ * Uncalibrated gyroscope.
+ *
+ * Generates: CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA
+ *
+ * Note that the UNCALIBRATED_GYROSCOPE_DATA must be factory calibrated data,
+ * but not runtime calibrated.
+ */
+#define CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE  UINT8_C(0x07)
+
+/**
+ * Magnetometer.
+ *
+ * Generates: CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_DATA and
+ *     optionally CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO
+ *
+ * Note that the GEOMAGNETIC_FIELD_DATA is always the fully calibrated data,
+ * including factory calibration and runtime calibration if available.
+ *
+ * @see chreConfigureSensorBiasEvents
+ */
+#define CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD  UINT8_C(0x08)
+
+/**
+ * Uncalibrated magnetometer.
+ *
+ * Generates: CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA
+ *
+ * Note that the UNCALIBRATED_GEOMAGNETIC_FIELD_DATA must be factory calibrated
+ * data, but not runtime calibrated.
+ */
+#define CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD  UINT8_C(0x09)
+
+/**
+ * Barometric pressure sensor.
+ *
+ * Generates: CHRE_EVENT_SENSOR_PRESSURE_DATA
+ */
+#define CHRE_SENSOR_TYPE_PRESSURE  UINT8_C(0x0A)
+
+/**
+ * Ambient light sensor.
+ *
+ * Generates: CHRE_EVENT_SENSOR_LIGHT_DATA
+ *
+ * This is an on-change sensor.
+ */
+#define CHRE_SENSOR_TYPE_LIGHT  UINT8_C(0x0C)
+
+/**
+ * Proximity detection.
+ *
+ * Generates: CHRE_EVENT_SENSOR_PROXIMITY_DATA
+ *
+ * This is an on-change sensor.
+ */
+#define CHRE_SENSOR_TYPE_PROXIMITY  UINT8_C(0x0D)
+
+/**
+ * Step detection.
+ *
+ * Generates: CHRE_EVENT_SENSOR_STEP_DETECT_DATA
+ *
+ * @since v1.3
+ */
+#define CHRE_SENSOR_TYPE_STEP_DETECT  UINT8_C(0x17)
+
+/**
+ * Step counter.
+ *
+ * Generates: CHRE_EVENT_SENSOR_STEP_COUNTER_DATA
+ *
+ * This is an on-change sensor. Note that the data returned by this sensor must
+ * match the value that can be obtained via the Android sensors framework at the
+ * same point in time. This means, if CHRE reboots from the rest of the system,
+ * the counter must not reset to 0.
+ *
+ * @since v1.5
+ */
+#define CHRE_SENSOR_TYPE_STEP_COUNTER UINT8_C(0x18)
+
+/**
+ * Significant motion detection.
+ *
+ * Generates: CHRE_EVENT_SENSOR_SIGNIFICANT_MOTION_DATA
+ *
+ * This is a one-shot sensor.
+ *
+ * @since v1.10
+ */
+#define CHRE_SENSOR_TYPE_SIGNIFICANT_MOTION  UINT8_C(0x1C)
+
+/**
+ * Hinge angle sensor.
+ *
+ * Generates: CHRE_EVENT_SENSOR_HINGE_ANGLE_DATA
+ *
+ * This is an on-change sensor.
+ *
+ * A sensor of this type measures the angle, in degrees, between two
+ * integral parts of the device. Movement of a hinge measured by this sensor
+ * type is expected to alter the ways in which the user may interact with
+ * the device, for example by unfolding or revealing a display.
+ *
+ * @since v1.5
+ */
+#define CHRE_SENSOR_TYPE_HINGE_ANGLE UINT8_C(0x24)
+
+/**
+ * Uncalibrated accelerometer.
+ *
+ * Generates: CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA
+ *
+ * Note that the UNCALIBRATED_ACCELEROMETER_DATA must be factory calibrated
+ * data, but not runtime calibrated.
+ */
+#define CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER  UINT8_C(0x37)
+
+/**
+ * Accelerometer temperature.
+ *
+ * Generates: CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA
+ */
+#define CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE  UINT8_C(0x38)
+
+/**
+ * Gyroscope temperature.
+ *
+ * Generates: CHRE_EVENT_SENSOR_GYROSCOPE_TEMPERATURE_DATA
+ */
+#define CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE  UINT8_C(0x39)
+
+/**
+ * Magnetometer temperature.
+ *
+ * Generates: CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_TEMPERATURE_DATA
+ */
+#define CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE  UINT8_C(0x3A)
+
+#if CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE >= CHRE_SENSOR_TYPE_VENDOR_START
+#error Too many sensor types
+#endif
+
+/**
+ * Values that can be stored in the accuracy field of chreSensorDataHeader.
+ * If CHRE_SENSOR_ACCURACY_UNKNOWN is returned, then the driver did not provide
+ * accuracy information with the data. Values in the range
+ * [CHRE_SENSOR_ACCURACY_VENDOR_START, CHRE_SENSOR_ACCURACY_VENDOR_END] are
+ * reserved for vendor-specific values for vendor sensor types, and are not used
+ * by CHRE for standard sensor types.
+ *
+ * Otherwise, the values have the same meaning as defined in the Android
+ * Sensors definition:
+ * https://developer.android.com/reference/android/hardware/SensorManager
+ *
+ * @since v1.3
+ *
+ * @defgroup CHRE_SENSOR_ACCURACY
+ * @{
+ */
+
+#define CHRE_SENSOR_ACCURACY_UNKNOWN       UINT8_C(0x00)
+#define CHRE_SENSOR_ACCURACY_UNRELIABLE    UINT8_C(0x01)
+#define CHRE_SENSOR_ACCURACY_LOW           UINT8_C(0x02)
+#define CHRE_SENSOR_ACCURACY_MEDIUM        UINT8_C(0x03)
+#define CHRE_SENSOR_ACCURACY_HIGH          UINT8_C(0x04)
+#define CHRE_SENSOR_ACCURACY_VENDOR_START  UINT8_C(0xC0)
+#define CHRE_SENSOR_ACCURACY_VENDOR_END    UINT8_MAX
+
+/** @} */
+
+/**
+ * Header used in every structure containing batchable data from a sensor.
+ *
+ * The typical structure for sensor data looks like:
+ *
+ *   struct chreSensorTypeData {
+ *       struct chreSensorDataHeader header;
+ *       struct chreSensorTypeSampleData {
+ *           uint32_t timestampDelta;
+ *           union {
+ *               <type> value;
+ *               <type> interpretation0;
+ *               <type> interpretation1;
+ *           };
+ *       } readings[1];
+ *   };
+ *
+ * Despite 'readings' being declared as an array of 1 element,
+ * an instance of the struct will actually have 'readings' as
+ * an array of header.readingCount elements (which may be 1).
+ * The 'timestampDelta' is in relation to the previous 'readings' (or
+ * the baseTimestamp for readings[0].  So,
+ * Timestamp for readings[0] == header.baseTimestamp +
+ *     readings[0].timestampDelta.
+ * Timestamp for readings[1] == timestamp for readings[0] +
+ *     readings[1].timestampDelta.
+ * And thus, in order to determine the timestamp for readings[N], it's
+ * necessary to process through all of the N-1 readings.  The advantage,
+ * though, is that our entire readings can span an arbitrary length of time,
+ * just as long as any two consecutive readings differ by no more than
+ * 4.295 seconds (timestampDelta, like all time in the CHRE, is in
+ * nanoseconds).
+ *
+ * If a sensor has batched readings where two consecutive readings differ by
+ * more than 4.295 seconds, the CHRE will split them across multiple
+ * instances of the struct, and send multiple events.
+ *
+ * The value from the sensor is typically expressed in a union,
+ * allowing a generic access to the data ('value'), along with
+ * differently named access giving a more natural interpretation
+ * of the data for the specific sensor types which use this
+ * structure.  This allows, for example, barometer code to
+ * reference readings[N].pressure, and an ambient light sensor
+ * to reference readings[N].light, while both use the same
+ * structure.
+ */
+struct chreSensorDataHeader {
+    /**
+     * The base timestamp, in nanoseconds; must be in the same time base as
+     * chreGetTime().
+     */
+    uint64_t baseTimestamp;
+
+    /**
+     * The handle of the sensor producing this event.
+     */
+    uint32_t sensorHandle;
+
+    /**
+     * The number elements in the 'readings' array.
+     *
+     * This must be at least 1.
+     */
+    uint16_t readingCount;
+
+    /**
+     * The accuracy of the sensor data.
+     *
+     * @ref CHRE_SENSOR_ACCURACY
+     *
+     * @since v1.3
+     */
+    uint8_t accuracy;
+
+    /**
+     * Reserved bytes.
+     *
+     * This must be 0.
+     */
+    uint8_t reserved;
+};
+
+/**
+ * Data for a sensor which reports on three axes.
+ *
+ * This is used by CHRE_EVENT_SENSOR_ACCELEROMETER_DATA,
+ * CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO,
+ * CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA,
+ * CHRE_EVENT_SENSOR_GYROSCOPE_DATA,
+ * CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO,
+ * CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA,
+ * CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_DATA,
+ * CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO, and
+ * CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA.
+ */
+struct chreSensorThreeAxisData {
+    /**
+     * @see chreSensorDataHeader
+     */
+    struct chreSensorDataHeader header;
+    struct chreSensorThreeAxisSampleData {
+        /**
+         * @see chreSensorDataHeader
+         */
+        uint32_t timestampDelta;
+        union {
+            float values[3];
+            float v[3];
+            struct {
+                float x;
+                float y;
+                float z;
+            };
+            float bias[3];
+            struct {
+                float x_bias;
+                float y_bias;
+                float z_bias;
+            };
+        };
+    } readings[1];
+};
+
+/**
+ * Data from a sensor where we only care about a event occurring.
+ *
+ * This is a bit unusual in that our readings have no data in addition
+ * to the timestamp.  But since we only care about the occurrence, we
+ * don't need to know anything else.
+ *
+ * Used by: CHRE_EVENT_SENSOR_INSTANT_MOTION_DETECT_DATA,
+ *     CHRE_EVENT_SENSOR_STATIONARY_DETECT_DATA,
+ *     CHRE_EVENT_SENSOR_STEP_DETECT_DATA, and
+ *     CHRE_EVENT_SENSOR_SIGNIFICANT_MOTION_DATA.
+ */
+struct chreSensorOccurrenceData {
+    struct chreSensorDataHeader header;
+    struct chreSensorOccurrenceSampleData {
+        uint32_t timestampDelta;
+        // This space intentionally left blank.
+        // Only the timestamp is meaningful here, there
+        // is no additional data.
+    } readings[1];
+};
+
+/**
+ * This is used by CHRE_EVENT_SENSOR_LIGHT_DATA,
+ * CHRE_EVENT_SENSOR_PRESSURE_DATA,
+ * CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA,
+ * CHRE_EVENT_SENSOR_GYROSCOPE_TEMPERATURE_DATA,
+ * CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_TEMPERATURE_DATA, and
+ * CHRE_EVENT_SENSOR_HINGE_ANGLE_DATA.
+ */
+struct chreSensorFloatData {
+    struct chreSensorDataHeader header;
+    struct chreSensorFloatSampleData {
+        uint32_t timestampDelta;
+        union {
+            float value;
+            float light;        //!< Unit: lux
+            float pressure;     //!< Unit: hectopascals (hPa)
+            float temperature;  //!< Unit: degrees Celsius
+            float angle;        //!< Unit: angular degrees
+        };
+    } readings[1];
+};
+
+/**
+ * CHRE_EVENT_SENSOR_PROXIMITY_DATA.
+ */
+struct chreSensorByteData {
+    struct chreSensorDataHeader header;
+    struct chreSensorByteSampleData {
+        uint32_t timestampDelta;
+        union {
+            uint8_t value;
+            struct {
+                uint8_t isNear : 1;
+                //! @deprecated As of v1.2, this field is deprecated and must
+                //! always be set to 0
+                uint8_t invalid : 1;
+                uint8_t padding0 : 6;
+            };
+        };
+    } readings[1];
+};
+
+/**
+ * Data for a sensor which reports a single uint64 value.
+ *
+ * This is used by CHRE_EVENT_SENSOR_STEP_COUNTER_DATA.
+ */
+struct chreSensorUint64Data {
+    struct chreSensorDataHeader header;
+    struct chreSensorUint64SampleData {
+        uint32_t timestampDelta;
+        uint64_t value;
+    } readings[1];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_SENSOR_TYPES_H_ */
diff --git a/chre_api/legacy/v1_10/chre/toolchain.h b/chre_api/legacy/v1_10/chre/toolchain.h
new file mode 100644
index 0000000..7c93bb1
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/toolchain.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef CHRE_TOOLCHAIN_H_
+#define CHRE_TOOLCHAIN_H_
+
+/**
+ * @file
+ * Compiler/build toolchain-specific macros used by the CHRE API
+ */
+
+#if defined(__GNUC__) || defined(__clang__)
+// For GCC and clang
+
+#define CHRE_DEPRECATED(message) \
+  __attribute__((deprecated(message)))
+
+// Indicates that the function does not return (i.e. abort).
+#define CHRE_NO_RETURN __attribute__((noreturn))
+
+// Enable printf-style compiler warnings for mismatched format string and args
+#define CHRE_PRINTF_ATTR(formatPos, argStart) \
+  __attribute__((format(printf, formatPos, argStart)))
+
+#define CHRE_BUILD_ERROR(message) CHRE_DO_PRAGMA(GCC error message)
+#define CHRE_DO_PRAGMA(message) _Pragma(#message)
+
+// Marks a function as malloc-like, for optimizations with the return pointer
+#define CHRE_MALLOC_ATTR __attribute__((__malloc__))
+
+#elif defined(__ICCARM__) || defined(__CC_ARM)
+// For IAR ARM and Keil MDK-ARM compilers
+
+#define CHRE_PRINTF_ATTR(formatPos, argStart)
+
+#define CHRE_DEPRECATED(message)
+
+#define CHRE_NO_RETURN
+
+#define CHRE_MALLOC_ATTR
+
+#elif defined(_MSC_VER)
+// For Microsoft Visual Studio
+
+#define CHRE_PRINTF_ATTR(formatPos, argStart)
+
+#define CHRE_DEPRECATED(message)
+
+#define CHRE_NO_RETURN
+
+#define CHRE_MALLOC_ATTR
+
+#else  // if !defined(__GNUC__) && !defined(__clang__)
+
+#error Need to add support for new compiler
+
+#endif
+
+// For platforms that don't support error pragmas, utilize the best method of
+// showing an error depending on the platform support.
+#ifndef CHRE_BUILD_ERROR
+#ifdef __cplusplus  // C++17 or greater assumed
+#define CHRE_BUILD_ERROR(message) static_assert(0, message)
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+#define CHRE_BUILD_ERROR(message) _Static_assert(0, message)
+#else
+#define CHRE_BUILD_ERROR(message) char buildError[-1] = message
+#endif
+#endif
+
+#endif  // CHRE_TOOLCHAIN_H_
diff --git a/chre_api/legacy/v1_10/chre/user_settings.h b/chre_api/legacy/v1_10/chre/user_settings.h
new file mode 100644
index 0000000..a13290d
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/user_settings.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_USER_SETTINGS_H_
+#define _CHRE_USER_SETTINGS_H_
+
+/**
+ * @file
+ * The API for requesting notifications on changes in the settings of the
+ * active user. If the device is set up with one or more secondary users
+ * (see https://source.android.com/devices/tech/admin/multi-user), the user
+ * settings in CHRE reflect that of the currently active user.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <chre/event.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The user settings that nanoapps can request notifications for on a status
+ * change.
+ *
+ * NOTE: The WIFI available setting indicates the overall availability
+ * of WIFI related functionality. For example, if wifi is disabled for
+ * connectivity but enabled for location, the WIFI available setting is
+ * enabled.
+ *
+ * NOTE: The BLE available setting is the logical OR of the main Bluetooth
+ * setting and the Bluetooth scanning setting found under Location settings.
+ * Note that this indicates whether the user is allowing Bluetooth to be used,
+ * however the system may still fully power down the BLE chip in some scenarios
+ * if no request for it exists on the Android host side. See the
+ * chreBleStartScanAsync() API documentation for more information.
+ *
+ * @defgroup CHRE_USER_SETTINGS
+ * @{
+ */
+#define CHRE_USER_SETTING_LOCATION             UINT8_C(0)
+#define CHRE_USER_SETTING_WIFI_AVAILABLE       UINT8_C(1)
+#define CHRE_USER_SETTING_AIRPLANE_MODE        UINT8_C(2)
+#define CHRE_USER_SETTING_MICROPHONE           UINT8_C(3)
+#define CHRE_USER_SETTING_BLE_AVAILABLE        UINT8_C(4)
+
+/** @} */
+
+/**
+ * Produce an event ID in the block of IDs reserved for settings notifications.
+ *
+ * @param offset Index into the event ID block, valid in the range [0,15]
+ */
+#define CHRE_SETTING_EVENT_ID(offset) (CHRE_EVENT_SETTING_CHANGED_FIRST_EVENT + (offset))
+
+/**
+ * nanoappHandleEvent argument: struct chreUserSettingChangedEvent
+ *
+ * Notify nanoapps of a change in the associated setting. Nanoapps must first
+ * register (via chreUserSettingConfigureEvents) for events before they are
+ * sent out.
+ */
+#define CHRE_EVENT_SETTING_CHANGED_LOCATION         CHRE_SETTING_EVENT_ID(0)
+#define CHRE_EVENT_SETTING_CHANGED_WIFI_AVAILABLE   CHRE_SETTING_EVENT_ID(1)
+#define CHRE_EVENT_SETTING_CHANGED_AIRPLANE_MODE    CHRE_SETTING_EVENT_ID(2)
+#define CHRE_EVENT_SETTING_CHANGED_MICROPHONE       CHRE_SETTING_EVENT_ID(3)
+#define CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE    CHRE_SETTING_EVENT_ID(4)
+
+#if CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE > CHRE_EVENT_SETTING_CHANGED_LAST_EVENT
+#error Too many setting changed events.
+#endif
+
+/**
+ * Indicates the current state of a setting.
+ * The setting state is 'unknown' only in the following scenarios:
+ *  - CHRE hasn't received the initial state yet on a restart.
+ *  - The nanoapp is running on CHRE v1.4 or older
+ *  - Nanoapp provided in invalid setting ID to chreUserSettingGetStatus.
+ */
+enum chreUserSettingState {
+  CHRE_USER_SETTING_STATE_UNKNOWN = -1,
+  CHRE_USER_SETTING_STATE_DISABLED = 0,
+  CHRE_USER_SETTING_STATE_ENABLED = 1
+};
+
+/**
+ * The nanoappHandleEvent argument for CHRE settings changed notifications.
+ */
+struct chreUserSettingChangedEvent {
+  //! Indicates the setting whose state has changed.
+  uint8_t setting;
+
+  //! A value that corresponds to a member in enum chreUserSettingState,
+  // indicating the latest value of the setting.
+  int8_t settingState;
+};
+
+/**
+ * Get the current state of a given setting.
+ *
+ * @param setting The setting to get the current status of.
+ *
+ * @return The current state of the requested setting. The state is returned
+ * as an int8_t to be consistent with the associated event data, but is
+ * guaranteed to be a valid enum chreUserSettingState member.
+ *
+ * @since v1.5
+ */
+int8_t chreUserSettingGetState(uint8_t setting);
+
+/**
+ * Register or deregister for a notification on a status change for a given
+ * setting. Note that registration does not produce an event with the initial
+ * (or current) state, though nanoapps can use chreUserSettingGetState() for
+ * this purpose.
+ *
+ * @param setting The setting on whose change a notification is desired.
+ * @param enable The nanoapp is registered to receive notifications on a
+ * change in the user settings if this parameter is true, otherwise the
+ * nanoapp receives no further notifications for this setting.
+ *
+ * @since v1.5
+ */
+void chreUserSettingConfigureEvents(uint8_t setting, bool enable);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_USER_SETTINGS_H_ */
diff --git a/chre_api/legacy/v1_10/chre/version.h b/chre_api/legacy/v1_10/chre/version.h
new file mode 100644
index 0000000..8f4e3d6
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/version.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_VERSION_H_
+#define _CHRE_VERSION_H_
+
+/**
+ * @file
+ * Definitions and methods for the versioning of the Context Hub Runtime
+ * Environment.
+ *
+ * The CHRE API versioning pertains to all header files in the CHRE API.
+ */
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Value for version 0.1 of the Context Hub Runtime Environment API interface.
+ *
+ * This is a legacy version of the CHRE API. Version 1.0 is considered the first
+ * official CHRE API version.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_0_1 UINT32_C(0x00010000)
+
+/**
+ * Value for version 1.0 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API shipped with the Android Nougat release.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_0 UINT32_C(0x01000000)
+
+/**
+ * Value for version 1.1 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API shipped with the Android O release. It adds
+ * initial support for new GNSS, WiFi, and WWAN modules.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_1 UINT32_C(0x01010000)
+
+/**
+ * Value for version 1.2 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API shipped with the Android P release. It adds
+ * initial support for the new audio module.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_2 UINT32_C(0x01020000)
+
+/**
+ * Value for version 1.3 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API shipped with the Android Q release. It adds
+ * support for GNSS location altitude/speed/bearing accuracy. It also adds step
+ * detect as a standard CHRE sensor and supports bias event delivery and sensor
+ * data flushing.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_3 UINT32_C(0x01030000)
+
+/**
+ * Value for version 1.4 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API shipped with the Android R release. It adds
+ * support for collecting debug dump information from nanoapps, receiving L5
+ * GNSS measurements, determining if a sensor supports passive requests,
+ * receiving 5G cell info, and deprecates chreSendMessageToHost.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_4 UINT32_C(0x01040000)
+
+/**
+ * Value for version 1.5 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API shipped with the Android S release. It adds
+ * support for multiple sensors of the same type, permissions for sensitive CHRE
+ * APIs / data usage, ability to receive user settings updates, step counter and
+ * hinge angle sensors, improved WiFi scan preferences to support power
+ * optimization, new WiFi security types, increased the lower bound for the
+ * maximum CHRE to host message size, and increased GNSS measurements in
+ * chreGnssDataEvent.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_5 UINT32_C(0x01050000)
+
+/**
+ * Value for version 1.6 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API is shipped with the Android T release. It adds
+ * support for BLE scanning, subscribing to the WiFi NAN discovery engine,
+ * subscribing to host endpoint notifications, requesting metadata for a host
+ * endpoint ID, nanoapps publishing RPC services they support, and limits the
+ * nanoapp instance ID size to INT16_MAX.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_6 UINT32_C(0x01060000)
+
+/**
+ * Value for version 1.7 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API is shipped with a post-launch update to the
+ * Android T release. It adds the BLE flush API.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_7 UINT32_C(0x01070000)
+
+/**
+ * Value for version 1.8 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API is shipped with the Android U release. It adds
+ * support for filtering by manufacturer data in BLE scans, reading the RSSI
+ * value of a BLE connection, allowing the nanoapp to check BLE scan status,
+ * allowing the nanoapp to specify which RPC services it supports, and
+ * delivering batch complete events for batched BLE scans.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_8 UINT32_C(0x01080000)
+
+/**
+ * Value for version 1.9 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API is shipped with a post-launch update to the
+ * Android U release. It adds the BLE Broadcaster Address filter.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_9 UINT32_C(0x01090000)
+
+/**
+ * Value for version 1.10 of the Context Hub Runtime Environment API interface.
+ *
+ * This version of the CHRE API is shipped with Android V. It adds support for
+ * reliable messaging.
+ *
+ * @note This version of the CHRE API has not been finalized yet, and is
+ * currently considered a preview that is subject to change.
+ *
+ * @see CHRE_API_VERSION
+ */
+#define CHRE_API_VERSION_1_10 UINT32_C(0x010a0000)
+
+/**
+ * Major and Minor Version of this Context Hub Runtime Environment API.
+ *
+ * The major version changes when there is an incompatible API change.
+ *
+ * The minor version changes when there is an addition in functionality
+ * in a backwards-compatible manner.
+ *
+ * We define the version number as an unsigned 32-bit value.  The most
+ * significant byte is the Major Version.  The second-most significant byte
+ * is the Minor Version.  The two least significant bytes are the Patch
+ * Version.  The Patch Version is not defined by this header API, but
+ * is provided by a specific CHRE implementation (see chreGetVersion()).
+ *
+ * Note that version numbers can always be numerically compared with
+ * expected results, so 1.0.0 < 1.0.4 < 1.1.0 < 2.0.300 < 3.5.0.
+ */
+#define CHRE_API_VERSION CHRE_API_VERSION_1_10
+
+/**
+ * Utility macro to extract only the API major version of a composite CHRE
+ * version.
+ *
+ * @param version A uint32_t version, e.g. the value returned by
+ *     chreGetApiVersion()
+ *
+ * @return The API major version in the least significant byte, e.g. 0x01
+ */
+#define CHRE_EXTRACT_MAJOR_VERSION(version) \
+  (uint32_t)(((version) & UINT32_C(0xFF000000)) >> 24)
+
+/**
+ * Utility macro to extract only the API minor version of a composite CHRE
+ * version.
+ *
+ * @param version A uint32_t version, e.g. the CHRE_API_VERSION constant
+ *
+ * @return The API minor version in the least significant byte, e.g. 0x01
+ */
+#define CHRE_EXTRACT_MINOR_VERSION(version) \
+  (uint32_t)(((version) & UINT32_C(0x00FF0000)) >> 16)
+
+/**
+ * Utility macro to extract only the API minor version of a composite CHRE
+ * version.
+ *
+ * @param version A complete uint32_t version, e.g. the value returned by
+ *     chreGetVersion()
+ *
+ * @return The implementation patch version in the least significant two bytes,
+ *     e.g. 0x0123, with all other bytes set to 0
+ */
+#define CHRE_EXTRACT_PATCH_VERSION(version) (uint32_t)((version) & UINT32_C(0xFFFF))
+
+/**
+ * Get the API version the CHRE implementation was compiled against.
+ *
+ * This is not necessarily the CHRE_API_VERSION in the header the nanoapp was
+ * built against, and indeed may not have even appeared in the context_hub_os.h
+ * header which this nanoapp was built against.
+ *
+ * By definition, this will have the two least significant bytes set to 0,
+ * and only contain the major and minor version number.
+ *
+ * @return The API version.
+ */
+uint32_t chreGetApiVersion(void);
+
+/**
+ * Get the version of this CHRE implementation.
+ *
+ * By definition, ((chreGetApiVersion() & UINT32_C(0xFFFF0000)) ==
+ *                 (chreGetVersion()    & UINT32_C(0xFFFF0000))).
+ *
+ * The Patch Version, in the lower two bytes, only have meaning in context
+ * of this specific platform ID.  It is increased by the platform every time
+ * a backwards-compatible bug fix is released.
+ *
+ * @return The version.
+ *
+ * @see chreGetPlatformId()
+ */
+uint32_t chreGetVersion(void);
+
+/**
+ * Get the Platform ID of this CHRE.
+ *
+ * The most significant five bytes are the vendor ID as set out by the
+ * NANOAPP_VENDOR convention in the original context hub HAL header file
+ * (context_hub.h), also used by nanoapp IDs.
+ *
+ * The least significant three bytes are set by the vendor, but must be
+ * unique for each different CHRE implementation/hardware that the vendor
+ * supplies.
+ *
+ * The idea is that in the case of known bugs in the field, a new nanoapp could
+ * be shipped with a workaround that would use this value, and chreGetVersion(),
+ * to have code that can conditionally work around the bug on a buggy version.
+ * Thus, we require this uniqueness to allow such a setup to work.
+ *
+ * @return The platform ID.
+ *
+ * @see CHRE_EXTRACT_VENDOR_ID
+ */
+uint64_t chreGetPlatformId(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CHRE_VERSION_H_ */
diff --git a/chre_api/legacy/v1_10/chre/wifi.h b/chre_api/legacy/v1_10/chre/wifi.h
new file mode 100644
index 0000000..44d3d41
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/wifi.h
@@ -0,0 +1,1318 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_WIFI_H_
+#define _CHRE_WIFI_H_
+
+/**
+ * @file
+ * WiFi (IEEE 802.11) API, currently covering scanning features useful for
+ * determining location and offloading certain connectivity scans.
+ *
+ * In this file, specification references use the following shorthand:
+ *
+ *    Shorthand | Full specification name
+ *   ---------- | ------------------------
+ *     "802.11" | IEEE Std 802.11-2007
+ *     "HT"     | IEEE Std 802.11n-2009
+ *     "VHT"    | IEEE Std 802.11ac-2013
+ *     "WiFi 6" | IEEE Std 802.11ax draft
+ *     "NAN"    | Wi-Fi Neighbor Awareness Networking (NAN) Technical
+ *                Specification (v3.2)
+ *
+ * In the current version of CHRE API, the 6GHz band introduced in WiFi 6 is
+ * not supported. A scan request from CHRE should not result in scanning 6GHz
+ * channels. In particular, if a 6GHz channel is specified in scanning or
+ * ranging request parameter, CHRE should return an error code of
+ * CHRE_ERROR_NOT_SUPPORTED. Additionally, CHRE implementations must not include
+ * observations of access points on 6GHz channels in scan results, especially
+ * those produced due to scan monitoring.
+ */
+
+#include "common.h"
+#include <chre/common.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The set of flags returned by chreWifiGetCapabilities().
+ * @defgroup CHRE_WIFI_CAPABILITIES
+ * @{
+ */
+
+//! No WiFi APIs are supported
+#define CHRE_WIFI_CAPABILITIES_NONE              UINT32_C(0)
+
+//! Listening to scan results is supported, as enabled via
+//! chreWifiConfigureScanMonitorAsync()
+#define CHRE_WIFI_CAPABILITIES_SCAN_MONITORING   UINT32_C(1 << 0)
+
+//! Requesting WiFi scans on-demand is supported via chreWifiRequestScanAsync()
+#define CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN    UINT32_C(1 << 1)
+
+//! Specifying the radio chain preference in on-demand scan requests, and
+//! reporting it in scan events is supported
+//! @since v1.2
+#define CHRE_WIFI_CAPABILITIES_RADIO_CHAIN_PREF  UINT32_C(1 << 2)
+
+//! Requesting RTT ranging is supported via chreWifiRequestRangingAsync()
+//! @since v1.2
+#define CHRE_WIFI_CAPABILITIES_RTT_RANGING       UINT32_C(1 << 3)
+
+//! Specifies if WiFi NAN service subscription is supported. If a platform
+//! supports subscriptions, then it must also support RTT ranging for NAN
+//! services via chreWifiNanRequestRangingAsync()
+//! @since v1.6
+#define CHRE_WIFI_CAPABILITIES_NAN_SUB           UINT32_C(1 << 4)
+
+/** @} */
+
+/**
+ * Produce an event ID in the block of IDs reserved for WiFi
+ * @param offset  Index into WiFi event ID block; valid range [0,15]
+ */
+#define CHRE_WIFI_EVENT_ID(offset)  (CHRE_EVENT_WIFI_FIRST_EVENT + (offset))
+
+/**
+ * nanoappHandleEvent argument: struct chreAsyncResult
+ *
+ * Communicates the asynchronous result of a request to the WiFi API. The
+ * requestType field in {@link #chreAsyncResult} is set to a value from enum
+ * chreWifiRequestType.
+ */
+#define CHRE_EVENT_WIFI_ASYNC_RESULT  CHRE_WIFI_EVENT_ID(0)
+
+/**
+ * nanoappHandleEvent argument: struct chreWifiScanEvent
+ *
+ * Provides results of a WiFi scan.
+ */
+#define CHRE_EVENT_WIFI_SCAN_RESULT  CHRE_WIFI_EVENT_ID(1)
+
+/**
+ * nanoappHandleEvent argument: struct chreWifiRangingEvent
+ *
+ * Provides results of an RTT ranging request.
+ */
+#define CHRE_EVENT_WIFI_RANGING_RESULT  CHRE_WIFI_EVENT_ID(2)
+
+/**
+ * nanoappHandleEvent argument: struct chreWifiNanIdentifierEvent
+ *
+ * Lets the client know if the NAN engine was able to successfully assign
+ * an identifier to the subscribe call. The 'cookie' field in the event
+ * argument struct can be used to track which subscribe request this identifier
+ * maps to.
+ */
+#define CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT   CHRE_WIFI_EVENT_ID(3)
+
+/**
+ * nanoappHandleEvent argument: struct chreWifiNanDiscoveryEvent
+ *
+ * Event that is sent whenever a NAN service matches the criteria specified
+ * in a subscription request.
+ */
+#define CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT  CHRE_WIFI_EVENT_ID(4)
+
+/**
+ * nanoappHandleEvent argument: struct chreWifiNanSessionLostEvent
+ *
+ * Informs the client that a discovered service is no longer available or
+ * visible.
+ * The ID of the service on the client that was communicating with the extinct
+ * service is indicated by the event argument.
+ */
+#define CHRE_EVENT_WIFI_NAN_SESSION_LOST  CHRE_WIFI_EVENT_ID(5)
+
+/**
+ * nanoappHandleEvent argument: struct chreWifiNanSessionTerminatedEvent
+ *
+ * Signals the end of a NAN subscription session. The termination can be due to
+ * the user turning the WiFi off, or other platform reasons like not being able
+ * to support NAN concurrency with the host. The terminated event will have a
+ * reason code appropriately populated to denote why the event was sent.
+ */
+#define CHRE_EVENT_WIFI_NAN_SESSION_TERMINATED  CHRE_WIFI_EVENT_ID(6)
+
+// NOTE: Do not add new events with ID > 15; only values 0-15 are reserved
+// (see chre/event.h)
+
+/**
+ * The maximum amount of time that is allowed to elapse between a call to
+ * chreWifiRequestScanAsync() that returns true, and the associated
+ * CHRE_EVENT_WIFI_ASYNC_RESULT used to indicate whether the scan completed
+ * successfully or not.
+ */
+#define CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS  (30 * CHRE_NSEC_PER_SEC)
+
+/**
+ * The maximum amount of time that is allowed to elapse between a call to
+ * chreWifiRequestRangingAsync() that returns true, and the associated
+ * CHRE_EVENT_WIFI_RANGING_RESULT used to indicate whether the ranging operation
+ * completed successfully or not.
+ */
+#define CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS  (30 * CHRE_NSEC_PER_SEC)
+
+/**
+ * The current compatibility version of the chreWifiScanEvent structure,
+ * including nested structures.
+ */
+#define CHRE_WIFI_SCAN_EVENT_VERSION  UINT8_C(1)
+
+/**
+ * The current compatibility version of the chreWifiRangingEvent structure,
+ * including nested structures.
+ */
+#define CHRE_WIFI_RANGING_EVENT_VERSION  UINT8_C(0)
+
+/**
+ * Maximum number of frequencies that can be explicitly specified when
+ * requesting a scan
+ * @see #chreWifiScanParams
+ */
+#define CHRE_WIFI_FREQUENCY_LIST_MAX_LEN  (20)
+
+/**
+ * Maximum number of SSIDs that can be explicitly specified when requesting a
+ * scan
+ * @see #chreWifiScanParams
+ */
+#define CHRE_WIFI_SSID_LIST_MAX_LEN  (20)
+
+/**
+ * The maximum number of devices that can be specified in a single RTT ranging
+ * request.
+ * @see #chreWifiRangingParams
+ */
+#define CHRE_WIFI_RANGING_LIST_MAX_LEN  (10)
+
+/**
+ * The maximum number of octets in an SSID (see 802.11 7.3.2.1)
+ */
+#define CHRE_WIFI_SSID_MAX_LEN  (32)
+
+/**
+ * The number of octets in a BSSID (see 802.11 7.1.3.3.3)
+ */
+#define CHRE_WIFI_BSSID_LEN  (6)
+
+/**
+ * Set of flags which can either indicate a frequency band. Specified as a bit
+ * mask to allow for combinations in future API versions.
+ * @defgroup CHRE_WIFI_BAND_MASK
+ * @{
+ */
+
+#define CHRE_WIFI_BAND_MASK_2_4_GHZ  UINT8_C(1 << 0)  //!< 2.4 GHz
+#define CHRE_WIFI_BAND_MASK_5_GHZ    UINT8_C(1 << 1)  //!< 5 GHz
+
+/** @} */
+
+/**
+ * Characteristics of a scanned device given in struct chreWifiScanResult.flags
+ * @defgroup CHRE_WIFI_SCAN_RESULT_FLAGS
+ * @{
+ */
+
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_NONE                         UINT8_C(0)
+
+//! Element ID 61 (HT Operation) is present (see HT 7.3.2)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_HT_OPS_PRESENT               UINT8_C(1 << 0)
+
+//! Element ID 192 (VHT Operation) is present (see VHT 8.4.2)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_VHT_OPS_PRESENT              UINT8_C(1 << 1)
+
+//! Element ID 127 (Extended Capabilities) is present, and bit 70 (Fine Timing
+//! Measurement Responder) is set to 1 (see IEEE Std 802.11-2016 9.4.2.27)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER             UINT8_C(1 << 2)
+
+//! Retained for backwards compatibility
+//! @see CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_IS_80211MC_RTT_RESPONDER \
+    CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER
+
+//! HT Operation element indicates that a secondary channel is present
+//! (see HT 7.3.2.57)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_HAS_SECONDARY_CHANNEL_OFFSET UINT8_C(1 << 3)
+
+//! HT Operation element indicates that the secondary channel is below the
+//! primary channel (see HT 7.3.2.57)
+#define CHRE_WIFI_SCAN_RESULT_FLAGS_SECONDARY_CHANNEL_OFFSET_IS_BELOW  \
+                                                                 UINT8_C(1 << 4)
+
+/** @} */
+
+/**
+ * Identifies the authentication methods supported by an AP. Note that not every
+ * combination of flags may be possible. Based on WIFI_PNO_AUTH_CODE_* from
+ * hardware/libhardware_legacy/include/hardware_legacy/gscan.h in Android.
+ * @defgroup CHRE_WIFI_SECURITY_MODE_FLAGS
+ * @{
+ */
+
+#define CHRE_WIFI_SECURITY_MODE_UNKNOWN  UINT8_C(0)
+//! @deprecated since v1.10. Use CHRE_WIFI_SECURITY_MODE_UNKNOWN instead.
+#define CHRE_WIFI_SECURITY_MODE_UNKONWN CHRE_WIFI_SECURITY_MODE_UNKNOWN
+
+#define CHRE_WIFI_SECURITY_MODE_OPEN  UINT8_C(1 << 0)  //!< No auth/security
+#define CHRE_WIFI_SECURITY_MODE_WEP   UINT8_C(1 << 1)
+#define CHRE_WIFI_SECURITY_MODE_PSK   UINT8_C(1 << 2)  //!< WPA-PSK or WPA2-PSK
+#define CHRE_WIFI_SECURITY_MODE_EAP   UINT8_C(1 << 3)  //!< WPA-EAP or WPA2-EAP
+
+//! @since v1.5
+#define CHRE_WIFI_SECURITY_MODE_SAE   UINT8_C(1 << 4)
+
+//! @since v1.5
+#define CHRE_WIFI_SECURITY_MODE_EAP_SUITE_B  UINT8_C(1 << 5)
+
+//! @since v1.5
+#define CHRE_WIFI_SECURITY_MODE_OWE   UINT8_C(1 << 6)
+
+/** @} */
+
+/**
+ * Identifies which radio chain was used to discover an AP. The underlying
+ * hardware does not necessarily support more than one radio chain.
+ * @defgroup CHRE_WIFI_RADIO_CHAIN_FLAGS
+ * @{
+ */
+
+#define CHRE_WIFI_RADIO_CHAIN_UNKNOWN  UINT8_C(0)
+#define CHRE_WIFI_RADIO_CHAIN_0        UINT8_C(1 << 0)
+#define CHRE_WIFI_RADIO_CHAIN_1        UINT8_C(1 << 1)
+
+/** @} */
+
+//! Special value indicating that an LCI uncertainty fields is not provided
+//! Ref: RFC 6225
+#define CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN  UINT8_C(0)
+
+/**
+ * Defines the flags that may be returned in
+ * {@link #chreWifiRangingResult.flags}. Undefined bits are reserved for future
+ * use and must be ignored by nanoapps.
+ * @defgroup CHRE_WIFI_RTT_RESULT_FLAGS
+ * @{
+ */
+
+//! If set, the nested chreWifiLci structure is populated; otherwise it is
+//! invalid and must be ignored
+#define CHRE_WIFI_RTT_RESULT_HAS_LCI  UINT8_C(1 << 0)
+
+/** @} */
+
+/**
+ * Identifies a WiFi frequency band
+ */
+enum chreWifiBand {
+    CHRE_WIFI_BAND_2_4_GHZ = CHRE_WIFI_BAND_MASK_2_4_GHZ,
+    CHRE_WIFI_BAND_5_GHZ   = CHRE_WIFI_BAND_MASK_5_GHZ,
+};
+
+/**
+ * Indicates the BSS operating channel width determined from the VHT and/or HT
+ * Operation elements. Refer to VHT 8.4.2.161 and HT 7.3.2.57.
+ */
+enum chreWifiChannelWidth {
+    CHRE_WIFI_CHANNEL_WIDTH_20_MHZ         = 0,
+    CHRE_WIFI_CHANNEL_WIDTH_40_MHZ         = 1,
+    CHRE_WIFI_CHANNEL_WIDTH_80_MHZ         = 2,
+    CHRE_WIFI_CHANNEL_WIDTH_160_MHZ        = 3,
+    CHRE_WIFI_CHANNEL_WIDTH_80_PLUS_80_MHZ = 4,
+};
+
+/**
+ * Indicates the type of scan requested or performed
+ */
+enum chreWifiScanType {
+    //! Perform a purely active scan using probe requests. Do not scan channels
+    //! restricted to use via Dynamic Frequency Selection (DFS) only.
+    CHRE_WIFI_SCAN_TYPE_ACTIVE = 0,
+
+    //! Perform an active scan on unrestricted channels, and also perform a
+    //! passive scan on channels that are restricted to use via Dynamic
+    //! Frequency Selection (DFS), e.g. the U-NII bands 5250-5350MHz and
+    //! 5470-5725MHz in the USA as mandated by FCC regulation.
+    CHRE_WIFI_SCAN_TYPE_ACTIVE_PLUS_PASSIVE_DFS = 1,
+
+    //! Perform a passive scan, only listening for beacons.
+    CHRE_WIFI_SCAN_TYPE_PASSIVE = 2,
+
+    //! Client has no preference for a particular scan type.
+    //! Only valid in a {@link #chreWifiScanParams}.
+    //!
+    //! On a v1.4 or earlier platform, this will fall back to
+    //! CHRE_WIFI_SCAN_TYPE_ACTIVE if {@link #chreWifiScanParams.channelSet} is
+    //! set to CHRE_WIFI_CHANNEL_SET_NON_DFS, and to
+    //! CHRE_WIFI_SCAN_TYPE_ACTIVE_PLUS_PASSIVE_DFS otherwise.
+    //!
+    //! If CHRE_WIFI_CAPABILITIES_RADIO_CHAIN_PREF is supported, a v1.5 or
+    //! later platform shall perform a type of scan optimized for {@link
+    //! #chreWifiScanParams.radioChainPref}.
+    //!
+    //! Clients are strongly encouraged to set this value in {@link
+    //! #chreWifiScanParams.scanType} and instead express their preferences
+    //! through {@link #chreWifiRadioChainPref} and {@link #chreWifiChannelSet}
+    //! so the platform can best optimize power and performance.
+    //!
+    //! @since v1.5
+    CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE = 3,
+};
+
+/**
+ * Indicates whether RTT ranging with a specific device succeeded
+ */
+enum chreWifiRangingStatus {
+    //! Ranging completed successfully
+    CHRE_WIFI_RANGING_STATUS_SUCCESS = 0,
+
+    //! Ranging failed due to an unspecified error
+    CHRE_WIFI_RANGING_STATUS_ERROR   = 1,
+};
+
+/**
+ * Possible values for {@link #chreWifiLci.altitudeType}. Ref: RFC 6225 2.4
+ */
+enum chreWifiLciAltitudeType {
+    CHRE_WIFI_LCI_ALTITUDE_TYPE_UNKNOWN = 0,
+    CHRE_WIFI_LCI_ALTITUDE_TYPE_METERS  = 1,
+    CHRE_WIFI_LCI_ALTITUDE_TYPE_FLOORS  = 2,
+};
+
+/**
+ * Indicates a type of request made in this API. Used to populate the resultType
+ * field of struct chreAsyncResult sent with CHRE_EVENT_WIFI_ASYNC_RESULT.
+ */
+enum chreWifiRequestType {
+    CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR = 1,
+    CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN           = 2,
+    CHRE_WIFI_REQUEST_TYPE_RANGING                = 3,
+    CHRE_WIFI_REQUEST_TYPE_NAN_SUBSCRIBE          = 4,
+};
+
+/**
+ * Allows a nanoapp to express its preference for how multiple available
+ * radio chains should be used when performing an on-demand scan. This is only a
+ * preference from the nanoapp and is not guaranteed to be honored by the WiFi
+ * firmware.
+ */
+enum chreWifiRadioChainPref {
+    //! No preference for radio chain usage
+    CHRE_WIFI_RADIO_CHAIN_PREF_DEFAULT = 0,
+
+    //! In a scan result, indicates that the radio chain preference used for the
+    //! scan is not known
+    CHRE_WIFI_RADIO_CHAIN_PREF_UNKNOWN = CHRE_WIFI_RADIO_CHAIN_PREF_DEFAULT,
+
+    //! Prefer to use available radio chains in a way that minimizes time to
+    //! complete the scan
+    CHRE_WIFI_RADIO_CHAIN_PREF_LOW_LATENCY = 1,
+
+    //! Prefer to use available radio chains in a way that minimizes total power
+    //! consumed for the scan
+    CHRE_WIFI_RADIO_CHAIN_PREF_LOW_POWER = 2,
+
+    //! Prefer to use available radio chains in a way that maximizes accuracy of
+    //! the scan result, e.g. RSSI measurements
+    CHRE_WIFI_RADIO_CHAIN_PREF_HIGH_ACCURACY = 3,
+};
+
+/**
+ * WiFi NAN subscription type.
+ */
+enum chreWifiNanSubscribeType {
+    //! In the active mode, explicit transmission of a subscribe message is
+    //! requested, and publish messages are processed.
+    CHRE_WIFI_NAN_SUBSCRIBE_TYPE_ACTIVE = 0,
+
+    //! In the passive mode, no transmission of a subscribe message is
+    //! requested, but received publish messages are checked for matches.
+    CHRE_WIFI_NAN_SUBSCRIBE_TYPE_PASSIVE = 1,
+};
+
+/**
+ * Indicates the reason for a subscribe session termination.
+ */
+enum chreWifiNanTerminatedReason {
+    CHRE_WIFI_NAN_TERMINATED_BY_USER_REQUEST = 0,
+    CHRE_WIFI_NAN_TERMINATED_BY_TIMEOUT = 1,
+    CHRE_WIFI_NAN_TERMINATED_BY_FAILURE = 2,
+};
+
+/**
+ * SSID with an explicit length field, used when an array of SSIDs is supplied.
+ */
+struct chreWifiSsidListItem {
+    //! Number of valid bytes in ssid. Valid range [0, CHRE_WIFI_SSID_MAX_LEN]
+    uint8_t ssidLen;
+
+    //! Service Set Identifier (SSID)
+    uint8_t ssid[CHRE_WIFI_SSID_MAX_LEN];
+};
+
+/**
+ * Indicates the set of channels to be scanned.
+ *
+ * @since v1.5
+ */
+enum chreWifiChannelSet {
+    //! The set of channels that allows active scan using probe request.
+    CHRE_WIFI_CHANNEL_SET_NON_DFS = 0,
+
+    //! The set of all channels supported.
+    CHRE_WIFI_CHANNEL_SET_ALL = 1,
+};
+
+/**
+ * Data structure passed to chreWifiRequestScanAsync
+ */
+struct chreWifiScanParams {
+    //! Set to a value from @ref enum chreWifiScanType
+    uint8_t scanType;
+
+    //! Indicates whether the client is willing to tolerate receiving cached
+    //! results of a previous scan, and if so, the maximum age of the scan that
+    //! the client will accept. "Age" in this case is defined as the elapsed
+    //! time between when the most recent scan was completed and the request is
+    //! received, in milliseconds. If set to 0, no cached results may be
+    //! provided, and all scan results must come from a "fresh" WiFi scan, i.e.
+    //! one that completes strictly after this request is received. If more than
+    //! one scan is cached and meets this age threshold, only the newest scan is
+    //! provided.
+    uint32_t maxScanAgeMs;
+
+    //! If set to 0, scan all frequencies. Otherwise, this indicates the number
+    //! of frequencies to scan, as specified in the frequencyList array. Valid
+    //! range [0, CHRE_WIFI_FREQUENCY_LIST_MAX_LEN].
+    uint16_t frequencyListLen;
+
+    //! Pointer to an array of frequencies to scan, given as channel center
+    //! frequencies in MHz. This field may be NULL if frequencyListLen is 0.
+    const uint32_t *frequencyList;
+
+    //! If set to 0, do not restrict scan to any SSIDs. Otherwise, this
+    //! indicates the number of SSIDs in the ssidList array to be used for
+    //! directed probe requests. Not applicable and ignore when scanType is
+    //! CHRE_WIFI_SCAN_TYPE_PASSIVE.
+    uint8_t ssidListLen;
+
+    //! Pointer to an array of SSIDs to use for directed probe requests. May be
+    //! NULL if ssidListLen is 0.
+    const struct chreWifiSsidListItem *ssidList;
+
+    //! Set to a value from enum chreWifiRadioChainPref to specify the desired
+    //! trade-off between power consumption, accuracy, etc. If
+    //! chreWifiGetCapabilities() does not have the applicable bit set, this
+    //! parameter is ignored.
+    //! @since v1.2
+    uint8_t radioChainPref;
+
+    //! Set to a value from enum chreWifiChannelSet to specify the set of
+    //! channels to be scanned. This field is considered by the platform only
+    //! if scanType is CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE and frequencyListLen
+    //! is equal to zero.
+    //!
+    //! @since v1.5
+    uint8_t channelSet;
+};
+
+/**
+ * Provides information about a single access point (AP) detected in a scan.
+ */
+struct chreWifiScanResult {
+    //! Number of milliseconds prior to referenceTime in the enclosing
+    //! chreWifiScanEvent struct when the probe response or beacon frame that
+    //! was used to populate this structure was received.
+    uint32_t ageMs;
+
+    //! Capability Information field sent by the AP (see 802.11 7.3.1.4). This
+    //! field must reflect native byte order and bit ordering, such that
+    //! (capabilityInfo & 1) gives the bit for the ESS subfield.
+    uint16_t capabilityInfo;
+
+    //! Number of valid bytes in ssid. Valid range [0, CHRE_WIFI_SSID_MAX_LEN]
+    uint8_t ssidLen;
+
+    //! Service Set Identifier (SSID), a series of 0 to 32 octets identifying
+    //! the access point. Note that this is commonly a human-readable ASCII
+    //! string, but this is not the required encoding per the standard.
+    uint8_t ssid[CHRE_WIFI_SSID_MAX_LEN];
+
+    //! Basic Service Set Identifier (BSSID), represented in big-endian byte
+    //! order, such that the first octet of the OUI is accessed in byte index 0.
+    uint8_t bssid[CHRE_WIFI_BSSID_LEN];
+
+    //! A set of flags from CHRE_WIFI_SCAN_RESULT_FLAGS_*
+    uint8_t flags;
+
+    //! RSSI (Received Signal Strength Indicator), in dBm. Typically negative.
+    //! If multiple radio chains were used to scan this AP, this is a "best
+    //! available" measure that may be a composite of measurements taken across
+    //! the radio chains.
+    int8_t  rssi;
+
+    //! Operating band, set to a value from enum chreWifiBand
+    uint8_t band;
+
+    /**
+     * Indicates the center frequency of the primary 20MHz channel, given in
+     * MHz. This value is derived from the channel number via the formula:
+     *
+     *     primaryChannel (MHz) = CSF + 5 * primaryChannelNumber
+     *
+     * Where CSF is the channel starting frequency (in MHz) given by the
+     * operating class/band (i.e. 2407 or 5000), and primaryChannelNumber is the
+     * channel number in the range [1, 200].
+     *
+     * Refer to VHT 22.3.14.
+     */
+    uint32_t primaryChannel;
+
+    /**
+     * If the channel width is 20 MHz, this field is not relevant and set to 0.
+     * If the channel width is 40, 80, or 160 MHz, then this denotes the channel
+     * center frequency (in MHz). If the channel is 80+80 MHz, then this denotes
+     * the center frequency of segment 0, which contains the primary channel.
+     * This value is derived from the frequency index using the same formula as
+     * for primaryChannel.
+     *
+     * Refer to VHT 8.4.2.161, and VHT 22.3.14.
+     *
+     * @see #primaryChannel
+     */
+    uint32_t centerFreqPrimary;
+
+    /**
+     * If the channel width is 80+80MHz, then this denotes the center frequency
+     * of segment 1, which does not contain the primary channel. Otherwise, this
+     * field is not relevant and set to 0.
+     *
+     * @see #centerFreqPrimary
+     */
+    uint32_t centerFreqSecondary;
+
+    //! @see #chreWifiChannelWidth
+    uint8_t channelWidth;
+
+    //! Flags from CHRE_WIFI_SECURITY_MODE_* indicating supported authentication
+    //! and associated security modes
+    //! @see CHRE_WIFI_SECURITY_MODE_FLAGS
+    uint8_t securityMode;
+
+    //! Identifies the radio chain(s) used to discover this AP
+    //! @see CHRE_WIFI_RADIO_CHAIN_FLAGS
+    //! @since v1.2
+    uint8_t radioChain;
+
+    //! If the CHRE_WIFI_RADIO_CHAIN_0 bit is set in radioChain, gives the RSSI
+    //! measured on radio chain 0 in dBm; otherwise invalid and set to 0. This
+    //! field, along with its relative rssiChain1, can be used to determine RSSI
+    //! measurements from each radio chain when multiple chains were used to
+    //! discover this AP.
+    //! @see #radioChain
+    //! @since v1.2
+    int8_t rssiChain0;
+    int8_t rssiChain1;  //!< @see #rssiChain0
+
+    //! Reserved; set to 0
+    uint8_t reserved[7];
+};
+
+/**
+ * Data structure sent with events of type CHRE_EVENT_WIFI_SCAN_RESULT.
+ */
+struct chreWifiScanEvent {
+    //! Indicates the version of the structure, for compatibility purposes.
+    //! Clients do not normally need to worry about this field; the CHRE
+    //! implementation guarantees that the client only receives the structure
+    //! version it expects.
+    uint8_t version;
+
+    //! The number of entries in the results array in this event. The CHRE
+    //! implementation may split scan results across multiple events for memory
+    //! concerns, etc.
+    uint8_t resultCount;
+
+    //! The total number of results returned by the scan. Allows an event
+    //! consumer to identify when it has received all events associated with a
+    //! scan.
+    uint8_t resultTotal;
+
+    //! Sequence number for this event within the series of events comprising a
+    //! complete scan result. Scan events are delivered strictly in order, i.e.
+    //! this is monotonically increasing for the results of a single scan. Valid
+    //! range [0, <number of events for scan> - 1]. The number of events for a
+    //! scan is typically given by
+    //! ceil(resultTotal / <max results per event supported by platform>).
+    uint8_t eventIndex;
+
+    //! A value from enum chreWifiScanType indicating the type of scan performed
+    uint8_t scanType;
+
+    //! If a directed scan was performed to a limited set of SSIDs, then this
+    //! identifies the number of unique SSIDs included in the probe requests.
+    //! Otherwise, this is set to 0, indicating that the scan was not limited by
+    //! SSID. Note that if this is non-zero, the list of SSIDs used is not
+    //! included in the scan event.
+    uint8_t ssidSetSize;
+
+    //! If 0, indicates that all frequencies applicable for the scanType were
+    //! scanned. Otherwise, indicates the number of frequencies scanned, as
+    //! specified in scannedFreqList.
+    uint16_t scannedFreqListLen;
+
+    //! Timestamp when the scan was completed, from the same time base as
+    //! chreGetTime() (in nanoseconds)
+    uint64_t referenceTime;
+
+    //! Pointer to an array containing scannedFreqListLen values comprising the
+    //! set of frequencies that were scanned. Frequencies are specified as
+    //! channel center frequencies in MHz. May be NULL if scannedFreqListLen is
+    //! 0.
+    const uint32_t *scannedFreqList;
+
+    //! Pointer to an array containing resultCount entries. May be NULL if
+    //! resultCount is 0.
+    const struct chreWifiScanResult *results;
+
+    //! Set to a value from enum chreWifiRadioChainPref indicating the radio
+    //! chain preference used for the scan. If the applicable bit is not set in
+    //! chreWifiGetCapabilities(), this will always be set to
+    //! CHRE_WIFI_RADIO_CHAIN_PREF_UNKNOWN.
+    //! @since v1.2
+    uint8_t radioChainPref;
+};
+
+/**
+ * Identifies a device to perform RTT ranging against. These values are normally
+ * populated based on the contents of a scan result.
+ * @see #chreWifiScanResult
+ * @see chreWifiRangingTargetFromScanResult()
+ */
+struct chreWifiRangingTarget {
+    //! Device MAC address, specified in the same byte order as
+    //! {@link #chreWifiScanResult.bssid}
+    uint8_t macAddress[CHRE_WIFI_BSSID_LEN];
+
+    //! Center frequency of the primary 20MHz channel, in MHz
+    //! @see #chreWifiScanResult.primaryChannel
+    uint32_t primaryChannel;
+
+    //! Channel center frequency, in MHz, or 0 if not relevant
+    //! @see #chreWifiScanResult.centerFreqPrimary
+    uint32_t centerFreqPrimary;
+
+    //! Channel center frequency of segment 1 if channel width is 80+80MHz,
+    //! otherwise 0
+    //! @see #chreWifiScanResult.centerFreqSecondary
+    uint32_t centerFreqSecondary;
+
+    //! @see #chreWifiChannelWidth
+    uint8_t channelWidth;
+
+    //! Reserved for future use and ignored by CHRE
+    uint8_t reserved[3];
+};
+
+/**
+ * Parameters for an RTT ("Fine Timing Measurement" in terms of 802.11-2016)
+ * ranging request, supplied to chreWifiRequestRangingAsync().
+ */
+struct chreWifiRangingParams {
+    //! Number of devices to perform ranging against and the length of
+    //! targetList, in range [1, CHRE_WIFI_RANGING_LIST_MAX_LEN].
+    uint8_t targetListLen;
+
+    //! Array of macAddressListLen MAC addresses (e.g. BSSIDs) with which to
+    //! attempt RTT ranging.
+    const struct chreWifiRangingTarget *targetList;
+};
+
+/**
+ * Provides the result of RTT ranging with a single device.
+ */
+struct chreWifiRangingResult {
+    //! Time when the ranging operation on this device was performed, in the
+    //! same time base as chreGetTime() (in nanoseconds)
+    uint64_t timestamp;
+
+    //! MAC address of the device for which ranging was requested
+    uint8_t macAddress[CHRE_WIFI_BSSID_LEN];
+
+    //! Gives the result of ranging to this device. If not set to
+    //! CHRE_WIFI_RANGING_STATUS_SUCCESS, the ranging attempt to this device
+    //! failed, and other fields in this structure may be invalid.
+    //! @see #chreWifiRangingStatus
+    uint8_t status;
+
+    //! The mean RSSI measured during the RTT burst, in dBm. Typically negative.
+    //! If status is not CHRE_WIFI_RANGING_STATUS_SUCCESS, will be set to 0.
+    int8_t rssi;
+
+    //! Estimated distance to the device with the given BSSID, in millimeters.
+    //! Generally the mean of multiple measurements performed in a single burst.
+    //! If status is not CHRE_WIFI_RANGING_STATUS_SUCCESS, will be set to 0.
+    uint32_t distance;
+
+    //! Standard deviation of estimated distance across multiple measurements
+    //! performed in a single RTT burst, in millimeters. If status is not
+    //! CHRE_WIFI_RANGING_STATUS_SUCCESS, will be set to 0.
+    uint32_t distanceStdDev;
+
+    //! Location Configuration Information (LCI) information optionally returned
+    //! during the ranging procedure. Only valid if {@link #flags} has the
+    //! CHRE_WIFI_RTT_RESULT_HAS_LCI bit set. Refer to IEEE 802.11-2016
+    //! 9.4.2.22.10, 11.24.6.7, and RFC 6225 (July 2011) for more information.
+    //! Coordinates are to be interpreted according to the WGS84 datum.
+    struct chreWifiLci {
+        //! Latitude in degrees as 2's complement fixed-point with 25 fractional
+        //! bits, i.e. degrees * 2^25. Ref: RFC 6225 2.3
+        int64_t latitude;
+
+        //! Longitude, same format as {@link #latitude}
+        int64_t longitude;
+
+        //! Altitude represented as a 2's complement fixed-point value with 8
+        //! fractional bits. Interpretation depends on {@link #altitudeType}. If
+        //! UNKNOWN, this field must be ignored. If *METERS, distance relative
+        //! to the zero point in the vertical datum. If *FLOORS, a floor value
+        //! relative to the ground floor, potentially fractional, e.g. to
+        //! indicate mezzanine levels. Ref: RFC 6225 2.4
+        int32_t altitude;
+
+        //! Maximum extent of latitude uncertainty in degrees, decoded via this
+        //! formula: 2 ^ (8 - x) where "x" is the encoded value passed in this
+        //! field. Unknown if set to CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN.
+        //! Ref: RFC 6225 2.3.2
+        uint8_t latitudeUncertainty;
+
+        //! @see #latitudeUncertainty
+        uint8_t longitudeUncertainty;
+
+        //! Defines how to interpret altitude, set to a value from enum
+        //! chreWifiLciAltitudeType
+        uint8_t altitudeType;
+
+        //! Uncertainty in altitude, decoded via this formula: 2 ^ (21 - x)
+        //! where "x" is the encoded value passed in this field. Unknown if set
+        //! to CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN. Only applies when altitudeType
+        //! is CHRE_WIFI_LCI_ALTITUDE_TYPE_METERS. Ref: RFC 6225 2.4.5
+        uint8_t altitudeUncertainty;
+    } lci;
+
+    //! Refer to CHRE_WIFI_RTT_RESULT_FLAGS
+    uint8_t flags;
+
+    //! Reserved; set to 0
+    uint8_t reserved[7];
+};
+
+/**
+ * Data structure sent with events of type CHRE_EVENT_WIFI_RANGING_RESULT.
+ */
+struct chreWifiRangingEvent {
+    //! Indicates the version of the structure, for compatibility purposes.
+    //! Clients do not normally need to worry about this field; the CHRE
+    //! implementation guarantees that the client only receives the structure
+    //! version it expects.
+    uint8_t version;
+
+    //! The number of ranging results included in the results array; matches the
+    //! number of MAC addresses specified in the request
+    uint8_t resultCount;
+
+    //! Reserved; set to 0
+    uint8_t reserved[2];
+
+    //! Pointer to an array containing resultCount entries
+    const struct chreWifiRangingResult *results;
+};
+
+/**
+ * Indicates the WiFi NAN capabilities of the device. Must contain non-zero
+ * values if WiFi NAN is supported.
+ */
+struct chreWifiNanCapabilities {
+    //! Maximum length of the match filter arrays (applies to both tx and rx
+    //! match filters).
+    uint32_t maxMatchFilterLength;
+
+    //! Maximum length of the service specific information byte array.
+    uint32_t maxServiceSpecificInfoLength;
+
+    //! Maximum length of the service name. Includes the NULL terminator.
+    uint8_t maxServiceNameLength;
+
+    //! Reserved for future use.
+    uint8_t reserved[3];
+};
+
+/**
+ * Data structure sent with events of type
+ * CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT
+ */
+struct chreWifiNanIdentifierEvent {
+    //! A unique ID assigned by the NAN engine for the subscribe request
+    //! associated with the cookie encapsulated in the async result below. The
+    //! ID is set to 0 if there was a request failure in which case the async
+    //! result below contains the appropriate error code indicating the failure
+    //! reason.
+    uint32_t id;
+
+    //! Structure which contains the cookie associated with the publish/
+    //! subscribe request, along with an error code that indicates request
+    //! success or failure.
+    struct chreAsyncResult result;
+};
+
+/**
+ * Indicates the desired configuration for a WiFi NAN ranging request.
+ */
+struct chreWifiNanRangingParams {
+    //! MAC address of the NAN device for which range is to be determined.
+    uint8_t macAddress[CHRE_WIFI_BSSID_LEN];
+};
+
+/**
+ * Configuration parameters specific to the Subscribe Function (Spec 4.1.1.1)
+ */
+struct chreWifiNanSubscribeConfig {
+    //! Indicates the subscribe type, set to a value from @ref
+    //! chreWifiNanSubscribeType.
+    uint8_t subscribeType;
+
+    //! UTF-8 name string that identifies the service/application. Must be NULL
+    //! terminated. Note that the string length cannot be greater than the
+    //! maximum length specified by @ref chreWifiNanCapabilities. No
+    //! restriction is placed on the string case, since the service name
+    //! matching is expected to be case insensitive.
+    const char *service;
+
+    //! An array of bytes (and the associated array length) of service-specific
+    //! information. Note that the array length must be less than the
+    //! maxServiceSpecificInfoLength parameter obtained from the NAN
+    //! capabilities (@see struct chreWifiNanCapabilities).
+    const uint8_t *serviceSpecificInfo;
+    uint32_t serviceSpecificInfoSize;
+
+    //! Ordered sequence of {length | value} pairs that specify match criteria
+    //! beyond the service name. 'length' uses 1 byte, and its value indicates
+    //! the number of bytes of the match criteria that follow. The length of
+    //! the match filter array should not exceed the maximum match filter
+    //! length obtained from @ref chreWifiNanGetCapabilities. When a service
+    //! publish message discovery frame containing the Service ID being
+    //! subscribed to is received, the matching is done as follows:
+    //! Each {length | value} pair in the kth position (1 <= k <= #length-value
+    //! pairs) is compared against the kth {length | value} pair in the
+    //! matching filter field of the publish message.
+    //! - For a kth position {length | value} pair in the rx match filter with
+    //!   a length of 0, a match is declared regardless of the tx match filter
+    //!   contents.
+    //! - For a kth position {length | value} pair in the rx match with a non-
+    //!   zero length, there must be an exact match with the kth position pair
+    //!    in the match filter field of the received service descriptor for a
+    //!    match to be found.
+    //! Please refer to Appendix H of the NAN spec for examples on matching.
+    //! The match filter length should not exceed the maxMatchFilterLength
+    //! obtained from @ref chreWifiNanCapabilities.
+    const uint8_t *matchFilter;
+    uint32_t matchFilterLength;
+};
+
+/**
+ * Data structure sent with events of type
+ * CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT.
+ */
+struct chreWifiNanDiscoveryEvent {
+    //! Identifier of the subscribe function instance that requested a
+    //! discovery.
+    uint32_t subscribeId;
+
+    //! Identifier of the publisher on the remote NAN device.
+    uint32_t publishId;
+
+    //! NAN interface address of the publisher
+    uint8_t publisherAddress[CHRE_WIFI_BSSID_LEN];
+
+    //! An array of bytes (and the associated array length) of service-specific
+    //! information. Note that the array length must be less than the
+    //! maxServiceSpecificInfoLength parameter obtained from the NAN
+    //! capabilities (@see struct chreWifiNanCapabilities).
+    const uint8_t *serviceSpecificInfo;
+    uint32_t serviceSpecificInfoSize;
+};
+
+/**
+ * Data structure sent with events of type CHRE_EVENT_WIFI_NAN_SESSION_LOST.
+ */
+struct chreWifiNanSessionLostEvent {
+    //! The original ID (returned by the NAN discovery engine) of the subscriber
+    //! instance.
+    uint32_t id;
+
+    //! The ID of the previously discovered publisher on a peer NAN device that
+    //! is no longer connected.
+    uint32_t peerId;
+};
+
+/**
+ * Data structure sent with events of type
+ * CHRE_EVENT_WIFI_NAN_SESSION_TERMINATED.
+ */
+struct chreWifiNanSessionTerminatedEvent {
+    //! The original ID (returned by the NAN discovery engine) of the subscriber
+    //! instance that was terminated.
+    uint32_t id;
+
+    //! A value that maps to one of the termination reasons in @ref enum
+    //! chreWifiNanTerminatedReason.
+    uint8_t reason;
+
+    //! Reserved for future use.
+    uint8_t reserved[3];
+};
+
+/**
+ * Retrieves a set of flags indicating the WiFi features supported by the
+ * current CHRE implementation. The value returned by this function must be
+ * consistent for the entire duration of the Nanoapp's execution.
+ *
+ * The client must allow for more flags to be set in this response than it knows
+ * about, for example if the implementation supports a newer version of the API
+ * than the client was compiled against.
+ *
+ * @return A bitmask with zero or more CHRE_WIFI_CAPABILITIES_* flags set
+ *
+ * @since v1.1
+ */
+uint32_t chreWifiGetCapabilities(void);
+
+/**
+ * Retrieves device-specific WiFi NAN capabilities, and populates them in
+ * the @ref chreWifiNanCapabilities structure.
+ *
+ * @param capabilities Structure into which the WiFi NAN capabilities of
+ *        the device are populated into. Must not be NULL.
+ * @return true if WiFi NAN is supported, false otherwise.
+ *
+ * @since v1.6
+ */
+bool chreWifiNanGetCapabilities(struct chreWifiNanCapabilities *capabilities);
+
+/**
+ * Nanoapps must define CHRE_NANOAPP_USES_WIFI somewhere in their build
+ * system (e.g. Makefile) if the nanoapp needs to use the following WiFi APIs.
+ * In addition to allowing access to these APIs, defining this macro will also
+ * ensure CHRE enforces that all host clients this nanoapp talks to have the
+ * required Android permissions needed to listen to WiFi data by adding metadata
+ * to the nanoapp.
+ */
+#if defined(CHRE_NANOAPP_USES_WIFI) || !defined(CHRE_IS_NANOAPP_BUILD)
+
+/**
+ * Manages a client's request to receive the results of WiFi scans performed for
+ * other purposes, for example scans done to maintain connectivity and scans
+ * requested by other clients. The presence of this request has no effect on the
+ * frequency or configuration of the WiFi scans performed - it is purely a
+ * registration by the client to receive the results of scans that would
+ * otherwise occur normally. This should include all available scan results,
+ * including those that are not normally sent to the applications processor,
+ * such as Preferred Network Offload (PNO) scans. Scan results provided because
+ * of this registration must not contain cached results - they are always
+ * expected to contain the fresh results from a recent scan.
+ *
+ * An active scan monitor subscription must persist across temporary conditions
+ * under which no WiFi scans will be performed, for example if WiFi is
+ * completely disabled via user-controlled settings, or if the WiFi system
+ * restarts independently of CHRE. Likewise, a request to enable a scan monitor
+ * subscription must succeed under normal conditions, even in circumstances
+ * where no WiFi scans will be performed. In these cases, the scan monitor
+ * implementation must produce scan results once the temporary condition is
+ * cleared, for example after WiFi is enabled by the user.
+ *
+ * These scan results are delivered to the Nanoapp's handle event callback using
+ * CHRE_EVENT_WIFI_SCAN_RESULT.
+ *
+ * An active scan monitor subscription is not necessary to receive the results
+ * of an on-demand scan request sent via chreWifiRequestScanAsync(), and it does
+ * not result in duplicate delivery of scan results generated from
+ * chreWifiRequestScanAsync().
+ *
+ * If no monitor subscription is active at the time of a request with
+ * enable=false, it is treated as if an active subscription was successfully
+ * ended.
+ *
+ * The result of this request is delivered asynchronously via an event of type
+ * CHRE_EVENT_WIFI_ASYNC_RESULT. Refer to the note in {@link #chreAsyncResult}
+ * for more details.
+ *
+ * @param enable Set to true to enable monitoring scan results, false to
+ *        disable
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.1
+ * @note Requires WiFi permission
+ */
+bool chreWifiConfigureScanMonitorAsync(bool enable, const void *cookie);
+
+/**
+ * Sends an on-demand request for WiFi scan results. This may trigger a new
+ * scan, or be entirely serviced from cache, depending on the maxScanAgeMs
+ * parameter.
+ *
+ * This resulting status of this request is delivered asynchronously via an
+ * event of type CHRE_EVENT_WIFI_ASYNC_RESULT. The result must be delivered
+ * within CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS of the this request. Refer to the
+ * note in {@link #chreAsyncResult} for more details.
+ *
+ * A successful result provided in CHRE_EVENT_WIFI_ASYNC_RESULT indicates that
+ * the scan results are ready to be delivered in a subsequent event (or events,
+ * which arrive consecutively without any other scan results in between)
+ * of type CHRE_EVENT_WIFI_SCAN_RESULT.
+ *
+ * WiFi scanning must be disabled if both "WiFi scanning" and "WiFi" settings
+ * are disabled at the Android level. In this case, the CHRE implementation is
+ * expected to return a result with CHRE_ERROR_FUNCTION_DISABLED.
+ *
+ * It is not valid for a client to request a new scan while a result is pending
+ * based on a previous scan request from the same client. In this situation, the
+ * CHRE implementation is expected to return a result with CHRE_ERROR_BUSY.
+ * However, if a scan is currently pending or in progress due to a request from
+ * another client, whether within the CHRE or otherwise, the implementation must
+ * not fail the request for this reason. If the pending scan satisfies the
+ * client's request parameters, then the implementation should use its results
+ * to satisfy the request rather than scheduling a new scan.
+ *
+ * @param params A set of parameters for the scan request. Must not be NULL.
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.1
+ * @note Requires WiFi permission
+ */
+bool chreWifiRequestScanAsync(const struct chreWifiScanParams *params,
+                              const void *cookie);
+
+/**
+ * Convenience function which calls chreWifiRequestScanAsync() with a default
+ * set of scan parameters.
+ *
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.1
+ * @note Requires WiFi permission
+ */
+static inline bool chreWifiRequestScanAsyncDefault(const void *cookie) {
+    static const struct chreWifiScanParams params = {
+        /*.scanType=*/         CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE,
+        /*.maxScanAgeMs=*/     5000,  // 5 seconds
+        /*.frequencyListLen=*/ 0,
+        /*.frequencyList=*/    NULL,
+        /*.ssidListLen=*/      0,
+        /*.ssidList=*/         NULL,
+        /*.radioChainPref=*/   CHRE_WIFI_RADIO_CHAIN_PREF_DEFAULT,
+        /*.channelSet=*/       CHRE_WIFI_CHANNEL_SET_NON_DFS
+    };
+    return chreWifiRequestScanAsync(&params, cookie);
+}
+
+/**
+ * Issues a request to initiate distance measurements using round-trip time
+ * (RTT), aka Fine Timing Measurement (FTM), to one or more devices identified
+ * by MAC address. Within CHRE, MACs are typically the BSSIDs of scanned APs
+ * that have the CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER flag set.
+ *
+ * This resulting status of this request is delivered asynchronously via an
+ * event of type CHRE_EVENT_WIFI_ASYNC_RESULT. The result must be delivered
+ * within CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS of the this request. Refer to the
+ * note in {@link #chreAsyncResult} for more details.
+ *
+ * WiFi RTT ranging must be disabled if any of the following is true:
+ * - Both "WiFi" and "WiFi Scanning" settings are disabled at the Android level.
+ * - The "Location" setting is disabled at the Android level.
+ * In this case, the CHRE implementation is expected to return a result with
+ * CHRE_ERROR_FUNCTION_DISABLED.
+ *
+ * A successful result provided in CHRE_EVENT_WIFI_ASYNC_RESULT indicates that
+ * the results of ranging will be delivered in a subsequent event of type
+ * CHRE_EVENT_WIFI_RANGING_RESULT. Note that the CHRE_EVENT_WIFI_ASYNC_RESULT
+ * gives an overall status - for example, it is used to indicate failure if the
+ * entire ranging request was rejected because WiFi is disabled. However, it is
+ * valid for this event to indicate success, but RTT ranging to fail for all
+ * requested devices - for example, they may be out of range. Therefore, it is
+ * also necessary to check the status field in {@link #chreWifiRangingResult}.
+ *
+ * @param params Structure containing the parameters of the scan request,
+ *        including the list of devices to attempt ranging.
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.2
+ * @note Requires WiFi permission
+ */
+bool chreWifiRequestRangingAsync(const struct chreWifiRangingParams *params,
+                                 const void *cookie);
+
+/**
+ * Helper function to populate an instance of struct chreWifiRangingTarget with
+ * the contents of a scan result provided in struct chreWifiScanResult.
+ * Populates other parameters that are not directly derived from the scan result
+ * with default values.
+ *
+ * @param scanResult The scan result to parse as input
+ * @param rangingTarget The RTT ranging target to populate as output
+ *
+ * @note Requires WiFi permission
+ */
+static inline void chreWifiRangingTargetFromScanResult(
+        const struct chreWifiScanResult *scanResult,
+        struct chreWifiRangingTarget *rangingTarget) {
+    memcpy(rangingTarget->macAddress, scanResult->bssid,
+           sizeof(rangingTarget->macAddress));
+    rangingTarget->primaryChannel      = scanResult->primaryChannel;
+    rangingTarget->centerFreqPrimary   = scanResult->centerFreqPrimary;
+    rangingTarget->centerFreqSecondary = scanResult->centerFreqSecondary;
+    rangingTarget->channelWidth        = scanResult->channelWidth;
+
+    // Note that this is not strictly necessary (CHRE can see which API version
+    // the nanoapp was built against, so it knows to ignore these fields), but
+    // we do it here to keep things nice and tidy
+    memset(rangingTarget->reserved, 0, sizeof(rangingTarget->reserved));
+}
+
+/**
+ * Subscribe to a NAN service.
+ *
+ * Sends a subscription request to the NAN discovery engine with the
+ * specified configuration parameters. If successful, a unique non-zero
+ * subscription ID associated with this instance of the subscription
+ * request is assigned by the NAN discovery engine. The subscription request
+ * is active until explicitly canceled, or if the connection was interrupted.
+ *
+ * Note that CHRE forwards any discovery events that it receives to the
+ * subscribe function instance, and does no duplicate filtering. If
+ * multiple events of the same discovery are undesirable, it is up to the
+ * platform NAN discovery engine implementation to implement redundancy
+ * detection mechanisms.
+ *
+ * If WiFi is turned off by the user at the Android level, an existing
+ * subscribe session is canceled, and a CHRE_EVENT_WIFI_ASYNC_RESULT event is
+ * event is sent to the subscriber. Nanoapps are expected to register for user
+ * settings notifications (@see chreUserSettingConfigureEvents), and
+ * re-establish a subscribe session on a WiFi re-enabled settings changed
+ * notification.
+ *
+ * @param config Service subscription configuration
+ * @param cookie A value that the nanoapp uses to track this particular
+ *        subscription request.
+ * @return true if NAN is enabled and a subscription request was successfully
+ *         made to the NAN engine. The actual result of the service discovery
+ *         is sent via a CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT event.
+ *
+ * @since v1.6
+ * @note Requires WiFi permission
+ */
+bool chreWifiNanSubscribe(struct chreWifiNanSubscribeConfig *config,
+                          const void *cookie);
+
+/**
+ * Cancel a subscribe function instance.
+ *
+ * @param subscriptionId The ID that was originally assigned to this instance
+ *        of the subscribe function.
+ * @return true if NAN is enabled, the subscribe ID  was found and the instance
+ *         successfully canceled.
+ *
+ * @since v1.6
+ * @note Requires WiFi permission
+ */
+bool chreWifiNanSubscribeCancel(uint32_t subscriptionID);
+
+/**
+ * Request RTT ranging from a peer NAN device.
+ *
+ * Nanoapps can use this API to explicitly request measurement reports from
+ * the peer device. Note that both end points have to support ranging for a
+ * successful request. The MAC address of the peer NAN device for which ranging
+ * is desired may be obtained either from a NAN service discovery or from an
+ * out-of-band source (HAL service, BLE, etc.).
+ *
+ * If WiFi is turned off by the user at the Android level, an existing
+ * ranging session is canceled, and a CHRE_EVENT_WIFI_ASYNC_RESULT event is
+ * sent to the subscriber. Nanoapps are expected to register for user settings
+ * notifications (@see chreUserSettingConfigureEvents), and perform another
+ * ranging request on a WiFi re-enabled settings changed notification.
+ *
+ * A successful result provided in CHRE_EVENT_WIFI_ASYNC_RESULT indicates that
+ * the results of ranging will be delivered in a subsequent event of type
+ * CHRE_EVENT_WIFI_RANGING_RESULT.
+ *
+ * @param params Structure containing the parameters of the ranging request,
+ *        including the MAC address of the peer NAN device to attempt ranging.
+ * @param cookie An opaque value that will be included in the chreAsyncResult
+ *        sent in relation to this request.
+ * @return true if the request was accepted for processing, false otherwise.
+ */
+bool chreWifiNanRequestRangingAsync(const struct chreWifiNanRangingParams *params,
+                                    const void *cookie);
+
+#else  /* defined(CHRE_NANOAPP_USES_WIFI) || !defined(CHRE_IS_NANOAPP_BUILD) */
+#define CHRE_WIFI_PERM_ERROR_STRING \
+    "CHRE_NANOAPP_USES_WIFI must be defined when building this nanoapp in " \
+    "order to refer to "
+#define chreWifiConfigureScanMonitorAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING \
+                     "chreWifiConfigureScanMonitorAsync")
+#define chreWifiRequestScanAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING \
+                     "chreWifiRequestScanAsync")
+#define chreWifiRequestScanAsyncDefault(...) \
+    CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING \
+                     "chreWifiRequestScanAsyncDefault")
+#define chreWifiRequestRangingAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING "chreWifiRequestRangingAsync")
+#define chreWifiRangingTargetFromScanResult(...) \
+    CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING \
+                     "chreWifiRangingTargetFromScanResult")
+#define chreWifiNanSubscribe(...) \
+    CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING "chreWifiNanSubscribe")
+#define chreWifiNanSubscribeCancel(...) \
+    CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING "chreWifiNanSubscribeCancel")
+#define chreWifiNanRequestRangingAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_WIFI_PERM_ERROR_STRING "chreWifiNanRequestRangingAsync")
+#endif  /* defined(CHRE_NANOAPP_USES_WIFI) || !defined(CHRE_IS_NANOAPP_BUILD) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_WIFI_H_ */
diff --git a/chre_api/legacy/v1_10/chre/wwan.h b/chre_api/legacy/v1_10/chre/wwan.h
new file mode 100644
index 0000000..dc46182
--- /dev/null
+++ b/chre_api/legacy/v1_10/chre/wwan.h
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// IWYU pragma: private, include "chre_api/chre.h"
+// IWYU pragma: friend chre/.*\.h
+
+#ifndef _CHRE_WWAN_H_
+#define _CHRE_WWAN_H_
+
+/**
+ * @file
+ * Wireless Wide Area Network (WWAN, i.e. mobile/cellular network) API relevant
+ * for querying cell tower identity and associated information that can be
+ * useful in determining location.
+ *
+ * Based on Android N RIL definitions (located at this path as of the time of
+ * this comment: hardware/ril/include/telephony/ril.h), version 12. Updated
+ * based on Android radio HAL definition (hardware/interfaces/radio) for more
+ * recent Android builds. Refer to those files and associated documentation for
+ * further details.
+ *
+ * In general, the parts of this API that are taken from the RIL follow the
+ * field naming conventions established in that interface rather than the CHRE
+ * API conventions, in order to avoid confusion and enable code re-use where
+ * applicable. Note that structure names include the chreWwan* prefix rather
+ * than RIL_*, but field names are the same. If necessary to enable code
+ * sharing, it is recommended to create typedefs that map from the CHRE
+ * structures to the associated RIL type names, for example "typedef struct
+ * chreWwanCellIdentityGsm RIL_CellIdentityGsm_v12", etc.
+ */
+
+#include <chre/common.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The set of flags returned by chreWwanGetCapabilities().
+ * @defgroup CHRE_WWAN_CAPABILITIES
+ * @{
+ */
+
+//! No WWAN APIs are supported
+#define CHRE_WWAN_CAPABILITIES_NONE  UINT32_C(0)
+
+//! Current cell information can be queried via chreWwanGetCellInfoAsync()
+#define CHRE_WWAN_GET_CELL_INFO      UINT32_C(1 << 0)
+
+/** @} */
+
+/**
+ * Produce an event ID in the block of IDs reserved for WWAN
+ * @param offset  Index into WWAN event ID block; valid range [0,15]
+ */
+#define CHRE_WWAN_EVENT_ID(offset)  (CHRE_EVENT_WWAN_FIRST_EVENT + (offset))
+
+/**
+ * nanoappHandleEvent argument: struct chreWwanCellInfoResult
+ *
+ * Provides the result of an asynchronous request for cell info sent via
+ * chreWwanGetCellInfoAsync().
+ */
+#define CHRE_EVENT_WWAN_CELL_INFO_RESULT  CHRE_WWAN_EVENT_ID(0)
+
+// NOTE: Do not add new events with ID > 15; only values 0-15 are reserved
+// (see chre/event.h)
+
+/**
+ * The current version of struct chreWwanCellInfoResult associated with this
+ * API definition.
+ */
+#define CHRE_WWAN_CELL_INFO_RESULT_VERSION  UINT8_C(1)
+
+//! Reference: RIL_CellIdentityGsm_v12
+struct chreWwanCellIdentityGsm {
+    //! 3-digit Mobile Country Code, 0..999, INT32_MAX if unknown
+    int32_t mcc;
+
+    //! 2 or 3-digit Mobile Network Code, 0..999, INT32_MAX if unknown
+    int32_t mnc;
+
+    //! 16-bit Location Area Code, 0..65535, INT32_MAX if unknown
+    int32_t lac;
+
+    //! 16-bit GSM Cell Identity described in TS 27.007, 0..65535,
+    //! INT32_MAX if unknown
+    int32_t cid;
+
+    //! 16-bit GSM Absolute RF channel number, INT32_MAX if unknown
+    int32_t arfcn;
+
+    //! 6-bit Base Station Identity Code, UINT8_MAX if unknown
+    uint8_t bsic;
+
+    //! Reserved for future use; must be set to 0
+    uint8_t reserved[3];
+};
+
+//! Reference: RIL_CellIdentityWcdma_v12
+struct chreWwanCellIdentityWcdma {
+    //! 3-digit Mobile Country Code, 0..999, INT32_MAX if unknown
+    int32_t mcc;
+
+    //! 2 or 3-digit Mobile Network Code, 0..999, INT32_MAX if unknown
+    int32_t mnc;
+
+    //! 16-bit Location Area Code, 0..65535, INT32_MAX if unknown
+    int32_t lac;
+
+    //! 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+    //! INT32_MAX if unknown
+    int32_t cid;
+
+    //! 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511,
+    //! INT32_MAX if unknown
+    int32_t psc;
+
+    //! 16-bit UMTS Absolute RF Channel Number, INT32_MAX if unknown
+    int32_t uarfcn;
+};
+
+//! Reference: RIL_CellIdentityCdma
+struct chreWwanCellIdentityCdma {
+    //! Network Id 0..65535, INT32_MAX if unknown
+    int32_t networkId;
+
+    //! CDMA System Id 0..32767, INT32_MAX if unknown
+    int32_t systemId;
+
+    //! Base Station Id 0..65535, INT32_MAX if unknown
+    int32_t basestationId;
+
+    //! Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+    //! It is represented in units of 0.25 seconds and ranges from -2592000
+    //! to 2592000, both values inclusive (corresponding to a range of -180
+    //! to +180 degrees). INT32_MAX if unknown
+    int32_t longitude;
+
+    //! Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+    //! It is represented in units of 0.25 seconds and ranges from -1296000
+    //! to 1296000, both values inclusive (corresponding to a range of -90
+    //! to +90 degrees). INT32_MAX if unknown
+    int32_t latitude;
+};
+
+//! Reference: RIL_CellIdentityLte_v12
+struct chreWwanCellIdentityLte {
+    //! 3-digit Mobile Country Code, 0..999, INT32_MAX if unknown
+    int32_t mcc;
+
+    //! 2 or 3-digit Mobile Network Code, 0..999, INT32_MAX if unknown
+    int32_t mnc;
+
+    //! 28-bit Cell Identity described in TS ???, INT32_MAX if unknown
+    int32_t ci;
+
+    //! physical cell id 0..503, INT32_MAX if unknown
+    int32_t pci;
+
+    //! 16-bit tracking area code, INT32_MAX if unknown
+    int32_t tac;
+
+    //! 18-bit LTE Absolute RF Channel Number, INT32_MAX if unknown
+    int32_t earfcn;
+};
+
+//! Reference: RIL_CellIdentityTdscdma
+struct chreWwanCellIdentityTdscdma {
+    //! 3-digit Mobile Country Code, 0..999, INT32_MAX if unknown
+    int32_t mcc;
+
+    //! 2 or 3-digit Mobile Network Code, 0..999, INT32_MAX if unknown
+    int32_t mnc;
+
+    //! 16-bit Location Area Code, 0..65535, INT32_MAX if unknown
+    int32_t lac;
+
+    //! 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+    //! INT32_MAX if unknown
+    int32_t cid;
+
+    //! 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT32_MAX if
+    //! unknown
+    int32_t cpid;
+};
+
+//! Reference: android.hardware.radio@1.4 CellIdentityNr
+//! @since v1.4
+struct chreWwanCellIdentityNr {
+    //! 3-digit Mobile Country Code, in range [0, 999]. This value must be valid
+    //! for registered or camped cells. INT32_MAX means invalid/unreported.
+    int32_t mcc;
+
+    //! 2 or 3-digit Mobile Network Code, in range [0, 999]. This value must be
+    //! valid for registered or camped cells. INT32_MAX means
+    //! invalid/unreported.
+    int32_t mnc;
+
+    //! NR Cell Identity in range [0, 68719476735] (36 bits), which
+    //! unambiguously identifies a cell within a public land mobile network
+    //! (PLMN). This value must be valid for registered or camped cells.
+    //! Reference: TS 38.413 section 9.3.1.7.
+    //!
+    //! Note: for backward compatibility reasons, the nominally int64_t nci is
+    //! split into two uint32_t values, with nci0 being the least significant 4
+    //! bytes. If chreWwanUnpackNrNci returns INT64_MAX, it means nci is
+    //! invalid/unreported.
+    //!
+    //! Users are recommended to use the helper accessor chreWwanUnpackNrNci to
+    //! access the nci field.
+    //!
+    //! @see chreWwanUnpackNrNci
+    uint32_t nci0;
+    uint32_t nci1;
+
+    //! Physical cell id in range [0, 1007]. This value must be valid.
+    //! Reference: TS 38.331 section 6.3.2.
+    int32_t pci;
+
+    //! 24-bit tracking area code in range [0, 16777215]. INT32_MAX means
+    //! invalid/unreported.
+    //! Reference: TS 38.413 section 9.3.3.10 and TS 29.571 section 5.4.2.
+    int32_t tac;
+
+    //! NR Absolute Radio Frequency Channel Number, in range [0, 3279165]. This
+    //! value must be valid.
+    //! Reference: TS 38.101-1 section 5.4.2.1 and TS 38.101-2 section 5.4.2.1.
+    int32_t nrarfcn;
+};
+
+//! Reference: RIL_GSM_SignalStrength_v12
+struct chreWwanSignalStrengthGsm {
+    //! Valid values are (0-31, 99) as defined in TS 27.007 8.5
+    //! INT32_MAX means invalid/unreported.
+    int32_t signalStrength;
+
+    //! bit error rate (0-7, 99) as defined in TS 27.007 8.5
+    //! INT32_MAX means invalid/unreported.
+    int32_t bitErrorRate;
+
+    //! Timing Advance in bit periods. 1 bit period = 48.13 us.
+    //! INT32_MAX means invalid/unreported.
+    int32_t timingAdvance;
+};
+
+//! Reference: RIL_SignalStrengthWcdma
+struct chreWwanSignalStrengthWcdma {
+    //! Valid values are (0-31, 99) as defined in TS 27.007 8.5
+    //! INT32_MAX means invalid/unreported.
+    int32_t signalStrength;
+
+    //! bit error rate (0-7, 99) as defined in TS 27.007 8.5
+    //! INT32_MAX means invalid/unreported.
+    int32_t bitErrorRate;
+};
+
+//! Reference: RIL_CDMA_SignalStrength
+struct chreWwanSignalStrengthCdma {
+    //! Valid values are positive integers.  This value is the actual RSSI value
+    //! multiplied by -1.  Example: If the actual RSSI is -75, then this
+    //! response value will be 75.
+    //! INT32_MAX means invalid/unreported.
+    int32_t dbm;
+
+    //! Valid values are positive integers.  This value is the actual Ec/Io
+    //! multiplied by -10.  Example: If the actual Ec/Io is -12.5 dB, then this
+    //! response value will be 125.
+    //! INT32_MAX means invalid/unreported.
+    int32_t ecio;
+};
+
+//! Reference: RIL_EVDO_SignalStrength
+struct chreWwanSignalStrengthEvdo {
+    //! Valid values are positive integers.  This value is the actual RSSI value
+    //! multiplied by -1.  Example: If the actual RSSI is -75, then this
+    //! response value will be 75.
+    //! INT32_MAX means invalid/unreported.
+    int32_t dbm;
+
+    //! Valid values are positive integers.  This value is the actual Ec/Io
+    //! multiplied by -10.  Example: If the actual Ec/Io is -12.5 dB, then this
+    //! response value will be 125.
+    //! INT32_MAX means invalid/unreported.
+    int32_t ecio;
+
+    //! Valid values are 0-8.  8 is the highest signal to noise ratio.
+    //! INT32_MAX means invalid/unreported.
+    int32_t signalNoiseRatio;
+};
+
+//! Reference: RIL_LTE_SignalStrength_v8
+struct chreWwanSignalStrengthLte {
+    //! Valid values are (0-31, 99) as defined in TS 27.007 8.5
+    int32_t signalStrength;
+
+    //! The current Reference Signal Receive Power in dBm multiplied by -1.
+    //! Range: 44 to 140 dBm
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: 3GPP TS 36.133 9.1.4
+    int32_t rsrp;
+
+    //! The current Reference Signal Receive Quality in dB multiplied by -1.
+    //! Range: 3 to 20 dB.
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: 3GPP TS 36.133 9.1.7
+    int32_t rsrq;
+
+    //! The current reference signal signal-to-noise ratio in 0.1 dB units.
+    //! Range: -200 to +300 (-200 = -20.0 dB, +300 = 30dB).
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: 3GPP TS 36.101 8.1.1
+    int32_t rssnr;
+
+    //! The current Channel Quality Indicator.
+    //! Range: 0 to 15.
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: 3GPP TS 36.101 9.2, 9.3, A.4
+    int32_t cqi;
+
+    //! timing advance in micro seconds for a one way trip from cell to device.
+    //! Approximate distance can be calculated using 300m/us * timingAdvance.
+    //! Range: 0 to 0x7FFFFFFE
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: 3GPP 36.321 section 6.1.3.5
+    //! also: http://www.cellular-planningoptimization.com/2010/02/timing-advance-with-calculation.html
+    int32_t timingAdvance;
+};
+
+//! Reference: RIL_TD_SCDMA_SignalStrength
+struct chreWwanSignalStrengthTdscdma {
+    //! The Received Signal Code Power in dBm multiplied by -1.
+    //! Range : 25 to 120
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: 3GPP TS 25.123, section 9.1.1.1
+    int32_t rscp;
+};
+
+//! Reference: android.hardware.radio@1.4 NrSignalStrength
+//! @since v1.4
+struct chreWwanSignalStrengthNr {
+    //! SS (second synchronization) reference signal received power in dBm
+    //! multiplied by -1.
+    //! Range [44, 140], INT32_MAX means invalid/unreported.
+    //! Reference: TS 38.215 section 5.1.1 and TS 38.133 section 10.1.6.
+    int32_t ssRsrp;
+
+    //! SS reference signal received quality in 0.5 dB units.
+    //! Range [-86, 41] with -86 = -43.0 dB and 41 = 20.5 dB.
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: TS 38.215 section 5.1.3 and TS 38.133 section 10.1.11.1.
+    int32_t ssRsrq;
+
+    //! SS signal-to-noise and interference ratio in 0.5 dB units.
+    //! Range [-46, 81] with -46 = -23.0 dB and 81 = 40.5 dB.
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: TS 38.215 section 5.1.5 and TS 38.133 section 10.1.16.1.
+    int32_t ssSinr;
+
+    //! CSI reference signal received power in dBm multiplied by -1.
+    //! Range [44, 140], INT32_MAX means invalid/unreported.
+    //! Reference: TS 38.215 section 5.1.2 and TS 38.133 section 10.1.6.
+    int32_t csiRsrp;
+
+    //! CSI reference signal received quality in 0.5 dB units.
+    //! Range [-86, 41] with -86 = -43.0 dB and 41 = 20.5 dB.
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: TS 38.215 section 5.1.4 and TS 38.133 section 10.1.11.1.
+    int32_t csiRsrq;
+
+    //! CSI signal-to-noise and interference ratio in 0.5 dB units.
+    //! Range [-46, 81] with -46 = -23.0 dB and 81 = 40.5 dB.
+    //! INT32_MAX means invalid/unreported.
+    //! Reference: TS 38.215 section 5.1.6 and TS 38.133 section 10.1.16.1.
+    int32_t csiSinr;
+};
+
+//! Reference: RIL_CellInfoGsm_v12
+struct chreWwanCellInfoGsm {
+    struct chreWwanCellIdentityGsm    cellIdentityGsm;
+    struct chreWwanSignalStrengthGsm  signalStrengthGsm;
+};
+
+//! Reference: RIL_CellInfoWcdma_v12
+struct chreWwanCellInfoWcdma {
+    struct chreWwanCellIdentityWcdma    cellIdentityWcdma;
+    struct chreWwanSignalStrengthWcdma  signalStrengthWcdma;
+};
+
+//! Reference: RIL_CellInfoCdma
+struct chreWwanCellInfoCdma {
+    struct chreWwanCellIdentityCdma    cellIdentityCdma;
+    struct chreWwanSignalStrengthCdma  signalStrengthCdma;
+    struct chreWwanSignalStrengthEvdo  signalStrengthEvdo;
+};
+
+//! Reference: RIL_CellInfoLte_v12
+struct chreWwanCellInfoLte {
+    struct chreWwanCellIdentityLte    cellIdentityLte;
+    struct chreWwanSignalStrengthLte  signalStrengthLte;
+};
+
+//! Reference: RIL_CellInfoTdscdma
+struct chreWwanCellInfoTdscdma {
+    struct chreWwanCellIdentityTdscdma    cellIdentityTdscdma;
+    struct chreWwanSignalStrengthTdscdma  signalStrengthTdscdma;
+};
+
+//! Reference: android.hardware.radio@1.4 CellInfoNr
+//! @since v1.4
+struct chreWwanCellInfoNr {
+    struct chreWwanCellIdentityNr    cellIdentityNr;
+    struct chreWwanSignalStrengthNr  signalStrengthNr;
+};
+
+//! Reference: RIL_CellInfoType
+//! All other values are reserved and should be ignored by nanoapps.
+enum chreWwanCellInfoType {
+    CHRE_WWAN_CELL_INFO_TYPE_GSM      = 1,
+    CHRE_WWAN_CELL_INFO_TYPE_CDMA     = 2,
+    CHRE_WWAN_CELL_INFO_TYPE_LTE      = 3,
+    CHRE_WWAN_CELL_INFO_TYPE_WCDMA    = 4,
+    CHRE_WWAN_CELL_INFO_TYPE_TD_SCDMA = 5,
+    CHRE_WWAN_CELL_INFO_TYPE_NR       = 6,  //! @since v1.4
+};
+
+//! Reference: RIL_TimeStampType
+enum chreWwanCellTimeStampType {
+    CHRE_WWAN_CELL_TIMESTAMP_TYPE_UNKNOWN  = 0,
+    CHRE_WWAN_CELL_TIMESTAMP_TYPE_ANTENNA  = 1,
+    CHRE_WWAN_CELL_TIMESTAMP_TYPE_MODEM    = 2,
+    CHRE_WWAN_CELL_TIMESTAMP_TYPE_OEM_RIL  = 3,
+    CHRE_WWAN_CELL_TIMESTAMP_TYPE_JAVA_RIL = 4,
+};
+
+//! Reference: RIL_CellInfo_v12
+struct chreWwanCellInfo {
+    //! Timestamp in nanoseconds; must be in the same time base as chreGetTime()
+    uint64_t timeStamp;
+
+    //! A value from enum {@link #CellInfoType} indicating the radio access
+    //! technology of the cell, and which field in union CellInfo can be used
+    //! to retrieve additional information
+    uint8_t cellInfoType;
+
+    //! A value from enum {@link #CellTimeStampType} that identifies the source
+    //! of the value in timeStamp. This is typically set to
+    //! CHRE_WWAN_CELL_TIMESTAMP_TYPE_OEM_RIL, and indicates the time given by
+    //! chreGetTime() that an intermediate module received the data from the
+    //! modem and forwarded it to the requesting CHRE client.
+    uint8_t timeStampType;
+
+    //! !0 if this cell is registered, 0 if not registered
+    uint8_t registered;
+
+    //! Reserved for future use; must be set to 0
+    uint8_t reserved;
+
+    //! The value in cellInfoType indicates which field in this union is valid
+    union chreWwanCellInfoPerRat {
+        struct chreWwanCellInfoGsm     gsm;
+        struct chreWwanCellInfoCdma    cdma;
+        struct chreWwanCellInfoLte     lte;
+        struct chreWwanCellInfoWcdma   wcdma;
+        struct chreWwanCellInfoTdscdma tdscdma;
+        struct chreWwanCellInfoNr      nr;  //! @since v1.4
+    } CellInfo;
+};
+
+/**
+ * Data structure provided with events of type CHRE_EVENT_WWAN_CELL_INFO_RESULT.
+ */
+struct chreWwanCellInfoResult {
+    //! Indicates the version of the structure, for compatibility purposes.
+    //! Clients do not normally need to worry about this field; the CHRE
+    //! implementation guarantees that the client only receives the structure
+    //! version it expects.
+    uint8_t version;
+
+    //! Populated with a value from enum {@link #chreError}, indicating whether
+    //! the request failed, and if so, provides the cause of the failure
+    uint8_t errorCode;
+
+    //! The number of valid entries in cells[]
+    uint8_t cellInfoCount;
+
+    //! Reserved for future use; must be set to 0
+    uint8_t reserved;
+
+    //! Set to the cookie parameter given to chreWwanGetCellInfoAsync()
+    const void *cookie;
+
+    //! Pointer to an array of cellInfoCount elements containing information
+    //! about serving and neighbor cells
+    const struct chreWwanCellInfo *cells;
+};
+
+
+/**
+ * Retrieves a set of flags indicating the WWAN features supported by the
+ * current CHRE implementation. The value returned by this function must be
+ * consistent for the entire duration of the Nanoapp's execution.
+ *
+ * The client must allow for more flags to be set in this response than it knows
+ * about, for example if the implementation supports a newer version of the API
+ * than the client was compiled against.
+ *
+ * @return A bitmask with zero or more CHRE_WWAN_CAPABILITIES_* flags set
+ *
+ * @since v1.1
+ */
+uint32_t chreWwanGetCapabilities(void);
+
+/**
+ * Nanoapps must define CHRE_NANOAPP_USES_WWAN somewhere in their build
+ * system (e.g. Makefile) if the nanoapp needs to use the following WWAN APIs.
+ * In addition to allowing access to these APIs, defining this macro will also
+ * ensure CHRE enforces that all host clients this nanoapp talks to have the
+ * required Android permissions needed to listen to WWAN data by adding metadata
+ * to the nanoapp.
+ */
+#if defined(CHRE_NANOAPP_USES_WWAN) || !defined(CHRE_IS_NANOAPP_BUILD)
+
+/**
+ * Query information about the current serving cell and its neighbors. This does
+ * not perform a network scan, but should return state from the current network
+ * registration data stored in the cellular modem. This is effectively the same
+ * as a request for RIL_REQUEST_GET_CELL_INFO_LIST in the RIL.
+ *
+ * The requested cellular information is returned asynchronously via
+ * CHRE_EVENT_WWAN_CELL_INFO_RESULT. The implementation must send this event,
+ * either with successful data or an error status, within
+ * CHRE_ASYNC_RESULT_TIMEOUT_NS.
+ *
+ * If the airplane mode setting is enabled at the Android level, the CHRE
+ * implementation is expected to return a successful asynchronous result with an
+ * empty cell info list.
+ *
+ * @param cookie An opaque value that will be included in the
+ *               chreWwanCellInfoResult sent in relation to this request.
+ *
+ * @return true if the request was accepted for processing, false otherwise
+ *
+ * @since v1.1
+ * @note Requires WWAN permission
+ */
+bool chreWwanGetCellInfoAsync(const void *cookie);
+
+/**
+ * Helper accessor for nci in the chreWwanCellIdentityNr struct.
+ *
+ * @return nci or INT64_MAX if invalid/unreported.
+ *
+ * @see chreWwanCellIdentityNr
+ *
+ * @since v1.4
+ * @note Requires WWAN permission
+ */
+static inline int64_t chreWwanUnpackNrNci(
+    const struct chreWwanCellIdentityNr *nrCellId) {
+  return (int64_t) (((uint64_t) nrCellId->nci1 << 32) | nrCellId->nci0);
+}
+
+#else  /* defined(CHRE_NANOAPP_USES_WWAN) || !defined(CHRE_IS_NANOAPP_BUILD) */
+#define CHRE_WWAN_PERM_ERROR_STRING \
+    "CHRE_NANOAPP_USES_WWAN must be defined when building this nanoapp in " \
+    "order to refer to "
+#define chreWwanGetCellInfoAsync(...) \
+    CHRE_BUILD_ERROR(CHRE_WWAN_PERM_ERROR_STRING "chreWwanGetCellInfoAsync")
+#define chreWwanUnpackNrNci(...) \
+    CHRE_BUILD_ERROR(CHRE_WWAN_PERM_ERROR_STRING "chreWwanUnpackNrNci")
+#endif  /* defined(CHRE_NANOAPP_USES_WWAN) || !defined(CHRE_IS_NANOAPP_BUILD) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _CHRE_WWAN_H_ */
diff --git a/chre_flags.aconfig b/chre_flags.aconfig
index 1fbb459..4348fd4 100644
--- a/chre_flags.aconfig
+++ b/chre_flags.aconfig
@@ -1,13 +1,20 @@
 package: "android.chre.flags"
 container: "system"
 
+# Exported flags should remain here indefinitely
+# Flags guarding APIs are exported
+# Begin removed exported flags (keep flag definition)
+
 flag {
-  name: "context_hub_callback_uuid_enabled"
+  name: "reliable_message"
+  is_exported: true
   namespace: "context_hub"
-  description: "Call IContextHubCallback.getUuid() to retrieve the UUID when this flag is on"
-  bug: "247124878"
+  description: "Enable the reliable message APIs"
+  bug: "314081414"
 }
 
+# End removed exported flags (keep flag definition)
+
 flag {
   name: "abort_if_no_context_hub_found"
   namespace: "context_hub"
@@ -23,21 +30,6 @@
 }
 
 flag {
-  name: "reliable_message"
-  is_exported: true
-  namespace: "context_hub"
-  description: "Enable the reliable message APIs"
-  bug: "314081414"
-}
-
-flag {
-  name: "reliable_message_implementation"
-  namespace: "context_hub"
-  description: "Enable support for reliable messages in CHRE"
-  bug: "314081414"
-}
-
-flag {
   name: "reliable_message_duplicate_detection_service"
   namespace: "context_hub"
   description: "Enable duplicate detection for reliable messages in the Context Hub Service"
@@ -96,20 +88,6 @@
 }
 
 flag {
-  name: "unified_metrics_reporting_api"
-  namespace: "context_hub"
-  description: "The API for unified metrics reporting in the Context Hub Service"
-  bug: "361804033"
-}
-
-flag {
-  name: "unified_metrics_reporting_implementation"
-  namespace: "context_hub"
-  description: "The implementation for unified metrics reporting in the Context Hub Service"
-  bug: "361804033"
-}
-
-flag {
   name: "reduce_locking_context_hub_transaction_manager"
   namespace: "context_hub"
   description: "Reduces locking in the ContextHubTransactionManager"
@@ -118,3 +96,55 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "offload_api"
+  namespace: "context_hub"
+  description: "Enables the generic offload APIs"
+  bug: "361573382"
+  is_exported: true
+}
+
+flag {
+  name: "offload_implementation"
+  namespace: "context_hub"
+  description: "Enables support for generic offload"
+  bug: "361573382"
+}
+
+flag {
+  name: "efw_xport_rewind_on_error"
+  namespace: "context_hub"
+  description: "Flag guarding the AOC-dependent behavior to rewind to the last good message"
+  bug: "371057943"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "efw_xport_in_context_hub"
+  namespace: "context_hub"
+  description: "Flag guarding the use of the new EFW transport for ContextHub <-> CHRE comms"
+  bug: "369883034"
+}
+
+flag {
+  name: "bug_fix_remove_exit_call_in_hal"
+  namespace: "context_hub"
+  description: "Flag enabling multiclient HAL to gracefully handle connection to CHRE in init/restart"
+  bug: "374773993"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "hal_handle_nanoapp_query_test_mode"
+  namespace: "context_hub"
+  description: "Flag guarding the fix for nanoapp query handling when enabling test mode"
+  bug: "379342519"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
new file mode 100644
index 0000000..3d5485f
--- /dev/null
+++ b/core/CMakeLists.txt
@@ -0,0 +1,100 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_library(chre.core STATIC
+  HEADERS
+    include/chre/core/host_comms_manager.h
+    include/chre/core/settings.h
+    include/chre/core/audio_request_manager.h
+    include/chre/core/api_manager_common.h
+    include/chre/core/wifi_request_manager.h
+    include/chre/core/event_ref_queue.h
+    include/chre/core/sensor_request_multiplexer.h
+    include/chre/core/audio_util.h
+    include/chre/core/gnss_manager.h
+    include/chre/core/init.h
+    include/chre/core/timer_pool.h
+    include/chre/core/event_loop_common.h
+    include/chre/core/sensor_type.h
+    include/chre/core/wwan_request_manager.h
+    include/chre/core/event_loop.h
+    include/chre/core/sensor_request.h
+    include/chre/core/ble_request_multiplexer.h
+    include/chre/core/static_nanoapps.h
+    include/chre/core/sensor.h
+    include/chre/core/ble_request_manager.h
+    include/chre/core/request_multiplexer.h
+    include/chre/core/event_loop_manager.h
+    include/chre/core/system_health_monitor.h
+    include/chre/core/request_multiplexer_impl.h
+    include/chre/core/debug_dump_manager.h
+    include/chre/core/nanoapp.h
+    include/chre/core/telemetry_manager.h
+    include/chre/core/sensor_type_helpers.h
+    include/chre/core/wifi_scan_request.h
+    include/chre/core/sensor_request_manager.h
+    include/chre/core/event.h
+    include/chre/core/ble_request.h
+    include/chre/core/host_endpoint_manager.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+    chre.platform.assert
+    chre.platform.atomic
+    chre.platform.fatal_error
+    chre.platform.host_link
+    chre.platform.log
+    chre.platform.memory_manager
+    chre.platform.mutex
+    chre.platform.platform_audio
+    chre.platform.platform_ble
+    chre.platform.platform_debug_dump_manager
+    chre.platform.platform_gnss
+    chre.platform.platform_nanoapp
+    chre.platform.platform_sensor
+    chre.platform.platform_sensor_manager
+    chre.platform.platform_sensor_type_helpers
+    chre.platform.platform_wifi
+    chre.platform.platform_wwan
+    chre.platform.power_control_manager
+    chre.platform.system_time
+    chre.platform.system_timer
+    chre.util
+    chre.util.system
+    chre.variant.config
+    pw_function
+  SOURCES
+    audio_request_manager.cc
+    ble_request.cc
+    ble_request_manager.cc
+    ble_request_multiplexer.cc
+    debug_dump_manager.cc
+    event.cc
+    event_loop.cc
+    event_loop_manager.cc
+    event_ref_queue.cc
+    gnss_manager.cc
+    host_comms_manager.cc
+    host_endpoint_manager.cc
+    init.cc
+    nanoapp.cc
+    sensor.cc
+    sensor_request.cc
+    sensor_request_manager.cc
+    sensor_request_multiplexer.cc
+    sensor_type.cc
+    sensor_type_helpers.cc
+    settings.cc
+    static_nanoapps.cc
+    system_health_monitor.cc
+    timer_pool.cc
+    wifi_request_manager.cc
+    wifi_scan_request.cc
+    wwan_request_manager.cc
+  PRIVATE_DEPS
+    chre.platform.context
+    chre.platform.log
+    chre.platform.memory
+    chre.platform.tracing
+    chre.platform.version
+)
diff --git a/core/audio_request_manager.cc b/core/audio_request_manager.cc
index 47c919d..360a3e0 100644
--- a/core/audio_request_manager.cc
+++ b/core/audio_request_manager.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_AUDIO_SUPPORT_ENABLED
+
 #include "chre/core/audio_request_manager.h"
 
 #include "chre/core/audio_util.h"
@@ -513,3 +515,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_AUDIO_SUPPORT_ENABLED
diff --git a/core/ble_request_manager.cc b/core/ble_request_manager.cc
index ed93e3e..577a623 100644
--- a/core/ble_request_manager.cc
+++ b/core/ble_request_manager.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_BLE_SUPPORT_ENABLED
+
 #include "chre/core/ble_request_manager.h"
 
 #include "chre/core/event_loop_manager.h"
@@ -716,3 +718,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_BLE_SUPPORT_ENABLED
diff --git a/core/chre_message_hub_manager.cc b/core/chre_message_hub_manager.cc
new file mode 100644
index 0000000..96ed194
--- /dev/null
+++ b/core/chre_message_hub_manager.cc
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
+
+#include "chre/core/chre_message_hub_manager.h"
+#include "chre/core/event_loop_common.h"
+#include "chre/core/event_loop_manager.h"
+#include "chre/core/nanoapp.h"
+#include "chre/util/system/message_common.h"
+#include "chre/util/system/message_router.h"
+#include "chre/util/unique_ptr.h"
+
+#include <cinttypes>
+#include <optional>
+
+using ::chre::message::Endpoint;
+using ::chre::message::EndpointId;
+using ::chre::message::EndpointInfo;
+using ::chre::message::EndpointType;
+using ::chre::message::Message;
+using ::chre::message::MessageRouter;
+using ::chre::message::MessageRouterSingleton;
+using ::chre::message::Session;
+
+namespace chre {
+
+void ChreMessageHubManager::init() {
+  std::optional<MessageRouter::MessageHub> chreMessageHub =
+      MessageRouterSingleton::get()->registerMessageHub(
+          "CHRE", kChreMessageHubId, *this);
+  if (chreMessageHub.has_value()) {
+    mChreMessageHub = std::move(*chreMessageHub);
+  } else {
+    LOGE("Failed to register the CHRE MessageHub");
+  }
+}
+
+void ChreMessageHubManager::onMessageToNanoappCallback(
+    SystemCallbackType /* type */, UniquePtr<MessageCallbackData> &&data) {
+  bool success = false;
+  Nanoapp *nanoapp =
+      EventLoopManagerSingleton::get()->getEventLoop().findNanoappByAppId(
+          data->nanoappId);
+  uint32_t messagePermissions = data->messageToNanoapp.messagePermissions;
+  if (nanoapp == nullptr) {
+    LOGE("Unable to find nanoapp with ID 0x%" PRIx64
+         " to receive message with type %" PRIu32 " and permissions %" PRIu32
+         " with session ID %" PRIu16,
+         data->nanoappId, data->messageToNanoapp.messageType,
+         data->messageToNanoapp.messagePermissions,
+         data->messageToNanoapp.sessionId);
+  } else if (!nanoapp->hasPermissions(messagePermissions)) {
+    LOGE("nanoapp with ID 0x%" PRIx64
+         " does not have permissions to receive "
+         "message with type %" PRIu32 " and permissions 0x%" PRIx32,
+         nanoapp->getAppId(), data->messageToNanoapp.messageType,
+         data->messageToNanoapp.messagePermissions);
+  } else if (!EventLoopManagerSingleton::get()
+                  ->getEventLoop()
+                  .distributeEventSync(CHRE_EVENT_MESSAGE_FROM_ENDPOINT,
+                                       &data->messageToNanoapp,
+                                       nanoapp->getInstanceId())) {
+    LOGE("Unable to distribute message to nanoapp with ID 0x%" PRIx64,
+         nanoapp->getAppId());
+  } else {
+    success = true;
+  }
+
+  // Close session on failure so sender knows there was an issue
+  if (!success) {
+    EventLoopManagerSingleton::get()
+        ->getChreMessageHubManager()
+        .getMessageHub()
+        .closeSession(data->messageToNanoapp.sessionId);
+  }
+}
+
+void ChreMessageHubManager::onSessionClosedCallback(
+    SystemCallbackType /* type */,
+    UniquePtr<SessionClosedCallbackData> &&data) {
+  Nanoapp *nanoapp =
+      EventLoopManagerSingleton::get()->getEventLoop().findNanoappByAppId(
+          data->nanoappId);
+  if (nanoapp == nullptr) {
+    LOGE("Unable to find nanoapp with ID 0x%" PRIx64
+         " to close the session with ID %" PRIu16,
+         data->nanoappId, data->sessionClosedData.sessionId);
+    return;
+  }
+
+  bool success =
+      EventLoopManagerSingleton::get()->getEventLoop().distributeEventSync(
+          CHRE_EVENT_ENDPOINT_SESSION_CLOSED, &data->sessionClosedData,
+          nanoapp->getInstanceId());
+  if (!success) {
+    LOGE("Unable to process session closed event to nanoapp with ID 0x%" PRIx64,
+         nanoapp->getAppId());
+  }
+}
+
+bool ChreMessageHubManager::onMessageReceived(pw::UniquePtr<std::byte[]> &&data,
+                                              size_t length,
+                                              uint32_t messageType,
+                                              uint32_t messagePermissions,
+                                              const Session &session,
+                                              bool sentBySessionInitiator) {
+  Endpoint receiver = sentBySessionInitiator ? session.peer : session.initiator;
+  auto messageCallbackData = MakeUnique<MessageCallbackData>();
+  if (messageCallbackData.isNull()) {
+    LOG_OOM();
+    return false;
+  }
+
+  messageCallbackData->messageToNanoapp = {
+      .messageType = messageType,
+      .messagePermissions = messagePermissions,
+      .message = data.get(),
+      .messageSize = length,
+      .sessionId = session.sessionId,
+  };
+  messageCallbackData->data = std::move(data);
+  messageCallbackData->nanoappId = receiver.endpointId;
+
+  EventLoopManagerSingleton::get()->deferCallback(
+      SystemCallbackType::EndpointMessageToNanoappEvent,
+      std::move(messageCallbackData),
+      ChreMessageHubManager::onMessageToNanoappCallback);
+  return true;
+}
+
+void ChreMessageHubManager::onSessionClosed(const Session &session) {
+  auto sessionClosedCallbackData = MakeUnique<SessionClosedCallbackData>();
+  if (sessionClosedCallbackData.isNull()) {
+    LOG_OOM();
+    return;
+  }
+
+  Endpoint otherParty;
+  uint64_t nanoappId;
+  if (session.initiator.messageHubId == kChreMessageHubId) {
+    otherParty = session.peer;
+    nanoappId = session.initiator.endpointId;
+  } else {
+    otherParty = session.initiator;
+    nanoappId = session.peer.endpointId;
+  }
+
+  sessionClosedCallbackData->sessionClosedData = {
+      .hubId = otherParty.messageHubId,
+      .endpointId = otherParty.endpointId,
+      .sessionId = session.sessionId,
+  };
+  sessionClosedCallbackData->nanoappId = nanoappId;
+
+  EventLoopManagerSingleton::get()->deferCallback(
+      SystemCallbackType::EndpointSessionClosedEvent,
+      std::move(sessionClosedCallbackData),
+      ChreMessageHubManager::onSessionClosedCallback);
+}
+
+void ChreMessageHubManager::forEachEndpoint(
+    const pw::Function<bool(const EndpointInfo &)> &function) {
+  EventLoopManagerSingleton::get()->getEventLoop().onMatchingNanoappEndpoint(
+      function);
+}
+
+std::optional<EndpointInfo> ChreMessageHubManager::getEndpointInfo(
+    EndpointId endpointId) {
+  return EventLoopManagerSingleton::get()->getEventLoop().getEndpointInfo(
+      endpointId);
+}
+
+}  // namespace chre
+
+#endif  // CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
diff --git a/core/core.mk b/core/core.mk
index 02aa3ec..d931458 100644
--- a/core/core.mk
+++ b/core/core.mk
@@ -9,6 +9,7 @@
 
 # Common Source Files ##########################################################
 
+COMMON_SRCS += $(CHRE_PREFIX)/core/chre_message_hub_manager.cc
 COMMON_SRCS += $(CHRE_PREFIX)/core/debug_dump_manager.cc
 COMMON_SRCS += $(CHRE_PREFIX)/core/event.cc
 COMMON_SRCS += $(CHRE_PREFIX)/core/event_loop.cc
@@ -17,7 +18,6 @@
 COMMON_SRCS += $(CHRE_PREFIX)/core/host_comms_manager.cc
 COMMON_SRCS += $(CHRE_PREFIX)/core/host_endpoint_manager.cc
 COMMON_SRCS += $(CHRE_PREFIX)/core/init.cc
-COMMON_SRCS += $(CHRE_PREFIX)/core/log.cc
 COMMON_SRCS += $(CHRE_PREFIX)/core/nanoapp.cc
 COMMON_SRCS += $(CHRE_PREFIX)/core/settings.cc
 COMMON_SRCS += $(CHRE_PREFIX)/core/static_nanoapps.cc
diff --git a/core/event_loop.cc b/core/event_loop.cc
index 7c304c9..d5063e8 100644
--- a/core/event_loop.cc
+++ b/core/event_loop.cc
@@ -24,6 +24,7 @@
 #include "chre/core/nanoapp.h"
 #include "chre/platform/assert.h"
 #include "chre/platform/context.h"
+#include "chre/platform/event_loop_hooks.h"
 #include "chre/platform/fatal_error.h"
 #include "chre/platform/system_time.h"
 #include "chre/util/conditional_lock_guard.h"
@@ -35,6 +36,9 @@
 #include "chre/util/time.h"
 #include "chre_api/chre/version.h"
 
+using ::chre::message::EndpointInfo;
+using ::chre::message::EndpointType;
+
 namespace chre {
 
 // Out of line declaration required for nonintegral static types
@@ -301,25 +305,16 @@
                                   targetLowPriorityEventRemove);
 }
 
-bool EventLoop::deliverEventSync(uint16_t nanoappInstanceId,
-                                 uint16_t eventType,
-                                 void *eventData) {
+bool EventLoop::distributeEventSync(uint16_t eventType, void *eventData,
+                                    uint16_t targetInstanceId,
+                                    uint16_t targetGroupMask) {
   CHRE_ASSERT(inEventLoopThread());
-
   Event event(eventType, eventData,
               /* freeCallback= */ nullptr,
               /* isLowPriority= */ false,
-              /* senderInstanceId= */ kSystemInstanceId,
-              /* targetInstanceId= */ nanoappInstanceId,
-              kDefaultTargetGroupMask);
-  for (const UniquePtr<Nanoapp> &app : mNanoapps) {
-    if (app->getInstanceId() == nanoappInstanceId) {
-      deliverNextEvent(app, &event);
-      return true;
-    }
-  }
-
-  return false;
+              /* senderInstanceId= */ kSystemInstanceId, targetInstanceId,
+              targetGroupMask);
+  return distributeEventCommon(&event);
 }
 
 // TODO(b/264108686): Refactor this function and postSystemEvent
@@ -332,6 +327,9 @@
         !allocateAndPostEvent(eventType, eventData, freeCallback,
                               /* isLowPriority= */ false, kSystemInstanceId,
                               targetInstanceId, targetGroupMask)) {
+      CHRE_HANDLE_FAILED_SYSTEM_EVENT_ENQUEUE(
+          this, eventType, eventData, freeCallback, kSystemInstanceId,
+          targetInstanceId, targetGroupMask);
       FATAL_ERROR("Failed to post critical system event 0x%" PRIx16, eventType);
     }
   } else if (freeCallback != nullptr) {
@@ -347,6 +345,8 @@
   }
 
   if (hasNoSpaceForHighPriorityEvent()) {
+    CHRE_HANDLE_EVENT_QUEUE_FULL_DURING_SYSTEM_POST(this, eventType, eventData,
+                                                    callback, extraData);
     FATAL_ERROR("Failed to post critical system event 0x%" PRIx16
                 ": Full of high priority "
                 "events",
@@ -355,6 +355,9 @@
 
   Event *event = mEventPool.allocate(eventType, eventData, callback, extraData);
   if (event == nullptr || !mEvents.push(event)) {
+    CHRE_HANDLE_FAILED_SYSTEM_EVENT_ENQUEUE(
+        this, eventType, eventData, callback, kSystemInstanceId,
+        kBroadcastInstanceId, kDefaultTargetGroupMask);
     FATAL_ERROR("Failed to post critical system event 0x%" PRIx16
                 ": out of memory",
                 eventType);
@@ -377,6 +380,9 @@
     if (!eventPosted) {
       LOGE("Failed to allocate event 0x%" PRIx16 " to instanceId %" PRIu16,
            eventType, targetInstanceId);
+      CHRE_HANDLE_LOW_PRIORITY_ENQUEUE_FAILURE(
+          this, eventType, eventData, freeCallback, senderInstanceId,
+          targetInstanceId, targetGroupMask);
       ++mNumDroppedLowPriEvents;
     }
   }
@@ -408,6 +414,11 @@
   return lookupAppByInstanceId(instanceId);
 }
 
+Nanoapp *EventLoop::findNanoappByAppId(uint64_t appId) const {
+  ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
+  return lookupAppByAppId(appId);
+}
+
 bool EventLoop::populateNanoappInfoForAppId(
     uint64_t appId, struct chreNanoappInfo *info) const {
   ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
@@ -462,6 +473,25 @@
   }
 }
 
+void EventLoop::onMatchingNanoappEndpoint(
+    const pw::Function<bool(const EndpointInfo &)> &function) {
+  ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
+
+  for (const UniquePtr<Nanoapp> &app : mNanoapps) {
+    if (function(getEndpointInfoFromNanoappLocked(*app.get()))) {
+      break;
+    }
+  }
+}
+
+std::optional<EndpointInfo> EventLoop::getEndpointInfo(uint64_t appId) {
+  ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
+  Nanoapp *app = lookupAppByAppId(appId);
+  return app == nullptr
+             ? std::nullopt
+             : std::make_optional(getEndpointInfoFromNanoappLocked(*app));
+}
+
 bool EventLoop::allocateAndPostEvent(uint16_t eventType, void *eventData,
                                      chreEventCompleteFunction *freeCallback,
                                      bool isLowPriority,
@@ -514,13 +544,27 @@
 }
 
 void EventLoop::distributeEvent(Event *event) {
+  distributeEventCommon(event);
+  CHRE_ASSERT(event->isUnreferenced());
+  freeEvent(event);
+}
+
+bool EventLoop::distributeEventCommon(Event *event) {
   bool eventDelivered = false;
-  for (const UniquePtr<Nanoapp> &app : mNanoapps) {
-    if ((event->targetInstanceId == chre::kBroadcastInstanceId &&
-         app->isRegisteredForBroadcastEvent(event)) ||
-        event->targetInstanceId == app->getInstanceId()) {
-      eventDelivered = true;
-      deliverNextEvent(app, event);
+  if (event->targetInstanceId == kBroadcastInstanceId) {
+    for (const UniquePtr<Nanoapp> &app : mNanoapps) {
+      if (app->isRegisteredForBroadcastEvent(event)) {
+        eventDelivered = true;
+        deliverNextEvent(app, event);
+      }
+    }
+  } else {
+    for (const UniquePtr<Nanoapp> &app : mNanoapps) {
+      if (event->targetInstanceId == app->getInstanceId()) {
+        eventDelivered = true;
+        deliverNextEvent(app, event);
+        break;
+      }
     }
   }
   // Log if an event unicast to a nanoapp isn't delivered, as this is could be
@@ -533,8 +577,7 @@
     LOGW("Dropping event 0x%" PRIx16 " from instanceId %" PRIu16 "->%" PRIu16,
          event->eventType, event->senderInstanceId, event->targetInstanceId);
   }
-  CHRE_ASSERT(event->isUnreferenced());
-  freeEvent(event);
+  return eventDelivered;
 }
 
 void EventLoop::flushInboundEventQueue() {
@@ -681,4 +724,14 @@
   }
 }
 
+EndpointInfo EventLoop::getEndpointInfoFromNanoappLocked(
+    const Nanoapp &nanoapp) {
+  return EndpointInfo(
+      /* id= */ nanoapp.getAppId(),
+      /* name= */ nanoapp.getAppName(),
+      /* version= */ nanoapp.getAppVersion(),
+      /* type= */ EndpointType::NANOAPP,
+      /* requiredPermissions= */ nanoapp.getAppPermissions());
+}
+
 }  // namespace chre
diff --git a/core/event_loop_manager.cc b/core/event_loop_manager.cc
index 4baf5d6..e802135 100644
--- a/core/event_loop_manager.cc
+++ b/core/event_loop_manager.cc
@@ -16,6 +16,8 @@
 
 #include "chre/core/event_loop_manager.h"
 
+#include "chre/core/event_loop_common.h"
+#include "chre/event.h"
 #include "chre/platform/atomic.h"
 #include "chre/platform/fatal_error.h"
 #include "chre/platform/memory.h"
@@ -71,6 +73,10 @@
 #ifdef CHRE_BLE_SUPPORT_ENABLED
   mBleRequestManager.init();
 #endif  // CHRE_BLE_SUPPORT_ENABLED
+
+#ifdef CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
+  mChreMessageHubManager.init();
+#endif  // CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
 }
 
 // Explicitly instantiate the EventLoopManagerSingleton to reduce codesize.
diff --git a/core/gnss_manager.cc b/core/gnss_manager.cc
index 4fce1ef..e11e9b1 100644
--- a/core/gnss_manager.cc
+++ b/core/gnss_manager.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_GNSS_SUPPORT_ENABLED
+
 #include "chre/core/gnss_manager.h"
 
 #include <cstddef>
@@ -768,3 +770,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_GNSS_SUPPORT_ENABLED
diff --git a/core/host_comms_manager.cc b/core/host_comms_manager.cc
index b00a8c5..1dc6b79 100644
--- a/core/host_comms_manager.cc
+++ b/core/host_comms_manager.cc
@@ -27,7 +27,6 @@
 #include "chre/platform/context.h"
 #include "chre/platform/host_link.h"
 #include "chre/platform/log.h"
-#include "chre/target_platform/log.h"
 #include "chre/util/duplicate_message_detector.h"
 #include "chre/util/macros.h"
 #include "chre/util/nested_data_ptr.h"
@@ -352,9 +351,9 @@
   if (!foundNanoapp) {
     error = CHRE_ERROR_DESTINATION_NOT_FOUND;
   } else if (shouldDeliverMessage) {
-    EventLoopManagerSingleton::get()->getEventLoop().deliverEventSync(
-        targetInstanceId, CHRE_EVENT_MESSAGE_FROM_HOST,
-        &craftedMessage->fromHostData);
+    EventLoopManagerSingleton::get()->getEventLoop().distributeEventSync(
+        CHRE_EVENT_MESSAGE_FROM_HOST, &craftedMessage->fromHostData,
+        targetInstanceId);
     error = CHRE_ERROR_NONE;
   }
 
@@ -485,8 +484,8 @@
     asyncResult.cookie = message->cookie;
 
     onMessageToHostCompleteInternal(message);
-    eventLoop.deliverEventSync(
-        nanoappInstanceId, CHRE_EVENT_RELIABLE_MSG_ASYNC_RESULT, &asyncResult);
+    eventLoop.distributeEventSync(CHRE_EVENT_RELIABLE_MSG_ASYNC_RESULT,
+                                  &asyncResult, nanoappInstanceId);
   }
 }
 
diff --git a/core/host_endpoint_manager.cc b/core/host_endpoint_manager.cc
index 694be90..3e776e8 100644
--- a/core/host_endpoint_manager.cc
+++ b/core/host_endpoint_manager.cc
@@ -88,12 +88,11 @@
 bool HostEndpointManager::getHostEndpointInfo(
     uint16_t hostEndpointId, struct chreHostEndpointInfo *info) {
   size_t index;
-  if (isHostEndpointConnected(hostEndpointId, &index)) {
+  bool isConnected = isHostEndpointConnected(hostEndpointId, &index);
+  if (isConnected) {
     *info = mHostEndpoints[index];
-    return true;
-  } else {
-    return false;
   }
+  return isConnected;
 }
 
 void HostEndpointManager::postHostEndpointConnected(
diff --git a/core/include/chre/core/audio_request_manager.h b/core/include/chre/core/audio_request_manager.h
index 8b97a34..a35b54b 100644
--- a/core/include/chre/core/audio_request_manager.h
+++ b/core/include/chre/core/audio_request_manager.h
@@ -17,6 +17,8 @@
 #ifndef CHRE_CORE_AUDIO_REQUEST_MANAGER_H_
 #define CHRE_CORE_AUDIO_REQUEST_MANAGER_H_
 
+#ifdef CHRE_AUDIO_SUPPORT_ENABLED
+
 #include <cstdint>
 
 #include "chre/core/nanoapp.h"
@@ -383,4 +385,6 @@
 
 }  // namespace chre
 
+#endif  // CHRE_AUDIO_SUPPORT_ENABLED
+
 #endif  // CHRE_CORE_AUDIO_REQUEST_MANAGER_H_
diff --git a/core/include/chre/core/ble_request_manager.h b/core/include/chre/core/ble_request_manager.h
index 957b8ab..847994d 100644
--- a/core/include/chre/core/ble_request_manager.h
+++ b/core/include/chre/core/ble_request_manager.h
@@ -17,6 +17,8 @@
 #ifndef CHRE_CORE_BLE_REQUEST_MANAGER_H_
 #define CHRE_CORE_BLE_REQUEST_MANAGER_H_
 
+#ifdef CHRE_BLE_SUPPORT_ENABLED
+
 #include "chre/core/ble_request.h"
 #include "chre/core/ble_request_multiplexer.h"
 #include "chre/core/nanoapp.h"
@@ -582,4 +584,6 @@
 
 }  // namespace chre
 
+#endif  // CHRE_BLE_SUPPORT_ENABLED
+
 #endif  // CHRE_CORE_BLE_REQUEST_MANAGER_H_
diff --git a/core/include/chre/core/chre_message_hub_manager.h b/core/include/chre/core/chre_message_hub_manager.h
new file mode 100644
index 0000000..15aaea9
--- /dev/null
+++ b/core/include/chre/core/chre_message_hub_manager.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_CORE_CHRE_MESSAGE_HUB_MANAGER_H_
+#define CHRE_CORE_CHRE_MESSAGE_HUB_MANAGER_H_
+
+#ifdef CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
+
+#include "chre/core/event_loop_common.h"
+#include "chre/util/non_copyable.h"
+#include "chre/util/system/message_common.h"
+#include "chre/util/system/message_router.h"
+#include "chre/util/unique_ptr.h"
+
+#include <cinttypes>
+#include <optional>
+
+namespace chre {
+
+//! Manager class for the CHRE Message Hub.
+class ChreMessageHubManager
+    : public NonCopyable,
+      public message::MessageRouter::MessageHubCallback {
+ public:
+  //! The ID of the CHRE MessageHub
+  constexpr static message::MessageHubId kChreMessageHubId = CHRE_PLATFORM_ID;
+
+  //! Initializes the ChreMessageHubManager
+  void init();
+
+  //! Returns the MessageHub for the CHRE Message Hub
+  message::MessageRouter::MessageHub &getMessageHub() {
+    return mChreMessageHub;
+  }
+
+ private:
+  //! Data to be passed to the message callback
+  struct MessageCallbackData {
+    chreMessageFromEndpointData messageToNanoapp;
+    pw::UniquePtr<std::byte[]> data;
+    uint64_t nanoappId;
+  };
+
+  //! Data to be passed to the session closed callback
+  struct SessionClosedCallbackData {
+    chreEndpointSessionClosedData sessionClosedData;
+    uint64_t nanoappId;
+  };
+
+  //! Callback to process message sent to a nanoapp - used by the event loop
+  static void onMessageToNanoappCallback(
+      SystemCallbackType /* type */,
+      UniquePtr<ChreMessageHubManager::MessageCallbackData> &&data);
+
+  //! Callback to process session closed event for a nanoapp - used by the event
+  //! loop
+  static void onSessionClosedCallback(
+      SystemCallbackType /* type */,
+      UniquePtr<ChreMessageHubManager::SessionClosedCallbackData> &&data);
+
+  //! Definitions for MessageHubCallback
+  //! @see MessageRouter::MessageHubCallback
+  bool onMessageReceived(pw::UniquePtr<std::byte[]> &&data, size_t length,
+                         uint32_t messageType, uint32_t messagePermissions,
+                         const message::Session &session,
+                         bool sentBySessionInitiator) override;
+  void onSessionClosed(const message::Session &session) override;
+  void forEachEndpoint(const pw::Function<bool(const message::EndpointInfo &)>
+                           &function) override;
+  std::optional<message::EndpointInfo> getEndpointInfo(
+      message::EndpointId endpointId) override;
+
+  message::MessageRouter::MessageHub mChreMessageHub;
+};
+
+}  // namespace chre
+
+#endif  // CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
+
+#endif  // CHRE_CORE_CHRE_MESSAGE_HUB_MANAGER_H_
diff --git a/core/include/chre/core/event_loop.h b/core/include/chre/core/event_loop.h
index fe7993a..97e11e5 100644
--- a/core/include/chre/core/event_loop.h
+++ b/core/include/chre/core/event_loop.h
@@ -17,6 +17,10 @@
 #ifndef CHRE_CORE_EVENT_LOOP_H_
 #define CHRE_CORE_EVENT_LOOP_H_
 
+#include <pw_function/function.h>
+#include <stddef.h>
+#include <optional>
+
 #include "chre/core/event.h"
 #include "chre/core/nanoapp.h"
 #include "chre/core/timer_pool.h"
@@ -27,13 +31,14 @@
 #include "chre/util/dynamic_vector.h"
 #include "chre/util/non_copyable.h"
 #include "chre/util/system/debug_dump.h"
+#include "chre/util/system/message_common.h"
 #include "chre/util/system/stats_container.h"
 #include "chre/util/unique_ptr.h"
 #include "chre_api/chre/event.h"
 
 #ifdef CHRE_STATIC_EVENT_LOOP
-#include "chre/util/fixed_size_blocking_queue.h"
-#include "chre/util/synchronized_memory_pool.h"
+#include "chre/util/system/fixed_size_blocking_queue.h"
+#include "chre/util/system/synchronized_memory_pool.h"
 
 // These default values can be overridden in the variant-specific makefile.
 #ifndef CHRE_MAX_EVENT_COUNT
@@ -45,7 +50,7 @@
 #endif
 #else
 #include "chre/util/blocking_segmented_queue.h"
-#include "chre/util/synchronized_expandable_memory_pool.h"
+#include "chre/util/system/synchronized_expandable_memory_pool.h"
 
 // These default values can be overridden in the variant-specific makefile.
 #ifndef CHRE_EVENT_PER_BLOCK
@@ -67,6 +72,11 @@
  */
 class EventLoop : public NonCopyable {
  public:
+  /**
+   * Synchronous callback used with forEachNanoapp
+   */
+  typedef void(NanoappCallbackFunction)(const Nanoapp *nanoapp, void *data);
+
   EventLoop()
       :
 #ifndef CHRE_STATIC_EVENT_LOOP
@@ -77,11 +87,6 @@
   }
 
   /**
-   * Synchronous callback used with forEachNanoapp
-   */
-  typedef void(NanoappCallbackFunction)(const Nanoapp *nanoapp, void *data);
-
-  /**
    * Searches the set of nanoapps managed by this EventLoop for one with the
    * given app ID. If found, provides its instance ID, which can be used to send
    * events to the app.
@@ -172,8 +177,15 @@
   void stop();
 
   /**
-   * Synchronously deliver an event to a nanoapp. The event is sent from the
-   * system to the nanoapp with instance ID nanoappInstanceId.
+   * Synchronously distributes an event to all nanoapps that should receive it.
+   * The event is sent from the system to a specific nanoapp if targetInstanceId
+   * matches the nanoappId, or to all registered nanoapps if targetInstanceId
+   * is set to kBroadcastInstanceId
+   *
+   * This is intended to be used by the function provided to
+   * EventLoopManager::deferCallback in cases where pre- and post-processing are
+   * required around event delivery. This closes the gaps around event delivery
+   * and can remove the need for posting multiple events.
    *
    * This must only be used from the EventLoop thread, and must only be used in
    * rare circumstances where one of the postEvent functions cannot be used. In
@@ -181,16 +193,18 @@
    * ordering guarantees and trigger subtle bugs in nanoapps, so use with
    * caution.
    *
-   * freeCallback is guaranteed to be called before returning.
+   * No freeCallback is provided. The caller is expected to manage the memory
+   * for eventData, and handle any cleanup.
    *
-   * @param nanoappInstanceId The instance ID of the destination of this event
    * @param eventType Event type identifier, which implies the type of eventData
    * @param eventData The data being posted
-   * @return true if the event was successfully delivered, false otherwise.
+   * @param targetInstanceId The instance ID of the destination of this event
+   * @param targetGroupMask Mask used to limit the recipients that are
+   *        registered to receive this event
    */
-  bool deliverEventSync(uint16_t nanoappInstanceId,
-                        uint16_t eventType,
-                        void *eventData);
+  bool distributeEventSync(uint16_t eventType, void *eventData,
+                           uint16_t targetInstanceId = kBroadcastInstanceId,
+                           uint16_t targetGroupMask = kDefaultTargetGroupMask);
 
   /**
    * Posts an event to a nanoapp that is currently running (or all nanoapps if
@@ -314,6 +328,17 @@
   Nanoapp *findNanoappByInstanceId(uint16_t instanceId) const;
 
   /**
+   * Searches the set of nanoapps managed by this EventLoop for one with the
+   * given nanoapp ID.
+   *
+   * This function is safe to call from any thread.
+   *
+   * @param appId The nanoapp ID to search for.
+   * @return a pointer to the found nanoapp or nullptr if no match was found.
+   */
+  Nanoapp *findNanoappByAppId(uint64_t appId) const;
+
+  /**
    * Looks for an app with the given ID and if found, populates info with its
    * metadata. Safe to call from any thread.
    *
@@ -347,6 +372,28 @@
   void logStateToBuffer(DebugDumpWrapper &debugDump) const;
 
   /**
+   * Executes function for each nanoapp in the event loop. If function
+   * returns true, the iteration will stop.
+   *
+   * This function is safe to call from any thread.
+   *
+   * @param function The function to execute for each nanoapp.
+   */
+  void onMatchingNanoappEndpoint(
+      const pw::Function<bool(const message::EndpointInfo &)> &function);
+
+  /**
+   * Returns the EndpointInfo for the given nanoapp.
+   *
+   * This function is safe to call from any thread.
+   *
+   * @param appId The nanoapp ID to search for.
+   * @return The EndpointInfo for the given nanoapp, or std::nullopt if not
+   * found.
+   */
+  std::optional<message::EndpointInfo> getEndpointInfo(uint64_t appId);
+
+  /**
    * Returns a reference to the power control manager. This allows power
    * controls from subsystems outside the event loops.
    */
@@ -489,6 +536,16 @@
   void distributeEvent(Event *event);
 
   /**
+   * Shared functionality to distributeEvent and distributeEventSync. Should
+   * only be called by those functions. Hnadles event distribution and logging
+   * without any pre- or post-processing.
+   *
+   * @param event The Event to distribute to Nanoapps
+   * @return True if the event was delivered to any nanoapps, otherwise false
+   */
+  bool distributeEventCommon(Event *event);
+
+  /**
    * Distribute all events pending in the inbound event queue. Note that this
    * function only guarantees that any events in the inbound queue at the time
    * it is called will be distributed to Nanoapp event queues - new events may
@@ -559,6 +616,18 @@
    * @param count The number of dangling resources.
    */
   void logDanglingResources(const char *name, uint32_t count);
+
+  /**
+   * Returns the EndpointInfo for the given nanoapp.
+   *
+   * Only safe to call within this EventLoop's thread, or if mNanoappsLock is
+   * held.
+   *
+   * @param nanoapp The nanoapp to get the EndpointInfo for.
+   * @return The EndpointInfo for the given nanoapp
+   */
+  message::EndpointInfo getEndpointInfoFromNanoappLocked(
+      const Nanoapp &nanoapp);
 };
 
 }  // namespace chre
diff --git a/core/include/chre/core/event_loop_common.h b/core/include/chre/core/event_loop_common.h
index ca280d1..ea2203d 100644
--- a/core/include/chre/core/event_loop_common.h
+++ b/core/include/chre/core/event_loop_common.h
@@ -76,6 +76,8 @@
   ReliableMessageEvent,
   TimerPoolTimerExpired,
   TransactionManagerTimeout,
+  EndpointMessageToNanoappEvent,
+  EndpointSessionClosedEvent,
 };
 
 //! Deferred/delayed callbacks use the event subsystem but are invariably sent
diff --git a/core/include/chre/core/event_loop_manager.h b/core/include/chre/core/event_loop_manager.h
index b5f52b3..255f9ee 100644
--- a/core/include/chre/core/event_loop_manager.h
+++ b/core/include/chre/core/event_loop_manager.h
@@ -17,13 +17,21 @@
 #ifndef CHRE_CORE_EVENT_LOOP_MANAGER_H_
 #define CHRE_CORE_EVENT_LOOP_MANAGER_H_
 
+#include "chre/core/audio_request_manager.h"
+#include "chre/core/ble_request_manager.h"
+#include "chre/core/chre_message_hub_manager.h"
 #include "chre/core/debug_dump_manager.h"
 #include "chre/core/event_loop.h"
 #include "chre/core/event_loop_common.h"
+#include "chre/core/gnss_manager.h"
 #include "chre/core/host_comms_manager.h"
 #include "chre/core/host_endpoint_manager.h"
+#include "chre/core/sensor_request_manager.h"
 #include "chre/core/settings.h"
 #include "chre/core/system_health_monitor.h"
+#include "chre/core/telemetry_manager.h"
+#include "chre/core/wifi_request_manager.h"
+#include "chre/core/wwan_request_manager.h"
 #include "chre/platform/atomic.h"
 #include "chre/platform/memory_manager.h"
 #include "chre/platform/mutex.h"
@@ -34,34 +42,6 @@
 #include "chre/util/unique_ptr.h"
 #include "chre_api/chre/event.h"
 
-#ifdef CHRE_AUDIO_SUPPORT_ENABLED
-#include "chre/core/audio_request_manager.h"
-#endif  // CHRE_AUDIO_SUPPORT_ENABLED
-
-#ifdef CHRE_BLE_SUPPORT_ENABLED
-#include "chre/core/ble_request_manager.h"
-#endif  // CHRE_BLE_SUPPORT_ENABLED
-
-#ifdef CHRE_GNSS_SUPPORT_ENABLED
-#include "chre/core/gnss_manager.h"
-#endif  // CHRE_GNSS_SUPPORT_ENABLED
-
-#ifdef CHRE_SENSORS_SUPPORT_ENABLED
-#include "chre/core/sensor_request_manager.h"
-#endif  // CHRE_SENSORS_SUPPORT_ENABLED
-
-#ifdef CHRE_WIFI_SUPPORT_ENABLED
-#include "chre/core/wifi_request_manager.h"
-#endif  // CHRE_WIFI_SUPPORT_ENABLED
-
-#ifdef CHRE_WWAN_SUPPORT_ENABLED
-#include "chre/core/wwan_request_manager.h"
-#endif  // CHRE_WWAN_SUPPORT_ENABLED
-
-#ifdef CHRE_TELEMETRY_SUPPORT_ENABLED
-#include "chre/core/telemetry_manager.h"
-#endif  // CHRE_TELEMETRY_SUPPORT_ENABLED
-
 #include <cstddef>
 
 namespace chre {
@@ -342,6 +322,12 @@
     return mSystemHealthMonitor;
   }
 
+#ifdef CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
+  ChreMessageHubManager &getChreMessageHubManager() {
+    return mChreMessageHubManager;
+  }
+#endif  // CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
+
   /**
    * Performs second-stage initialization of things that are not necessarily
    * required at construction time but need to be completed prior to executing
@@ -413,6 +399,11 @@
 
   //! The SettingManager that manages setting states.
   SettingManager mSettingManager;
+
+#ifdef CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
+  //! The ChreMessageHubManager that manages the CHRE Message Hub.
+  ChreMessageHubManager mChreMessageHubManager;
+#endif  // CHRE_MESSAGE_ROUTER_SUPPORT_ENABLED
 };
 
 //! Provide an alias to the EventLoopManager singleton.
diff --git a/core/include/chre/core/gnss_manager.h b/core/include/chre/core/gnss_manager.h
index d4885ff..ed595d9 100644
--- a/core/include/chre/core/gnss_manager.h
+++ b/core/include/chre/core/gnss_manager.h
@@ -17,6 +17,8 @@
 #ifndef CHRE_CORE_GNSS_MANAGER_H_
 #define CHRE_CORE_GNSS_MANAGER_H_
 
+#ifdef CHRE_GNSS_SUPPORT_ENABLED
+
 #include <cstdint>
 
 #include "chre/core/api_manager_common.h"
@@ -511,4 +513,6 @@
 
 }  // namespace chre
 
+#endif  // CHRE_GNSS_SUPPORT_ENABLED
+
 #endif  // CHRE_CORE_GNSS_MANAGER_H_
diff --git a/core/include/chre/core/host_comms_manager.h b/core/include/chre/core/host_comms_manager.h
index a32a09a..305c19e 100644
--- a/core/include/chre/core/host_comms_manager.h
+++ b/core/include/chre/core/host_comms_manager.h
@@ -27,9 +27,9 @@
 #include "chre/util/buffer.h"
 #include "chre/util/duplicate_message_detector.h"
 #include "chre/util/non_copyable.h"
-#include "chre/util/synchronized_memory_pool.h"
+#include "chre/util/system/synchronized_memory_pool.h"
+#include "chre/util/system/transaction_manager.h"
 #include "chre/util/time.h"
-#include "chre/util/transaction_manager.h"
 #include "chre_api/chre/event.h"
 
 namespace chre {
diff --git a/core/include/chre/core/nanoapp.h b/core/include/chre/core/nanoapp.h
index 9fb9a98..68404d2 100644
--- a/core/include/chre/core/nanoapp.h
+++ b/core/include/chre/core/nanoapp.h
@@ -302,6 +302,15 @@
     return mFirstHeader;
   }
 
+  /**
+   * @return whether the nanoapp has the provided permissions.
+   */
+  bool hasPermissions(uint32_t permissions) const {
+    return permissions == 0 ||
+           (supportsAppPermissions() &&
+            (getAppPermissions() & permissions) == permissions);
+  }
+
  private:
   uint16_t mInstanceId = kInvalidInstanceId;
 
diff --git a/core/include/chre/core/sensor_request_manager.h b/core/include/chre/core/sensor_request_manager.h
index b91c154..5509d94 100644
--- a/core/include/chre/core/sensor_request_manager.h
+++ b/core/include/chre/core/sensor_request_manager.h
@@ -17,6 +17,8 @@
 #ifndef CHRE_CORE_SENSOR_REQUEST_MANAGER_H_
 #define CHRE_CORE_SENSOR_REQUEST_MANAGER_H_
 
+#ifdef CHRE_SENSORS_SUPPORT_ENABLED
+
 #include "chre/core/sensor.h"
 #include "chre/core/sensor_request.h"
 #include "chre/core/sensor_request_multiplexer.h"
@@ -515,4 +517,6 @@
 
 }  // namespace chre
 
+#endif  // CHRE_SENSORS_SUPPORT_ENABLED
+
 #endif  // CHRE_CORE_SENSOR_REQUEST_MANAGER_H_
diff --git a/core/include/chre/core/telemetry_manager.h b/core/include/chre/core/telemetry_manager.h
index 9364f86..768b654 100644
--- a/core/include/chre/core/telemetry_manager.h
+++ b/core/include/chre/core/telemetry_manager.h
@@ -17,6 +17,8 @@
 #ifndef CHRE_CORE_TELEMETRY_MANAGER_H_
 #define CHRE_CORE_TELEMETRY_MANAGER_H_
 
+#ifdef CHRE_TELEMETRY_SUPPORT_ENABLED
+
 #include <cinttypes>
 
 #include "chre/util/non_copyable.h"
@@ -64,4 +66,6 @@
 
 }  // namespace chre
 
+#endif  // CHRE_TELEMETRY_SUPPORT_ENABLED
+
 #endif  // CHRE_CORE_TELEMETRY_MANAGER_H_
diff --git a/core/include/chre/core/wifi_request_manager.h b/core/include/chre/core/wifi_request_manager.h
index b5e2708..46837d6 100644
--- a/core/include/chre/core/wifi_request_manager.h
+++ b/core/include/chre/core/wifi_request_manager.h
@@ -17,6 +17,8 @@
 #ifndef CHRE_CORE_WIFI_REQUEST_MANAGER_H_
 #define CHRE_CORE_WIFI_REQUEST_MANAGER_H_
 
+#ifdef CHRE_WIFI_SUPPORT_ENABLED
+
 #include "chre/core/api_manager_common.h"
 #include "chre/core/nanoapp.h"
 #include "chre/core/settings.h"
@@ -922,4 +924,6 @@
 
 }  // namespace chre
 
+#endif  // CHRE_WIFI_SUPPORT_ENABLED
+
 #endif  // CHRE_CORE_WIFI_REQUEST_MANAGER_H_
diff --git a/core/include/chre/core/wwan_request_manager.h b/core/include/chre/core/wwan_request_manager.h
index 1c1e278..9b8b1ef 100644
--- a/core/include/chre/core/wwan_request_manager.h
+++ b/core/include/chre/core/wwan_request_manager.h
@@ -17,6 +17,8 @@
 #ifndef CHRE_CORE_WWAN_REQUEST_MANAGER_H_
 #define CHRE_CORE_WWAN_REQUEST_MANAGER_H_
 
+#ifdef CHRE_WWAN_SUPPORT_ENABLED
+
 #include <cstdint>
 
 #include "chre/core/api_manager_common.h"
@@ -121,4 +123,6 @@
 
 }  // namespace chre
 
+#endif  // CHRE_WWAN_SUPPORT_ENABLED
+
 #endif  // CHRE_CORE_WWAN_REQUEST_MANAGER_H_
diff --git a/core/log.cc b/core/log.cc
deleted file mode 100644
index f95b888..0000000
--- a/core/log.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifdef CHRE_TOKENIZED_LOGGING_ENABLED
-#include "chre/platform/log.h"
-#include "pw_log_tokenized/config.h"
-#include "pw_tokenizer/encode_args.h"
-#include "pw_tokenizer/tokenize.h"
-
-// The callback function that must be defined to handle an encoded
-// tokenizer message.
-
-void EncodeTokenizedMessage(uint32_t level, pw_tokenizer_Token token,
-                            pw_tokenizer_ArgTypes types, ...) {
-  va_list args;
-  va_start(args, types);
-  pw::tokenizer::EncodedMessage<pw::log_tokenized::kEncodingBufferSizeBytes>
-      encodedMessage(token, types, args);
-  va_end(args);
-
-  chrePlatformEncodedLogToBuffer(static_cast<chreLogLevel>(level),
-                                 encodedMessage.data_as_uint8(),
-                                 encodedMessage.size());
-}
-
-#endif  // CHRE_TOKENIZED_LOGGING_ENABLED
diff --git a/core/sensor_request_manager.cc b/core/sensor_request_manager.cc
index 6230ccb..2888ae0 100644
--- a/core/sensor_request_manager.cc
+++ b/core/sensor_request_manager.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_SENSORS_SUPPORT_ENABLED
+
 #include "chre/core/sensor_request_manager.h"
 
 #include "chre/core/event_loop_manager.h"
@@ -939,3 +941,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_SENSORS_SUPPORT_ENABLED
diff --git a/core/telemetry_manager.cc b/core/telemetry_manager.cc
index 6bc32a7..70e1139 100644
--- a/core/telemetry_manager.cc
+++ b/core/telemetry_manager.cc
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_TELEMETRY_SUPPORT_ENABLED
+
 #include "chre/core/telemetry_manager.h"
 
 #include <pb_encode.h>
 
 #include "chre/core/event_loop_manager.h"
 #include "chre/platform/fatal_error.h"
-#include "chre/platform/shared/host_protocol_chre.h"
 #include "chre/util/macros.h"
 #include "chre/util/nested_data_ptr.h"
 #include "chre/util/time.h"
@@ -173,3 +174,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_TELEMETRY_SUPPORT_ENABLED
diff --git a/core/timer_pool.cc b/core/timer_pool.cc
index 36d032e..bb8b095 100644
--- a/core/timer_pool.cc
+++ b/core/timer_pool.cc
@@ -20,8 +20,8 @@
 #include "chre/core/event_loop_common.h"
 #include "chre/core/event_loop_manager.h"
 #include "chre/platform/fatal_error.h"
+#include "chre/platform/log.h"
 #include "chre/platform/system_time.h"
-#include "chre/target_platform/log.h"
 #include "chre/util/lock_guard.h"
 #include "chre/util/nested_data_ptr.h"
 
@@ -362,11 +362,9 @@
     }
   }
 
-  if (!EventLoopManagerSingleton::get()->getEventLoop()
-        .deliverEventSync(
-            currentTimerRequest.instanceId,
-            CHRE_EVENT_TIMER,
-            const_cast<void*>(currentTimerRequest.cookie))) {
+  if (!EventLoopManagerSingleton::get()->getEventLoop().distributeEventSync(
+          CHRE_EVENT_TIMER, const_cast<void *>(currentTimerRequest.cookie),
+          currentTimerRequest.instanceId)) {
     LOGW("Failed to deliver timer event");
   }
 }
diff --git a/core/wifi_request_manager.cc b/core/wifi_request_manager.cc
index 8aa4649..b95405b 100644
--- a/core/wifi_request_manager.cc
+++ b/core/wifi_request_manager.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_WIFI_SUPPORT_ENABLED
+
 #include "chre/core/wifi_request_manager.h"
 
 #include <cinttypes>
@@ -1418,3 +1420,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_WIFI_SUPPORT_ENABLED
diff --git a/core/wwan_request_manager.cc b/core/wwan_request_manager.cc
index 9fbbc92..b6ccefd 100644
--- a/core/wwan_request_manager.cc
+++ b/core/wwan_request_manager.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_WWAN_SUPPORT_ENABLED
+
 #include "chre/core/wwan_request_manager.h"
 
 #include "chre/core/event_loop_manager.h"
@@ -127,3 +129,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_WWAN_SUPPORT_ENABLED
diff --git a/external/pigweed/pw_assert_nanoapp/public_overrides/pw_assert_backend/check_backend.h b/external/pigweed/pw_assert_nanoapp/public_overrides/pw_assert_backend/check_backend.h
index 99029d9..b17032f 100644
--- a/external/pigweed/pw_assert_nanoapp/public_overrides/pw_assert_backend/check_backend.h
+++ b/external/pigweed/pw_assert_nanoapp/public_overrides/pw_assert_backend/check_backend.h
@@ -35,6 +35,7 @@
 #define PW_HANDLE_ASSERT_FAILURE(condition_string, message, ...) \
   do {                                                           \
     PW_LOG(PW_LOG_LEVEL_FATAL,                                   \
+           PW_LOG_LEVEL,                                         \
            PW_LOG_MODULE_NAME,                                   \
            PW_LOG_FLAGS,                                         \
            "Check failed: " condition_string ". " message,       \
@@ -51,6 +52,7 @@
                                                 message, ...)             \
   do {                                                                    \
     PW_LOG(PW_LOG_LEVEL_FATAL,                                            \
+           PW_LOG_LEVEL,                                                  \
            PW_LOG_MODULE_NAME,                                            \
            PW_LOG_FLAGS,                                                  \
            "Check failed: "                                               \
diff --git a/host/common/config_util.cc b/host/common/config_util.cc
index fa6e985..c4e49f7 100644
--- a/host/common/config_util.cc
+++ b/host/common/config_util.cc
@@ -39,8 +39,9 @@
     if (!std::regex_match(entry->d_name, match, regex)) {
       continue;
     }
-    LOGD("Found nanoapp: %s", match[1]);
-    outNanoapps.push_back(match[1]);
+    std::string nanoapp_name = match[1];
+    LOGD("Found nanoapp: %s", nanoapp_name.c_str());
+    outNanoapps.push_back(nanoapp_name);
   }
   closedir(dir);
   return true;
diff --git a/host/common/host_protocol_host.cc b/host/common/host_protocol_host.cc
index a24ec94..830f1a5 100644
--- a/host/common/host_protocol_host.cc
+++ b/host/common/host_protocol_host.cc
@@ -88,9 +88,7 @@
         break;
 
       default:
-        LOGW("Got invalid/unexpected message type %" PRIu8,
-             static_cast<uint8_t>(msg.type));
-        success = false;
+        success = handlers.handleContextHubV4Message(msg);
     }
   }
 
diff --git a/host/common/include/chre_host/generated/host_messages_generated.h b/host/common/include/chre_host/generated/host_messages_generated.h
index b386f8d..613b27f 100644
--- a/host/common/include/chre_host/generated/host_messages_generated.h
+++ b/host/common/include/chre_host/generated/host_messages_generated.h
@@ -145,6 +145,90 @@
 struct PulseResponseBuilder;
 struct PulseResponseT;
 
+struct LeCocChannelInfo;
+struct LeCocChannelInfoBuilder;
+struct LeCocChannelInfoT;
+
+struct BtSocketOpen;
+struct BtSocketOpenBuilder;
+struct BtSocketOpenT;
+
+struct BtSocketOpenResponse;
+struct BtSocketOpenResponseBuilder;
+struct BtSocketOpenResponseT;
+
+struct BtSocketClose;
+struct BtSocketCloseBuilder;
+struct BtSocketCloseT;
+
+struct BtSocketCloseResponse;
+struct BtSocketCloseResponseBuilder;
+struct BtSocketCloseResponseT;
+
+struct VendorHubInfo;
+struct VendorHubInfoBuilder;
+struct VendorHubInfoT;
+
+struct MessageHub;
+struct MessageHubBuilder;
+struct MessageHubT;
+
+struct RegisterMessageHub;
+struct RegisterMessageHubBuilder;
+struct RegisterMessageHubT;
+
+struct UnregisterMessageHub;
+struct UnregisterMessageHubBuilder;
+struct UnregisterMessageHubT;
+
+struct EndpointId;
+struct EndpointIdBuilder;
+struct EndpointIdT;
+
+struct Service;
+struct ServiceBuilder;
+struct ServiceT;
+
+struct EndpointInfo;
+struct EndpointInfoBuilder;
+struct EndpointInfoT;
+
+struct RegisterEndpoint;
+struct RegisterEndpointBuilder;
+struct RegisterEndpointT;
+
+struct UnregisterEndpoint;
+struct UnregisterEndpointBuilder;
+struct UnregisterEndpointT;
+
+struct GetMessageHubsAndEndpointsRequest;
+struct GetMessageHubsAndEndpointsRequestBuilder;
+struct GetMessageHubsAndEndpointsRequestT;
+
+struct GetMessageHubsAndEndpointsResponse;
+struct GetMessageHubsAndEndpointsResponseBuilder;
+struct GetMessageHubsAndEndpointsResponseT;
+
+struct OpenEndpointSessionRequest;
+struct OpenEndpointSessionRequestBuilder;
+struct OpenEndpointSessionRequestT;
+
+struct EndpointSessionOpened;
+struct EndpointSessionOpenedBuilder;
+struct EndpointSessionOpenedT;
+
+struct EndpointSessionClosed;
+struct EndpointSessionClosedBuilder;
+struct EndpointSessionClosedT;
+
+struct EndpointSessionMessage;
+struct EndpointSessionMessageBuilder;
+struct EndpointSessionMessageT;
+
+struct EndpointSessionMessageDeliveryStatus;
+struct EndpointSessionMessageDeliveryStatusBuilder;
+struct EndpointSessionMessageDeliveryStatusT;
+
 struct HostAddress;
 
 struct MessageContainer;
@@ -327,6 +411,362 @@
   return EnumNamesBtSnoopDirection()[index];
 }
 
+enum class ChannelInfo : uint8_t {
+  NONE = 0,
+  LeCocChannelInfo = 1,
+  MIN = NONE,
+  MAX = LeCocChannelInfo
+};
+
+inline const ChannelInfo (&EnumValuesChannelInfo())[2] {
+  static const ChannelInfo values[] = {
+    ChannelInfo::NONE,
+    ChannelInfo::LeCocChannelInfo
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesChannelInfo() {
+  static const char * const names[3] = {
+    "NONE",
+    "LeCocChannelInfo",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameChannelInfo(ChannelInfo e) {
+  if (flatbuffers::IsOutRange(e, ChannelInfo::NONE, ChannelInfo::LeCocChannelInfo)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesChannelInfo()[index];
+}
+
+template<typename T> struct ChannelInfoTraits {
+  static const ChannelInfo enum_value = ChannelInfo::NONE;
+};
+
+template<> struct ChannelInfoTraits<chre::fbs::LeCocChannelInfo> {
+  static const ChannelInfo enum_value = ChannelInfo::LeCocChannelInfo;
+};
+
+struct ChannelInfoUnion {
+  ChannelInfo type;
+  void *value;
+
+  ChannelInfoUnion() : type(ChannelInfo::NONE), value(nullptr) {}
+  ChannelInfoUnion(ChannelInfoUnion&& u) FLATBUFFERS_NOEXCEPT :
+    type(ChannelInfo::NONE), value(nullptr)
+    { std::swap(type, u.type); std::swap(value, u.value); }
+  ChannelInfoUnion(const ChannelInfoUnion &);
+  ChannelInfoUnion &operator=(const ChannelInfoUnion &u)
+    { ChannelInfoUnion t(u); std::swap(type, t.type); std::swap(value, t.value); return *this; }
+  ChannelInfoUnion &operator=(ChannelInfoUnion &&u) FLATBUFFERS_NOEXCEPT
+    { std::swap(type, u.type); std::swap(value, u.value); return *this; }
+  ~ChannelInfoUnion() { Reset(); }
+
+  void Reset();
+
+#ifndef FLATBUFFERS_CPP98_STL
+  template <typename T>
+  void Set(T&& val) {
+    using RT = typename std::remove_reference<T>::type;
+    Reset();
+    type = ChannelInfoTraits<typename RT::TableType>::enum_value;
+    if (type != ChannelInfo::NONE) {
+      value = new RT(std::forward<T>(val));
+    }
+  }
+#endif  // FLATBUFFERS_CPP98_STL
+
+  static void *UnPack(const void *obj, ChannelInfo type, const flatbuffers::resolver_function_t *resolver);
+  flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher = nullptr) const;
+
+  chre::fbs::LeCocChannelInfoT *AsLeCocChannelInfo() {
+    return type == ChannelInfo::LeCocChannelInfo ?
+      reinterpret_cast<chre::fbs::LeCocChannelInfoT *>(value) : nullptr;
+  }
+  const chre::fbs::LeCocChannelInfoT *AsLeCocChannelInfo() const {
+    return type == ChannelInfo::LeCocChannelInfo ?
+      reinterpret_cast<const chre::fbs::LeCocChannelInfoT *>(value) : nullptr;
+  }
+};
+
+bool VerifyChannelInfo(flatbuffers::Verifier &verifier, const void *obj, ChannelInfo type);
+bool VerifyChannelInfoVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
+
+enum class BtSocketOpenStatus : int8_t {
+  SUCCESS = 0,
+  FAILURE = 1,
+  MIN = SUCCESS,
+  MAX = FAILURE
+};
+
+inline const BtSocketOpenStatus (&EnumValuesBtSocketOpenStatus())[2] {
+  static const BtSocketOpenStatus values[] = {
+    BtSocketOpenStatus::SUCCESS,
+    BtSocketOpenStatus::FAILURE
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesBtSocketOpenStatus() {
+  static const char * const names[3] = {
+    "SUCCESS",
+    "FAILURE",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameBtSocketOpenStatus(BtSocketOpenStatus e) {
+  if (flatbuffers::IsOutRange(e, BtSocketOpenStatus::SUCCESS, BtSocketOpenStatus::FAILURE)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesBtSocketOpenStatus()[index];
+}
+
+enum class MessageHubDetails : uint8_t {
+  NONE = 0,
+  HubInfoResponse = 1,
+  VendorHubInfo = 2,
+  MIN = NONE,
+  MAX = VendorHubInfo
+};
+
+inline const MessageHubDetails (&EnumValuesMessageHubDetails())[3] {
+  static const MessageHubDetails values[] = {
+    MessageHubDetails::NONE,
+    MessageHubDetails::HubInfoResponse,
+    MessageHubDetails::VendorHubInfo
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesMessageHubDetails() {
+  static const char * const names[4] = {
+    "NONE",
+    "HubInfoResponse",
+    "VendorHubInfo",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameMessageHubDetails(MessageHubDetails e) {
+  if (flatbuffers::IsOutRange(e, MessageHubDetails::NONE, MessageHubDetails::VendorHubInfo)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesMessageHubDetails()[index];
+}
+
+template<typename T> struct MessageHubDetailsTraits {
+  static const MessageHubDetails enum_value = MessageHubDetails::NONE;
+};
+
+template<> struct MessageHubDetailsTraits<chre::fbs::HubInfoResponse> {
+  static const MessageHubDetails enum_value = MessageHubDetails::HubInfoResponse;
+};
+
+template<> struct MessageHubDetailsTraits<chre::fbs::VendorHubInfo> {
+  static const MessageHubDetails enum_value = MessageHubDetails::VendorHubInfo;
+};
+
+struct MessageHubDetailsUnion {
+  MessageHubDetails type;
+  void *value;
+
+  MessageHubDetailsUnion() : type(MessageHubDetails::NONE), value(nullptr) {}
+  MessageHubDetailsUnion(MessageHubDetailsUnion&& u) FLATBUFFERS_NOEXCEPT :
+    type(MessageHubDetails::NONE), value(nullptr)
+    { std::swap(type, u.type); std::swap(value, u.value); }
+  MessageHubDetailsUnion(const MessageHubDetailsUnion &);
+  MessageHubDetailsUnion &operator=(const MessageHubDetailsUnion &u)
+    { MessageHubDetailsUnion t(u); std::swap(type, t.type); std::swap(value, t.value); return *this; }
+  MessageHubDetailsUnion &operator=(MessageHubDetailsUnion &&u) FLATBUFFERS_NOEXCEPT
+    { std::swap(type, u.type); std::swap(value, u.value); return *this; }
+  ~MessageHubDetailsUnion() { Reset(); }
+
+  void Reset();
+
+#ifndef FLATBUFFERS_CPP98_STL
+  template <typename T>
+  void Set(T&& val) {
+    using RT = typename std::remove_reference<T>::type;
+    Reset();
+    type = MessageHubDetailsTraits<typename RT::TableType>::enum_value;
+    if (type != MessageHubDetails::NONE) {
+      value = new RT(std::forward<T>(val));
+    }
+  }
+#endif  // FLATBUFFERS_CPP98_STL
+
+  static void *UnPack(const void *obj, MessageHubDetails type, const flatbuffers::resolver_function_t *resolver);
+  flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher = nullptr) const;
+
+  chre::fbs::HubInfoResponseT *AsHubInfoResponse() {
+    return type == MessageHubDetails::HubInfoResponse ?
+      reinterpret_cast<chre::fbs::HubInfoResponseT *>(value) : nullptr;
+  }
+  const chre::fbs::HubInfoResponseT *AsHubInfoResponse() const {
+    return type == MessageHubDetails::HubInfoResponse ?
+      reinterpret_cast<const chre::fbs::HubInfoResponseT *>(value) : nullptr;
+  }
+  chre::fbs::VendorHubInfoT *AsVendorHubInfo() {
+    return type == MessageHubDetails::VendorHubInfo ?
+      reinterpret_cast<chre::fbs::VendorHubInfoT *>(value) : nullptr;
+  }
+  const chre::fbs::VendorHubInfoT *AsVendorHubInfo() const {
+    return type == MessageHubDetails::VendorHubInfo ?
+      reinterpret_cast<const chre::fbs::VendorHubInfoT *>(value) : nullptr;
+  }
+};
+
+bool VerifyMessageHubDetails(flatbuffers::Verifier &verifier, const void *obj, MessageHubDetails type);
+bool VerifyMessageHubDetailsVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
+
+/// An enum describing the type of an endpoint.
+enum class EndpointType : uint8_t {
+  INVALID = 0,
+  /// The endpoint is part of the Android Framework
+  FRAMEWORK = 1,
+  /// The endpoint is an Android app
+  APP = 2,
+  /// The endpoint is a native Android program
+  NATIVE = 3,
+  /// The endpoint is a nanoapp
+  NANOAPP = 4,
+  /// A generic, non-nanoapp endpoint
+  GENERIC = 5,
+  MIN = INVALID,
+  MAX = GENERIC
+};
+
+inline const EndpointType (&EnumValuesEndpointType())[6] {
+  static const EndpointType values[] = {
+    EndpointType::INVALID,
+    EndpointType::FRAMEWORK,
+    EndpointType::APP,
+    EndpointType::NATIVE,
+    EndpointType::NANOAPP,
+    EndpointType::GENERIC
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesEndpointType() {
+  static const char * const names[7] = {
+    "INVALID",
+    "FRAMEWORK",
+    "APP",
+    "NATIVE",
+    "NANOAPP",
+    "GENERIC",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameEndpointType(EndpointType e) {
+  if (flatbuffers::IsOutRange(e, EndpointType::INVALID, EndpointType::GENERIC)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesEndpointType()[index];
+}
+
+enum class RpcFormat : uint8_t {
+  /// Fully custom format
+  CUSTOM = 0,
+  /// Stable AIDL defined interface using Binder marshalling
+  AIDL = 1,
+  /// Pigweed RPC defined interface using Protobuf marshalling
+  PW_RPC = 2,
+  MIN = CUSTOM,
+  MAX = PW_RPC
+};
+
+inline const RpcFormat (&EnumValuesRpcFormat())[3] {
+  static const RpcFormat values[] = {
+    RpcFormat::CUSTOM,
+    RpcFormat::AIDL,
+    RpcFormat::PW_RPC
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesRpcFormat() {
+  static const char * const names[4] = {
+    "CUSTOM",
+    "AIDL",
+    "PW_RPC",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameRpcFormat(RpcFormat e) {
+  if (flatbuffers::IsOutRange(e, RpcFormat::CUSTOM, RpcFormat::PW_RPC)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesRpcFormat()[index];
+}
+
+/// "Reason"s for stopping an endpoint or session over an endpoint.
+enum class Reason : uint8_t {
+  /// Unspecified reason.
+  UNSPECIFIED = 0,
+  /// Out of memory. There's not enough memory to perform this operation.
+  OUT_OF_MEMORY = 1,
+  /// Timeout. This operation timed out.
+  TIMEOUT = 2,
+  /// Endpoint rejected this openEndpointSession request.
+  OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3,
+  /// Endpoint requested closeEndpointSession.
+  CLOSE_ENDPOINT_SESSION_REQUESTED = 4,
+  /// Invalid endpoint.
+  ENDPOINT_INVALID = 5,
+  /// Endpoint is now stopped.
+  ENDPOINT_GONE = 6,
+  /// Endpoint crashed.
+  ENDPOINT_CRASHED = 7,
+  /// Hub was reset or is resetting.
+  HUB_RESET = 8,
+  MIN = UNSPECIFIED,
+  MAX = HUB_RESET
+};
+
+inline const Reason (&EnumValuesReason())[9] {
+  static const Reason values[] = {
+    Reason::UNSPECIFIED,
+    Reason::OUT_OF_MEMORY,
+    Reason::TIMEOUT,
+    Reason::OPEN_ENDPOINT_SESSION_REQUEST_REJECTED,
+    Reason::CLOSE_ENDPOINT_SESSION_REQUESTED,
+    Reason::ENDPOINT_INVALID,
+    Reason::ENDPOINT_GONE,
+    Reason::ENDPOINT_CRASHED,
+    Reason::HUB_RESET
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesReason() {
+  static const char * const names[10] = {
+    "UNSPECIFIED",
+    "OUT_OF_MEMORY",
+    "TIMEOUT",
+    "OPEN_ENDPOINT_SESSION_REQUEST_REJECTED",
+    "CLOSE_ENDPOINT_SESSION_REQUESTED",
+    "ENDPOINT_INVALID",
+    "ENDPOINT_GONE",
+    "ENDPOINT_CRASHED",
+    "HUB_RESET",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameReason(Reason e) {
+  if (flatbuffers::IsOutRange(e, Reason::UNSPECIFIED, Reason::HUB_RESET)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesReason()[index];
+}
+
 /// A union that joins together all possible messages. Note that in FlatBuffers,
 /// unions have an implicit type
 enum class ChreMessage : uint8_t {
@@ -363,11 +803,26 @@
   PulseResponse = 30,
   NanoappTokenDatabaseInfo = 31,
   MessageDeliveryStatus = 32,
+  BtSocketOpen = 33,
+  BtSocketOpenResponse = 34,
+  BtSocketClose = 35,
+  BtSocketCloseResponse = 36,
+  GetMessageHubsAndEndpointsRequest = 37,
+  GetMessageHubsAndEndpointsResponse = 38,
+  RegisterMessageHub = 39,
+  UnregisterMessageHub = 40,
+  RegisterEndpoint = 41,
+  UnregisterEndpoint = 42,
+  OpenEndpointSessionRequest = 43,
+  EndpointSessionOpened = 44,
+  EndpointSessionClosed = 45,
+  EndpointSessionMessage = 46,
+  EndpointSessionMessageDeliveryStatus = 47,
   MIN = NONE,
-  MAX = MessageDeliveryStatus
+  MAX = EndpointSessionMessageDeliveryStatus
 };
 
-inline const ChreMessage (&EnumValuesChreMessage())[33] {
+inline const ChreMessage (&EnumValuesChreMessage())[48] {
   static const ChreMessage values[] = {
     ChreMessage::NONE,
     ChreMessage::NanoappMessage,
@@ -401,13 +856,28 @@
     ChreMessage::PulseRequest,
     ChreMessage::PulseResponse,
     ChreMessage::NanoappTokenDatabaseInfo,
-    ChreMessage::MessageDeliveryStatus
+    ChreMessage::MessageDeliveryStatus,
+    ChreMessage::BtSocketOpen,
+    ChreMessage::BtSocketOpenResponse,
+    ChreMessage::BtSocketClose,
+    ChreMessage::BtSocketCloseResponse,
+    ChreMessage::GetMessageHubsAndEndpointsRequest,
+    ChreMessage::GetMessageHubsAndEndpointsResponse,
+    ChreMessage::RegisterMessageHub,
+    ChreMessage::UnregisterMessageHub,
+    ChreMessage::RegisterEndpoint,
+    ChreMessage::UnregisterEndpoint,
+    ChreMessage::OpenEndpointSessionRequest,
+    ChreMessage::EndpointSessionOpened,
+    ChreMessage::EndpointSessionClosed,
+    ChreMessage::EndpointSessionMessage,
+    ChreMessage::EndpointSessionMessageDeliveryStatus
   };
   return values;
 }
 
 inline const char * const *EnumNamesChreMessage() {
-  static const char * const names[34] = {
+  static const char * const names[49] = {
     "NONE",
     "NanoappMessage",
     "HubInfoRequest",
@@ -441,13 +911,28 @@
     "PulseResponse",
     "NanoappTokenDatabaseInfo",
     "MessageDeliveryStatus",
+    "BtSocketOpen",
+    "BtSocketOpenResponse",
+    "BtSocketClose",
+    "BtSocketCloseResponse",
+    "GetMessageHubsAndEndpointsRequest",
+    "GetMessageHubsAndEndpointsResponse",
+    "RegisterMessageHub",
+    "UnregisterMessageHub",
+    "RegisterEndpoint",
+    "UnregisterEndpoint",
+    "OpenEndpointSessionRequest",
+    "EndpointSessionOpened",
+    "EndpointSessionClosed",
+    "EndpointSessionMessage",
+    "EndpointSessionMessageDeliveryStatus",
     nullptr
   };
   return names;
 }
 
 inline const char *EnumNameChreMessage(ChreMessage e) {
-  if (flatbuffers::IsOutRange(e, ChreMessage::NONE, ChreMessage::MessageDeliveryStatus)) return "";
+  if (flatbuffers::IsOutRange(e, ChreMessage::NONE, ChreMessage::EndpointSessionMessageDeliveryStatus)) return "";
   const size_t index = static_cast<size_t>(e);
   return EnumNamesChreMessage()[index];
 }
@@ -584,6 +1069,66 @@
   static const ChreMessage enum_value = ChreMessage::MessageDeliveryStatus;
 };
 
+template<> struct ChreMessageTraits<chre::fbs::BtSocketOpen> {
+  static const ChreMessage enum_value = ChreMessage::BtSocketOpen;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::BtSocketOpenResponse> {
+  static const ChreMessage enum_value = ChreMessage::BtSocketOpenResponse;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::BtSocketClose> {
+  static const ChreMessage enum_value = ChreMessage::BtSocketClose;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::BtSocketCloseResponse> {
+  static const ChreMessage enum_value = ChreMessage::BtSocketCloseResponse;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::GetMessageHubsAndEndpointsRequest> {
+  static const ChreMessage enum_value = ChreMessage::GetMessageHubsAndEndpointsRequest;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::GetMessageHubsAndEndpointsResponse> {
+  static const ChreMessage enum_value = ChreMessage::GetMessageHubsAndEndpointsResponse;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::RegisterMessageHub> {
+  static const ChreMessage enum_value = ChreMessage::RegisterMessageHub;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::UnregisterMessageHub> {
+  static const ChreMessage enum_value = ChreMessage::UnregisterMessageHub;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::RegisterEndpoint> {
+  static const ChreMessage enum_value = ChreMessage::RegisterEndpoint;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::UnregisterEndpoint> {
+  static const ChreMessage enum_value = ChreMessage::UnregisterEndpoint;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::OpenEndpointSessionRequest> {
+  static const ChreMessage enum_value = ChreMessage::OpenEndpointSessionRequest;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::EndpointSessionOpened> {
+  static const ChreMessage enum_value = ChreMessage::EndpointSessionOpened;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::EndpointSessionClosed> {
+  static const ChreMessage enum_value = ChreMessage::EndpointSessionClosed;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::EndpointSessionMessage> {
+  static const ChreMessage enum_value = ChreMessage::EndpointSessionMessage;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::EndpointSessionMessageDeliveryStatus> {
+  static const ChreMessage enum_value = ChreMessage::EndpointSessionMessageDeliveryStatus;
+};
+
 struct ChreMessageUnion {
   ChreMessage type;
   void *value;
@@ -872,6 +1417,126 @@
     return type == ChreMessage::MessageDeliveryStatus ?
       reinterpret_cast<const chre::fbs::MessageDeliveryStatusT *>(value) : nullptr;
   }
+  chre::fbs::BtSocketOpenT *AsBtSocketOpen() {
+    return type == ChreMessage::BtSocketOpen ?
+      reinterpret_cast<chre::fbs::BtSocketOpenT *>(value) : nullptr;
+  }
+  const chre::fbs::BtSocketOpenT *AsBtSocketOpen() const {
+    return type == ChreMessage::BtSocketOpen ?
+      reinterpret_cast<const chre::fbs::BtSocketOpenT *>(value) : nullptr;
+  }
+  chre::fbs::BtSocketOpenResponseT *AsBtSocketOpenResponse() {
+    return type == ChreMessage::BtSocketOpenResponse ?
+      reinterpret_cast<chre::fbs::BtSocketOpenResponseT *>(value) : nullptr;
+  }
+  const chre::fbs::BtSocketOpenResponseT *AsBtSocketOpenResponse() const {
+    return type == ChreMessage::BtSocketOpenResponse ?
+      reinterpret_cast<const chre::fbs::BtSocketOpenResponseT *>(value) : nullptr;
+  }
+  chre::fbs::BtSocketCloseT *AsBtSocketClose() {
+    return type == ChreMessage::BtSocketClose ?
+      reinterpret_cast<chre::fbs::BtSocketCloseT *>(value) : nullptr;
+  }
+  const chre::fbs::BtSocketCloseT *AsBtSocketClose() const {
+    return type == ChreMessage::BtSocketClose ?
+      reinterpret_cast<const chre::fbs::BtSocketCloseT *>(value) : nullptr;
+  }
+  chre::fbs::BtSocketCloseResponseT *AsBtSocketCloseResponse() {
+    return type == ChreMessage::BtSocketCloseResponse ?
+      reinterpret_cast<chre::fbs::BtSocketCloseResponseT *>(value) : nullptr;
+  }
+  const chre::fbs::BtSocketCloseResponseT *AsBtSocketCloseResponse() const {
+    return type == ChreMessage::BtSocketCloseResponse ?
+      reinterpret_cast<const chre::fbs::BtSocketCloseResponseT *>(value) : nullptr;
+  }
+  chre::fbs::GetMessageHubsAndEndpointsRequestT *AsGetMessageHubsAndEndpointsRequest() {
+    return type == ChreMessage::GetMessageHubsAndEndpointsRequest ?
+      reinterpret_cast<chre::fbs::GetMessageHubsAndEndpointsRequestT *>(value) : nullptr;
+  }
+  const chre::fbs::GetMessageHubsAndEndpointsRequestT *AsGetMessageHubsAndEndpointsRequest() const {
+    return type == ChreMessage::GetMessageHubsAndEndpointsRequest ?
+      reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsRequestT *>(value) : nullptr;
+  }
+  chre::fbs::GetMessageHubsAndEndpointsResponseT *AsGetMessageHubsAndEndpointsResponse() {
+    return type == ChreMessage::GetMessageHubsAndEndpointsResponse ?
+      reinterpret_cast<chre::fbs::GetMessageHubsAndEndpointsResponseT *>(value) : nullptr;
+  }
+  const chre::fbs::GetMessageHubsAndEndpointsResponseT *AsGetMessageHubsAndEndpointsResponse() const {
+    return type == ChreMessage::GetMessageHubsAndEndpointsResponse ?
+      reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsResponseT *>(value) : nullptr;
+  }
+  chre::fbs::RegisterMessageHubT *AsRegisterMessageHub() {
+    return type == ChreMessage::RegisterMessageHub ?
+      reinterpret_cast<chre::fbs::RegisterMessageHubT *>(value) : nullptr;
+  }
+  const chre::fbs::RegisterMessageHubT *AsRegisterMessageHub() const {
+    return type == ChreMessage::RegisterMessageHub ?
+      reinterpret_cast<const chre::fbs::RegisterMessageHubT *>(value) : nullptr;
+  }
+  chre::fbs::UnregisterMessageHubT *AsUnregisterMessageHub() {
+    return type == ChreMessage::UnregisterMessageHub ?
+      reinterpret_cast<chre::fbs::UnregisterMessageHubT *>(value) : nullptr;
+  }
+  const chre::fbs::UnregisterMessageHubT *AsUnregisterMessageHub() const {
+    return type == ChreMessage::UnregisterMessageHub ?
+      reinterpret_cast<const chre::fbs::UnregisterMessageHubT *>(value) : nullptr;
+  }
+  chre::fbs::RegisterEndpointT *AsRegisterEndpoint() {
+    return type == ChreMessage::RegisterEndpoint ?
+      reinterpret_cast<chre::fbs::RegisterEndpointT *>(value) : nullptr;
+  }
+  const chre::fbs::RegisterEndpointT *AsRegisterEndpoint() const {
+    return type == ChreMessage::RegisterEndpoint ?
+      reinterpret_cast<const chre::fbs::RegisterEndpointT *>(value) : nullptr;
+  }
+  chre::fbs::UnregisterEndpointT *AsUnregisterEndpoint() {
+    return type == ChreMessage::UnregisterEndpoint ?
+      reinterpret_cast<chre::fbs::UnregisterEndpointT *>(value) : nullptr;
+  }
+  const chre::fbs::UnregisterEndpointT *AsUnregisterEndpoint() const {
+    return type == ChreMessage::UnregisterEndpoint ?
+      reinterpret_cast<const chre::fbs::UnregisterEndpointT *>(value) : nullptr;
+  }
+  chre::fbs::OpenEndpointSessionRequestT *AsOpenEndpointSessionRequest() {
+    return type == ChreMessage::OpenEndpointSessionRequest ?
+      reinterpret_cast<chre::fbs::OpenEndpointSessionRequestT *>(value) : nullptr;
+  }
+  const chre::fbs::OpenEndpointSessionRequestT *AsOpenEndpointSessionRequest() const {
+    return type == ChreMessage::OpenEndpointSessionRequest ?
+      reinterpret_cast<const chre::fbs::OpenEndpointSessionRequestT *>(value) : nullptr;
+  }
+  chre::fbs::EndpointSessionOpenedT *AsEndpointSessionOpened() {
+    return type == ChreMessage::EndpointSessionOpened ?
+      reinterpret_cast<chre::fbs::EndpointSessionOpenedT *>(value) : nullptr;
+  }
+  const chre::fbs::EndpointSessionOpenedT *AsEndpointSessionOpened() const {
+    return type == ChreMessage::EndpointSessionOpened ?
+      reinterpret_cast<const chre::fbs::EndpointSessionOpenedT *>(value) : nullptr;
+  }
+  chre::fbs::EndpointSessionClosedT *AsEndpointSessionClosed() {
+    return type == ChreMessage::EndpointSessionClosed ?
+      reinterpret_cast<chre::fbs::EndpointSessionClosedT *>(value) : nullptr;
+  }
+  const chre::fbs::EndpointSessionClosedT *AsEndpointSessionClosed() const {
+    return type == ChreMessage::EndpointSessionClosed ?
+      reinterpret_cast<const chre::fbs::EndpointSessionClosedT *>(value) : nullptr;
+  }
+  chre::fbs::EndpointSessionMessageT *AsEndpointSessionMessage() {
+    return type == ChreMessage::EndpointSessionMessage ?
+      reinterpret_cast<chre::fbs::EndpointSessionMessageT *>(value) : nullptr;
+  }
+  const chre::fbs::EndpointSessionMessageT *AsEndpointSessionMessage() const {
+    return type == ChreMessage::EndpointSessionMessage ?
+      reinterpret_cast<const chre::fbs::EndpointSessionMessageT *>(value) : nullptr;
+  }
+  chre::fbs::EndpointSessionMessageDeliveryStatusT *AsEndpointSessionMessageDeliveryStatus() {
+    return type == ChreMessage::EndpointSessionMessageDeliveryStatus ?
+      reinterpret_cast<chre::fbs::EndpointSessionMessageDeliveryStatusT *>(value) : nullptr;
+  }
+  const chre::fbs::EndpointSessionMessageDeliveryStatusT *AsEndpointSessionMessageDeliveryStatus() const {
+    return type == ChreMessage::EndpointSessionMessageDeliveryStatus ?
+      reinterpret_cast<const chre::fbs::EndpointSessionMessageDeliveryStatusT *>(value) : nullptr;
+  }
 };
 
 bool VerifyChreMessage(flatbuffers::Verifier &verifier, const void *obj, ChreMessage type);
@@ -3888,6 +4553,2027 @@
 
 flatbuffers::Offset<PulseResponse> CreatePulseResponse(flatbuffers::FlatBufferBuilder &_fbb, const PulseResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
 
+struct LeCocChannelInfoT : public flatbuffers::NativeTable {
+  typedef LeCocChannelInfo TableType;
+  int32_t localCid;
+  int32_t remoteCid;
+  int32_t psm;
+  int32_t localMtu;
+  int32_t remoteMtu;
+  int32_t localMps;
+  int32_t remoteMps;
+  int32_t initialRxCredits;
+  int32_t initialTxCredits;
+  LeCocChannelInfoT()
+      : localCid(0),
+        remoteCid(0),
+        psm(0),
+        localMtu(0),
+        remoteMtu(0),
+        localMps(0),
+        remoteMps(0),
+        initialRxCredits(0),
+        initialTxCredits(0) {
+  }
+};
+
+struct LeCocChannelInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef LeCocChannelInfoT NativeTableType;
+  typedef LeCocChannelInfoBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_LOCALCID = 4,
+    VT_REMOTECID = 6,
+    VT_PSM = 8,
+    VT_LOCALMTU = 10,
+    VT_REMOTEMTU = 12,
+    VT_LOCALMPS = 14,
+    VT_REMOTEMPS = 16,
+    VT_INITIALRXCREDITS = 18,
+    VT_INITIALTXCREDITS = 20
+  };
+  int32_t localCid() const {
+    return GetField<int32_t>(VT_LOCALCID, 0);
+  }
+  bool mutate_localCid(int32_t _localCid) {
+    return SetField<int32_t>(VT_LOCALCID, _localCid, 0);
+  }
+  int32_t remoteCid() const {
+    return GetField<int32_t>(VT_REMOTECID, 0);
+  }
+  bool mutate_remoteCid(int32_t _remoteCid) {
+    return SetField<int32_t>(VT_REMOTECID, _remoteCid, 0);
+  }
+  int32_t psm() const {
+    return GetField<int32_t>(VT_PSM, 0);
+  }
+  bool mutate_psm(int32_t _psm) {
+    return SetField<int32_t>(VT_PSM, _psm, 0);
+  }
+  int32_t localMtu() const {
+    return GetField<int32_t>(VT_LOCALMTU, 0);
+  }
+  bool mutate_localMtu(int32_t _localMtu) {
+    return SetField<int32_t>(VT_LOCALMTU, _localMtu, 0);
+  }
+  int32_t remoteMtu() const {
+    return GetField<int32_t>(VT_REMOTEMTU, 0);
+  }
+  bool mutate_remoteMtu(int32_t _remoteMtu) {
+    return SetField<int32_t>(VT_REMOTEMTU, _remoteMtu, 0);
+  }
+  int32_t localMps() const {
+    return GetField<int32_t>(VT_LOCALMPS, 0);
+  }
+  bool mutate_localMps(int32_t _localMps) {
+    return SetField<int32_t>(VT_LOCALMPS, _localMps, 0);
+  }
+  int32_t remoteMps() const {
+    return GetField<int32_t>(VT_REMOTEMPS, 0);
+  }
+  bool mutate_remoteMps(int32_t _remoteMps) {
+    return SetField<int32_t>(VT_REMOTEMPS, _remoteMps, 0);
+  }
+  int32_t initialRxCredits() const {
+    return GetField<int32_t>(VT_INITIALRXCREDITS, 0);
+  }
+  bool mutate_initialRxCredits(int32_t _initialRxCredits) {
+    return SetField<int32_t>(VT_INITIALRXCREDITS, _initialRxCredits, 0);
+  }
+  int32_t initialTxCredits() const {
+    return GetField<int32_t>(VT_INITIALTXCREDITS, 0);
+  }
+  bool mutate_initialTxCredits(int32_t _initialTxCredits) {
+    return SetField<int32_t>(VT_INITIALTXCREDITS, _initialTxCredits, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int32_t>(verifier, VT_LOCALCID) &&
+           VerifyField<int32_t>(verifier, VT_REMOTECID) &&
+           VerifyField<int32_t>(verifier, VT_PSM) &&
+           VerifyField<int32_t>(verifier, VT_LOCALMTU) &&
+           VerifyField<int32_t>(verifier, VT_REMOTEMTU) &&
+           VerifyField<int32_t>(verifier, VT_LOCALMPS) &&
+           VerifyField<int32_t>(verifier, VT_REMOTEMPS) &&
+           VerifyField<int32_t>(verifier, VT_INITIALRXCREDITS) &&
+           VerifyField<int32_t>(verifier, VT_INITIALTXCREDITS) &&
+           verifier.EndTable();
+  }
+  LeCocChannelInfoT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(LeCocChannelInfoT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<LeCocChannelInfo> Pack(flatbuffers::FlatBufferBuilder &_fbb, const LeCocChannelInfoT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct LeCocChannelInfoBuilder {
+  typedef LeCocChannelInfo Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_localCid(int32_t localCid) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_LOCALCID, localCid, 0);
+  }
+  void add_remoteCid(int32_t remoteCid) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_REMOTECID, remoteCid, 0);
+  }
+  void add_psm(int32_t psm) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_PSM, psm, 0);
+  }
+  void add_localMtu(int32_t localMtu) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_LOCALMTU, localMtu, 0);
+  }
+  void add_remoteMtu(int32_t remoteMtu) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_REMOTEMTU, remoteMtu, 0);
+  }
+  void add_localMps(int32_t localMps) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_LOCALMPS, localMps, 0);
+  }
+  void add_remoteMps(int32_t remoteMps) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_REMOTEMPS, remoteMps, 0);
+  }
+  void add_initialRxCredits(int32_t initialRxCredits) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_INITIALRXCREDITS, initialRxCredits, 0);
+  }
+  void add_initialTxCredits(int32_t initialTxCredits) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_INITIALTXCREDITS, initialTxCredits, 0);
+  }
+  explicit LeCocChannelInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  LeCocChannelInfoBuilder &operator=(const LeCocChannelInfoBuilder &);
+  flatbuffers::Offset<LeCocChannelInfo> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<LeCocChannelInfo>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<LeCocChannelInfo> CreateLeCocChannelInfo(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int32_t localCid = 0,
+    int32_t remoteCid = 0,
+    int32_t psm = 0,
+    int32_t localMtu = 0,
+    int32_t remoteMtu = 0,
+    int32_t localMps = 0,
+    int32_t remoteMps = 0,
+    int32_t initialRxCredits = 0,
+    int32_t initialTxCredits = 0) {
+  LeCocChannelInfoBuilder builder_(_fbb);
+  builder_.add_initialTxCredits(initialTxCredits);
+  builder_.add_initialRxCredits(initialRxCredits);
+  builder_.add_remoteMps(remoteMps);
+  builder_.add_localMps(localMps);
+  builder_.add_remoteMtu(remoteMtu);
+  builder_.add_localMtu(localMtu);
+  builder_.add_psm(psm);
+  builder_.add_remoteCid(remoteCid);
+  builder_.add_localCid(localCid);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<LeCocChannelInfo> CreateLeCocChannelInfo(flatbuffers::FlatBufferBuilder &_fbb, const LeCocChannelInfoT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct BtSocketOpenT : public flatbuffers::NativeTable {
+  typedef BtSocketOpen TableType;
+  int64_t socketId;
+  std::vector<int8_t> name;
+  int32_t aclConnectionHandle;
+  chre::fbs::ChannelInfoUnion channelInfo;
+  int64_t hubId;
+  int64_t endpointId;
+  BtSocketOpenT()
+      : socketId(0),
+        aclConnectionHandle(0),
+        hubId(0),
+        endpointId(0) {
+  }
+};
+
+struct BtSocketOpen FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BtSocketOpenT NativeTableType;
+  typedef BtSocketOpenBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SOCKETID = 4,
+    VT_NAME = 6,
+    VT_ACLCONNECTIONHANDLE = 8,
+    VT_CHANNELINFO_TYPE = 10,
+    VT_CHANNELINFO = 12,
+    VT_HUBID = 14,
+    VT_ENDPOINTID = 16
+  };
+  int64_t socketId() const {
+    return GetField<int64_t>(VT_SOCKETID, 0);
+  }
+  bool mutate_socketId(int64_t _socketId) {
+    return SetField<int64_t>(VT_SOCKETID, _socketId, 0);
+  }
+  const flatbuffers::Vector<int8_t> *name() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  flatbuffers::Vector<int8_t> *mutable_name() {
+    return GetPointer<flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  int32_t aclConnectionHandle() const {
+    return GetField<int32_t>(VT_ACLCONNECTIONHANDLE, 0);
+  }
+  bool mutate_aclConnectionHandle(int32_t _aclConnectionHandle) {
+    return SetField<int32_t>(VT_ACLCONNECTIONHANDLE, _aclConnectionHandle, 0);
+  }
+  chre::fbs::ChannelInfo channelInfo_type() const {
+    return static_cast<chre::fbs::ChannelInfo>(GetField<uint8_t>(VT_CHANNELINFO_TYPE, 0));
+  }
+  const void *channelInfo() const {
+    return GetPointer<const void *>(VT_CHANNELINFO);
+  }
+  template<typename T> const T *channelInfo_as() const;
+  const chre::fbs::LeCocChannelInfo *channelInfo_as_LeCocChannelInfo() const {
+    return channelInfo_type() == chre::fbs::ChannelInfo::LeCocChannelInfo ? static_cast<const chre::fbs::LeCocChannelInfo *>(channelInfo()) : nullptr;
+  }
+  void *mutable_channelInfo() {
+    return GetPointer<void *>(VT_CHANNELINFO);
+  }
+  int64_t hubId() const {
+    return GetField<int64_t>(VT_HUBID, 0);
+  }
+  bool mutate_hubId(int64_t _hubId) {
+    return SetField<int64_t>(VT_HUBID, _hubId, 0);
+  }
+  int64_t endpointId() const {
+    return GetField<int64_t>(VT_ENDPOINTID, 0);
+  }
+  bool mutate_endpointId(int64_t _endpointId) {
+    return SetField<int64_t>(VT_ENDPOINTID, _endpointId, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_SOCKETID) &&
+           VerifyOffset(verifier, VT_NAME) &&
+           verifier.VerifyVector(name()) &&
+           VerifyField<int32_t>(verifier, VT_ACLCONNECTIONHANDLE) &&
+           VerifyField<uint8_t>(verifier, VT_CHANNELINFO_TYPE) &&
+           VerifyOffset(verifier, VT_CHANNELINFO) &&
+           VerifyChannelInfo(verifier, channelInfo(), channelInfo_type()) &&
+           VerifyField<int64_t>(verifier, VT_HUBID) &&
+           VerifyField<int64_t>(verifier, VT_ENDPOINTID) &&
+           verifier.EndTable();
+  }
+  BtSocketOpenT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(BtSocketOpenT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<BtSocketOpen> Pack(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketOpenT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+template<> inline const chre::fbs::LeCocChannelInfo *BtSocketOpen::channelInfo_as<chre::fbs::LeCocChannelInfo>() const {
+  return channelInfo_as_LeCocChannelInfo();
+}
+
+struct BtSocketOpenBuilder {
+  typedef BtSocketOpen Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_socketId(int64_t socketId) {
+    fbb_.AddElement<int64_t>(BtSocketOpen::VT_SOCKETID, socketId, 0);
+  }
+  void add_name(flatbuffers::Offset<flatbuffers::Vector<int8_t>> name) {
+    fbb_.AddOffset(BtSocketOpen::VT_NAME, name);
+  }
+  void add_aclConnectionHandle(int32_t aclConnectionHandle) {
+    fbb_.AddElement<int32_t>(BtSocketOpen::VT_ACLCONNECTIONHANDLE, aclConnectionHandle, 0);
+  }
+  void add_channelInfo_type(chre::fbs::ChannelInfo channelInfo_type) {
+    fbb_.AddElement<uint8_t>(BtSocketOpen::VT_CHANNELINFO_TYPE, static_cast<uint8_t>(channelInfo_type), 0);
+  }
+  void add_channelInfo(flatbuffers::Offset<void> channelInfo) {
+    fbb_.AddOffset(BtSocketOpen::VT_CHANNELINFO, channelInfo);
+  }
+  void add_hubId(int64_t hubId) {
+    fbb_.AddElement<int64_t>(BtSocketOpen::VT_HUBID, hubId, 0);
+  }
+  void add_endpointId(int64_t endpointId) {
+    fbb_.AddElement<int64_t>(BtSocketOpen::VT_ENDPOINTID, endpointId, 0);
+  }
+  explicit BtSocketOpenBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  BtSocketOpenBuilder &operator=(const BtSocketOpenBuilder &);
+  flatbuffers::Offset<BtSocketOpen> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<BtSocketOpen>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<BtSocketOpen> CreateBtSocketOpen(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> name = 0,
+    int32_t aclConnectionHandle = 0,
+    chre::fbs::ChannelInfo channelInfo_type = chre::fbs::ChannelInfo::NONE,
+    flatbuffers::Offset<void> channelInfo = 0,
+    int64_t hubId = 0,
+    int64_t endpointId = 0) {
+  BtSocketOpenBuilder builder_(_fbb);
+  builder_.add_endpointId(endpointId);
+  builder_.add_hubId(hubId);
+  builder_.add_socketId(socketId);
+  builder_.add_channelInfo(channelInfo);
+  builder_.add_aclConnectionHandle(aclConnectionHandle);
+  builder_.add_name(name);
+  builder_.add_channelInfo_type(channelInfo_type);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<BtSocketOpen> CreateBtSocketOpenDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    const std::vector<int8_t> *name = nullptr,
+    int32_t aclConnectionHandle = 0,
+    chre::fbs::ChannelInfo channelInfo_type = chre::fbs::ChannelInfo::NONE,
+    flatbuffers::Offset<void> channelInfo = 0,
+    int64_t hubId = 0,
+    int64_t endpointId = 0) {
+  auto name__ = name ? _fbb.CreateVector<int8_t>(*name) : 0;
+  return chre::fbs::CreateBtSocketOpen(
+      _fbb,
+      socketId,
+      name__,
+      aclConnectionHandle,
+      channelInfo_type,
+      channelInfo,
+      hubId,
+      endpointId);
+}
+
+flatbuffers::Offset<BtSocketOpen> CreateBtSocketOpen(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketOpenT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct BtSocketOpenResponseT : public flatbuffers::NativeTable {
+  typedef BtSocketOpenResponse TableType;
+  int64_t socketId;
+  chre::fbs::BtSocketOpenStatus status;
+  std::vector<int8_t> reason;
+  BtSocketOpenResponseT()
+      : socketId(0),
+        status(chre::fbs::BtSocketOpenStatus::SUCCESS) {
+  }
+};
+
+struct BtSocketOpenResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BtSocketOpenResponseT NativeTableType;
+  typedef BtSocketOpenResponseBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SOCKETID = 4,
+    VT_STATUS = 6,
+    VT_REASON = 8
+  };
+  int64_t socketId() const {
+    return GetField<int64_t>(VT_SOCKETID, 0);
+  }
+  bool mutate_socketId(int64_t _socketId) {
+    return SetField<int64_t>(VT_SOCKETID, _socketId, 0);
+  }
+  chre::fbs::BtSocketOpenStatus status() const {
+    return static_cast<chre::fbs::BtSocketOpenStatus>(GetField<int8_t>(VT_STATUS, 0));
+  }
+  bool mutate_status(chre::fbs::BtSocketOpenStatus _status) {
+    return SetField<int8_t>(VT_STATUS, static_cast<int8_t>(_status), 0);
+  }
+  const flatbuffers::Vector<int8_t> *reason() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_REASON);
+  }
+  flatbuffers::Vector<int8_t> *mutable_reason() {
+    return GetPointer<flatbuffers::Vector<int8_t> *>(VT_REASON);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_SOCKETID) &&
+           VerifyField<int8_t>(verifier, VT_STATUS) &&
+           VerifyOffset(verifier, VT_REASON) &&
+           verifier.VerifyVector(reason()) &&
+           verifier.EndTable();
+  }
+  BtSocketOpenResponseT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(BtSocketOpenResponseT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<BtSocketOpenResponse> Pack(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketOpenResponseT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct BtSocketOpenResponseBuilder {
+  typedef BtSocketOpenResponse Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_socketId(int64_t socketId) {
+    fbb_.AddElement<int64_t>(BtSocketOpenResponse::VT_SOCKETID, socketId, 0);
+  }
+  void add_status(chre::fbs::BtSocketOpenStatus status) {
+    fbb_.AddElement<int8_t>(BtSocketOpenResponse::VT_STATUS, static_cast<int8_t>(status), 0);
+  }
+  void add_reason(flatbuffers::Offset<flatbuffers::Vector<int8_t>> reason) {
+    fbb_.AddOffset(BtSocketOpenResponse::VT_REASON, reason);
+  }
+  explicit BtSocketOpenResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  BtSocketOpenResponseBuilder &operator=(const BtSocketOpenResponseBuilder &);
+  flatbuffers::Offset<BtSocketOpenResponse> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<BtSocketOpenResponse>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<BtSocketOpenResponse> CreateBtSocketOpenResponse(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    chre::fbs::BtSocketOpenStatus status = chre::fbs::BtSocketOpenStatus::SUCCESS,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> reason = 0) {
+  BtSocketOpenResponseBuilder builder_(_fbb);
+  builder_.add_socketId(socketId);
+  builder_.add_reason(reason);
+  builder_.add_status(status);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<BtSocketOpenResponse> CreateBtSocketOpenResponseDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    chre::fbs::BtSocketOpenStatus status = chre::fbs::BtSocketOpenStatus::SUCCESS,
+    const std::vector<int8_t> *reason = nullptr) {
+  auto reason__ = reason ? _fbb.CreateVector<int8_t>(*reason) : 0;
+  return chre::fbs::CreateBtSocketOpenResponse(
+      _fbb,
+      socketId,
+      status,
+      reason__);
+}
+
+flatbuffers::Offset<BtSocketOpenResponse> CreateBtSocketOpenResponse(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketOpenResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct BtSocketCloseT : public flatbuffers::NativeTable {
+  typedef BtSocketClose TableType;
+  int64_t socketId;
+  std::vector<int8_t> reason;
+  BtSocketCloseT()
+      : socketId(0) {
+  }
+};
+
+struct BtSocketClose FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BtSocketCloseT NativeTableType;
+  typedef BtSocketCloseBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SOCKETID = 4,
+    VT_REASON = 6
+  };
+  int64_t socketId() const {
+    return GetField<int64_t>(VT_SOCKETID, 0);
+  }
+  bool mutate_socketId(int64_t _socketId) {
+    return SetField<int64_t>(VT_SOCKETID, _socketId, 0);
+  }
+  const flatbuffers::Vector<int8_t> *reason() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_REASON);
+  }
+  flatbuffers::Vector<int8_t> *mutable_reason() {
+    return GetPointer<flatbuffers::Vector<int8_t> *>(VT_REASON);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_SOCKETID) &&
+           VerifyOffset(verifier, VT_REASON) &&
+           verifier.VerifyVector(reason()) &&
+           verifier.EndTable();
+  }
+  BtSocketCloseT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(BtSocketCloseT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<BtSocketClose> Pack(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketCloseT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct BtSocketCloseBuilder {
+  typedef BtSocketClose Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_socketId(int64_t socketId) {
+    fbb_.AddElement<int64_t>(BtSocketClose::VT_SOCKETID, socketId, 0);
+  }
+  void add_reason(flatbuffers::Offset<flatbuffers::Vector<int8_t>> reason) {
+    fbb_.AddOffset(BtSocketClose::VT_REASON, reason);
+  }
+  explicit BtSocketCloseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  BtSocketCloseBuilder &operator=(const BtSocketCloseBuilder &);
+  flatbuffers::Offset<BtSocketClose> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<BtSocketClose>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<BtSocketClose> CreateBtSocketClose(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> reason = 0) {
+  BtSocketCloseBuilder builder_(_fbb);
+  builder_.add_socketId(socketId);
+  builder_.add_reason(reason);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<BtSocketClose> CreateBtSocketCloseDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    const std::vector<int8_t> *reason = nullptr) {
+  auto reason__ = reason ? _fbb.CreateVector<int8_t>(*reason) : 0;
+  return chre::fbs::CreateBtSocketClose(
+      _fbb,
+      socketId,
+      reason__);
+}
+
+flatbuffers::Offset<BtSocketClose> CreateBtSocketClose(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketCloseT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct BtSocketCloseResponseT : public flatbuffers::NativeTable {
+  typedef BtSocketCloseResponse TableType;
+  int64_t socketId;
+  BtSocketCloseResponseT()
+      : socketId(0) {
+  }
+};
+
+struct BtSocketCloseResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BtSocketCloseResponseT NativeTableType;
+  typedef BtSocketCloseResponseBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SOCKETID = 4
+  };
+  int64_t socketId() const {
+    return GetField<int64_t>(VT_SOCKETID, 0);
+  }
+  bool mutate_socketId(int64_t _socketId) {
+    return SetField<int64_t>(VT_SOCKETID, _socketId, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_SOCKETID) &&
+           verifier.EndTable();
+  }
+  BtSocketCloseResponseT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(BtSocketCloseResponseT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<BtSocketCloseResponse> Pack(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketCloseResponseT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct BtSocketCloseResponseBuilder {
+  typedef BtSocketCloseResponse Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_socketId(int64_t socketId) {
+    fbb_.AddElement<int64_t>(BtSocketCloseResponse::VT_SOCKETID, socketId, 0);
+  }
+  explicit BtSocketCloseResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  BtSocketCloseResponseBuilder &operator=(const BtSocketCloseResponseBuilder &);
+  flatbuffers::Offset<BtSocketCloseResponse> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<BtSocketCloseResponse>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<BtSocketCloseResponse> CreateBtSocketCloseResponse(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0) {
+  BtSocketCloseResponseBuilder builder_(_fbb);
+  builder_.add_socketId(socketId);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<BtSocketCloseResponse> CreateBtSocketCloseResponse(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketCloseResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct VendorHubInfoT : public flatbuffers::NativeTable {
+  typedef VendorHubInfo TableType;
+  std::vector<int8_t> name;
+  uint32_t version;
+  std::vector<uint8_t> extended_info;
+  VendorHubInfoT()
+      : version(0) {
+  }
+};
+
+struct VendorHubInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef VendorHubInfoT NativeTableType;
+  typedef VendorHubInfoBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_NAME = 4,
+    VT_VERSION = 6,
+    VT_EXTENDED_INFO = 8
+  };
+  /// The name of the hub. Nominally a UTF-8 string, but note that we're not
+  /// using the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  const flatbuffers::Vector<int8_t> *name() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  flatbuffers::Vector<int8_t> *mutable_name() {
+    return GetPointer<flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  /// Hub version
+  uint32_t version() const {
+    return GetField<uint32_t>(VT_VERSION, 0);
+  }
+  bool mutate_version(uint32_t _version) {
+    return SetField<uint32_t>(VT_VERSION, _version, 0);
+  }
+  /// Additional vendor-defined data
+  const flatbuffers::Vector<uint8_t> *extended_info() const {
+    return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_EXTENDED_INFO);
+  }
+  flatbuffers::Vector<uint8_t> *mutable_extended_info() {
+    return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_EXTENDED_INFO);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_NAME) &&
+           verifier.VerifyVector(name()) &&
+           VerifyField<uint32_t>(verifier, VT_VERSION) &&
+           VerifyOffset(verifier, VT_EXTENDED_INFO) &&
+           verifier.VerifyVector(extended_info()) &&
+           verifier.EndTable();
+  }
+  VendorHubInfoT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(VendorHubInfoT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<VendorHubInfo> Pack(flatbuffers::FlatBufferBuilder &_fbb, const VendorHubInfoT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct VendorHubInfoBuilder {
+  typedef VendorHubInfo Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_name(flatbuffers::Offset<flatbuffers::Vector<int8_t>> name) {
+    fbb_.AddOffset(VendorHubInfo::VT_NAME, name);
+  }
+  void add_version(uint32_t version) {
+    fbb_.AddElement<uint32_t>(VendorHubInfo::VT_VERSION, version, 0);
+  }
+  void add_extended_info(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> extended_info) {
+    fbb_.AddOffset(VendorHubInfo::VT_EXTENDED_INFO, extended_info);
+  }
+  explicit VendorHubInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  VendorHubInfoBuilder &operator=(const VendorHubInfoBuilder &);
+  flatbuffers::Offset<VendorHubInfo> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<VendorHubInfo>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<VendorHubInfo> CreateVendorHubInfo(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> name = 0,
+    uint32_t version = 0,
+    flatbuffers::Offset<flatbuffers::Vector<uint8_t>> extended_info = 0) {
+  VendorHubInfoBuilder builder_(_fbb);
+  builder_.add_extended_info(extended_info);
+  builder_.add_version(version);
+  builder_.add_name(name);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<VendorHubInfo> CreateVendorHubInfoDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    const std::vector<int8_t> *name = nullptr,
+    uint32_t version = 0,
+    const std::vector<uint8_t> *extended_info = nullptr) {
+  auto name__ = name ? _fbb.CreateVector<int8_t>(*name) : 0;
+  auto extended_info__ = extended_info ? _fbb.CreateVector<uint8_t>(*extended_info) : 0;
+  return chre::fbs::CreateVendorHubInfo(
+      _fbb,
+      name__,
+      version,
+      extended_info__);
+}
+
+flatbuffers::Offset<VendorHubInfo> CreateVendorHubInfo(flatbuffers::FlatBufferBuilder &_fbb, const VendorHubInfoT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct MessageHubT : public flatbuffers::NativeTable {
+  typedef MessageHub TableType;
+  int64_t id;
+  chre::fbs::MessageHubDetailsUnion details;
+  MessageHubT()
+      : id(0) {
+  }
+};
+
+struct MessageHub FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef MessageHubT NativeTableType;
+  typedef MessageHubBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4,
+    VT_DETAILS_TYPE = 6,
+    VT_DETAILS = 8
+  };
+  /// The hub id. -1 is reserved and 0 is invalid. 0x416e64726f696400 represents
+  /// the ContextHub service.
+  int64_t id() const {
+    return GetField<int64_t>(VT_ID, 0);
+  }
+  bool mutate_id(int64_t _id) {
+    return SetField<int64_t>(VT_ID, _id, 0);
+  }
+  chre::fbs::MessageHubDetails details_type() const {
+    return static_cast<chre::fbs::MessageHubDetails>(GetField<uint8_t>(VT_DETAILS_TYPE, 0));
+  }
+  /// Details of the message hub.
+  const void *details() const {
+    return GetPointer<const void *>(VT_DETAILS);
+  }
+  template<typename T> const T *details_as() const;
+  const chre::fbs::HubInfoResponse *details_as_HubInfoResponse() const {
+    return details_type() == chre::fbs::MessageHubDetails::HubInfoResponse ? static_cast<const chre::fbs::HubInfoResponse *>(details()) : nullptr;
+  }
+  const chre::fbs::VendorHubInfo *details_as_VendorHubInfo() const {
+    return details_type() == chre::fbs::MessageHubDetails::VendorHubInfo ? static_cast<const chre::fbs::VendorHubInfo *>(details()) : nullptr;
+  }
+  void *mutable_details() {
+    return GetPointer<void *>(VT_DETAILS);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_ID) &&
+           VerifyField<uint8_t>(verifier, VT_DETAILS_TYPE) &&
+           VerifyOffset(verifier, VT_DETAILS) &&
+           VerifyMessageHubDetails(verifier, details(), details_type()) &&
+           verifier.EndTable();
+  }
+  MessageHubT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(MessageHubT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<MessageHub> Pack(flatbuffers::FlatBufferBuilder &_fbb, const MessageHubT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+template<> inline const chre::fbs::HubInfoResponse *MessageHub::details_as<chre::fbs::HubInfoResponse>() const {
+  return details_as_HubInfoResponse();
+}
+
+template<> inline const chre::fbs::VendorHubInfo *MessageHub::details_as<chre::fbs::VendorHubInfo>() const {
+  return details_as_VendorHubInfo();
+}
+
+struct MessageHubBuilder {
+  typedef MessageHub Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(int64_t id) {
+    fbb_.AddElement<int64_t>(MessageHub::VT_ID, id, 0);
+  }
+  void add_details_type(chre::fbs::MessageHubDetails details_type) {
+    fbb_.AddElement<uint8_t>(MessageHub::VT_DETAILS_TYPE, static_cast<uint8_t>(details_type), 0);
+  }
+  void add_details(flatbuffers::Offset<void> details) {
+    fbb_.AddOffset(MessageHub::VT_DETAILS, details);
+  }
+  explicit MessageHubBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  MessageHubBuilder &operator=(const MessageHubBuilder &);
+  flatbuffers::Offset<MessageHub> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<MessageHub>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<MessageHub> CreateMessageHub(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t id = 0,
+    chre::fbs::MessageHubDetails details_type = chre::fbs::MessageHubDetails::NONE,
+    flatbuffers::Offset<void> details = 0) {
+  MessageHubBuilder builder_(_fbb);
+  builder_.add_id(id);
+  builder_.add_details(details);
+  builder_.add_details_type(details_type);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<MessageHub> CreateMessageHub(flatbuffers::FlatBufferBuilder &_fbb, const MessageHubT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct RegisterMessageHubT : public flatbuffers::NativeTable {
+  typedef RegisterMessageHub TableType;
+  std::unique_ptr<chre::fbs::MessageHubT> hub;
+  RegisterMessageHubT() {
+  }
+};
+
+struct RegisterMessageHub FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef RegisterMessageHubT NativeTableType;
+  typedef RegisterMessageHubBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_HUB = 4
+  };
+  const chre::fbs::MessageHub *hub() const {
+    return GetPointer<const chre::fbs::MessageHub *>(VT_HUB);
+  }
+  chre::fbs::MessageHub *mutable_hub() {
+    return GetPointer<chre::fbs::MessageHub *>(VT_HUB);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_HUB) &&
+           verifier.VerifyTable(hub()) &&
+           verifier.EndTable();
+  }
+  RegisterMessageHubT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(RegisterMessageHubT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<RegisterMessageHub> Pack(flatbuffers::FlatBufferBuilder &_fbb, const RegisterMessageHubT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct RegisterMessageHubBuilder {
+  typedef RegisterMessageHub Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_hub(flatbuffers::Offset<chre::fbs::MessageHub> hub) {
+    fbb_.AddOffset(RegisterMessageHub::VT_HUB, hub);
+  }
+  explicit RegisterMessageHubBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  RegisterMessageHubBuilder &operator=(const RegisterMessageHubBuilder &);
+  flatbuffers::Offset<RegisterMessageHub> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<RegisterMessageHub>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<RegisterMessageHub> CreateRegisterMessageHub(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::MessageHub> hub = 0) {
+  RegisterMessageHubBuilder builder_(_fbb);
+  builder_.add_hub(hub);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<RegisterMessageHub> CreateRegisterMessageHub(flatbuffers::FlatBufferBuilder &_fbb, const RegisterMessageHubT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct UnregisterMessageHubT : public flatbuffers::NativeTable {
+  typedef UnregisterMessageHub TableType;
+  int64_t id;
+  UnregisterMessageHubT()
+      : id(0) {
+  }
+};
+
+struct UnregisterMessageHub FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef UnregisterMessageHubT NativeTableType;
+  typedef UnregisterMessageHubBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4
+  };
+  int64_t id() const {
+    return GetField<int64_t>(VT_ID, 0);
+  }
+  bool mutate_id(int64_t _id) {
+    return SetField<int64_t>(VT_ID, _id, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_ID) &&
+           verifier.EndTable();
+  }
+  UnregisterMessageHubT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(UnregisterMessageHubT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<UnregisterMessageHub> Pack(flatbuffers::FlatBufferBuilder &_fbb, const UnregisterMessageHubT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct UnregisterMessageHubBuilder {
+  typedef UnregisterMessageHub Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(int64_t id) {
+    fbb_.AddElement<int64_t>(UnregisterMessageHub::VT_ID, id, 0);
+  }
+  explicit UnregisterMessageHubBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  UnregisterMessageHubBuilder &operator=(const UnregisterMessageHubBuilder &);
+  flatbuffers::Offset<UnregisterMessageHub> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<UnregisterMessageHub>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<UnregisterMessageHub> CreateUnregisterMessageHub(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t id = 0) {
+  UnregisterMessageHubBuilder builder_(_fbb);
+  builder_.add_id(id);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<UnregisterMessageHub> CreateUnregisterMessageHub(flatbuffers::FlatBufferBuilder &_fbb, const UnregisterMessageHubT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct EndpointIdT : public flatbuffers::NativeTable {
+  typedef EndpointId TableType;
+  int64_t hubId;
+  int64_t id;
+  EndpointIdT()
+      : hubId(0),
+        id(0) {
+  }
+};
+
+struct EndpointId FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointIdT NativeTableType;
+  typedef EndpointIdBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_HUBID = 4,
+    VT_ID = 6
+  };
+  /// Id of the hub hosting the endpoint
+  int64_t hubId() const {
+    return GetField<int64_t>(VT_HUBID, 0);
+  }
+  bool mutate_hubId(int64_t _hubId) {
+    return SetField<int64_t>(VT_HUBID, _hubId, 0);
+  }
+  /// The id of the endpoint scoped to the hub
+  int64_t id() const {
+    return GetField<int64_t>(VT_ID, 0);
+  }
+  bool mutate_id(int64_t _id) {
+    return SetField<int64_t>(VT_ID, _id, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_HUBID) &&
+           VerifyField<int64_t>(verifier, VT_ID) &&
+           verifier.EndTable();
+  }
+  EndpointIdT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(EndpointIdT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<EndpointId> Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointIdT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct EndpointIdBuilder {
+  typedef EndpointId Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_hubId(int64_t hubId) {
+    fbb_.AddElement<int64_t>(EndpointId::VT_HUBID, hubId, 0);
+  }
+  void add_id(int64_t id) {
+    fbb_.AddElement<int64_t>(EndpointId::VT_ID, id, 0);
+  }
+  explicit EndpointIdBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointIdBuilder &operator=(const EndpointIdBuilder &);
+  flatbuffers::Offset<EndpointId> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointId>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointId> CreateEndpointId(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t hubId = 0,
+    int64_t id = 0) {
+  EndpointIdBuilder builder_(_fbb);
+  builder_.add_id(id);
+  builder_.add_hubId(hubId);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<EndpointId> CreateEndpointId(flatbuffers::FlatBufferBuilder &_fbb, const EndpointIdT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct ServiceT : public flatbuffers::NativeTable {
+  typedef Service TableType;
+  chre::fbs::RpcFormat format;
+  std::vector<int8_t> descriptor;
+  uint32_t major_version;
+  uint32_t minor_version;
+  ServiceT()
+      : format(chre::fbs::RpcFormat::CUSTOM),
+        major_version(0),
+        minor_version(0) {
+  }
+};
+
+struct Service FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef ServiceT NativeTableType;
+  typedef ServiceBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_FORMAT = 4,
+    VT_DESCRIPTOR = 6,
+    VT_MAJOR_VERSION = 8,
+    VT_MINOR_VERSION = 10
+  };
+  chre::fbs::RpcFormat format() const {
+    return static_cast<chre::fbs::RpcFormat>(GetField<uint8_t>(VT_FORMAT, 0));
+  }
+  bool mutate_format(chre::fbs::RpcFormat _format) {
+    return SetField<uint8_t>(VT_FORMAT, static_cast<uint8_t>(_format), 0);
+  }
+  /// Service descriptor. Nominally a UTF-8 string, but note that we're not
+  /// using the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  const flatbuffers::Vector<int8_t> *descriptor() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_DESCRIPTOR);
+  }
+  flatbuffers::Vector<int8_t> *mutable_descriptor() {
+    return GetPointer<flatbuffers::Vector<int8_t> *>(VT_DESCRIPTOR);
+  }
+  /// Breaking changes should bump the major version.
+  uint32_t major_version() const {
+    return GetField<uint32_t>(VT_MAJOR_VERSION, 0);
+  }
+  bool mutate_major_version(uint32_t _major_version) {
+    return SetField<uint32_t>(VT_MAJOR_VERSION, _major_version, 0);
+  }
+  /// Monotonically increasing minor version.
+  uint32_t minor_version() const {
+    return GetField<uint32_t>(VT_MINOR_VERSION, 0);
+  }
+  bool mutate_minor_version(uint32_t _minor_version) {
+    return SetField<uint32_t>(VT_MINOR_VERSION, _minor_version, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint8_t>(verifier, VT_FORMAT) &&
+           VerifyOffset(verifier, VT_DESCRIPTOR) &&
+           verifier.VerifyVector(descriptor()) &&
+           VerifyField<uint32_t>(verifier, VT_MAJOR_VERSION) &&
+           VerifyField<uint32_t>(verifier, VT_MINOR_VERSION) &&
+           verifier.EndTable();
+  }
+  ServiceT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(ServiceT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<Service> Pack(flatbuffers::FlatBufferBuilder &_fbb, const ServiceT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct ServiceBuilder {
+  typedef Service Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_format(chre::fbs::RpcFormat format) {
+    fbb_.AddElement<uint8_t>(Service::VT_FORMAT, static_cast<uint8_t>(format), 0);
+  }
+  void add_descriptor(flatbuffers::Offset<flatbuffers::Vector<int8_t>> descriptor) {
+    fbb_.AddOffset(Service::VT_DESCRIPTOR, descriptor);
+  }
+  void add_major_version(uint32_t major_version) {
+    fbb_.AddElement<uint32_t>(Service::VT_MAJOR_VERSION, major_version, 0);
+  }
+  void add_minor_version(uint32_t minor_version) {
+    fbb_.AddElement<uint32_t>(Service::VT_MINOR_VERSION, minor_version, 0);
+  }
+  explicit ServiceBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  ServiceBuilder &operator=(const ServiceBuilder &);
+  flatbuffers::Offset<Service> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<Service>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<Service> CreateService(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    chre::fbs::RpcFormat format = chre::fbs::RpcFormat::CUSTOM,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> descriptor = 0,
+    uint32_t major_version = 0,
+    uint32_t minor_version = 0) {
+  ServiceBuilder builder_(_fbb);
+  builder_.add_minor_version(minor_version);
+  builder_.add_major_version(major_version);
+  builder_.add_descriptor(descriptor);
+  builder_.add_format(format);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<Service> CreateServiceDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    chre::fbs::RpcFormat format = chre::fbs::RpcFormat::CUSTOM,
+    const std::vector<int8_t> *descriptor = nullptr,
+    uint32_t major_version = 0,
+    uint32_t minor_version = 0) {
+  auto descriptor__ = descriptor ? _fbb.CreateVector<int8_t>(*descriptor) : 0;
+  return chre::fbs::CreateService(
+      _fbb,
+      format,
+      descriptor__,
+      major_version,
+      minor_version);
+}
+
+flatbuffers::Offset<Service> CreateService(flatbuffers::FlatBufferBuilder &_fbb, const ServiceT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct EndpointInfoT : public flatbuffers::NativeTable {
+  typedef EndpointInfo TableType;
+  std::unique_ptr<chre::fbs::EndpointIdT> id;
+  chre::fbs::EndpointType type;
+  std::vector<int8_t> name;
+  uint32_t version;
+  uint32_t required_permissions;
+  std::vector<std::unique_ptr<chre::fbs::ServiceT>> services;
+  EndpointInfoT()
+      : type(chre::fbs::EndpointType::INVALID),
+        version(0),
+        required_permissions(0) {
+  }
+};
+
+struct EndpointInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointInfoT NativeTableType;
+  typedef EndpointInfoBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4,
+    VT_TYPE = 6,
+    VT_NAME = 8,
+    VT_VERSION = 10,
+    VT_REQUIRED_PERMISSIONS = 12,
+    VT_SERVICES = 14
+  };
+  const chre::fbs::EndpointId *id() const {
+    return GetPointer<const chre::fbs::EndpointId *>(VT_ID);
+  }
+  chre::fbs::EndpointId *mutable_id() {
+    return GetPointer<chre::fbs::EndpointId *>(VT_ID);
+  }
+  chre::fbs::EndpointType type() const {
+    return static_cast<chre::fbs::EndpointType>(GetField<uint8_t>(VT_TYPE, 0));
+  }
+  bool mutate_type(chre::fbs::EndpointType _type) {
+    return SetField<uint8_t>(VT_TYPE, static_cast<uint8_t>(_type), 0);
+  }
+  /// Endpoing name. Nominally a UTF-8 string, but note that we're not using
+  /// the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  const flatbuffers::Vector<int8_t> *name() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  flatbuffers::Vector<int8_t> *mutable_name() {
+    return GetPointer<flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  uint32_t version() const {
+    return GetField<uint32_t>(VT_VERSION, 0);
+  }
+  bool mutate_version(uint32_t _version) {
+    return SetField<uint32_t>(VT_VERSION, _version, 0);
+  }
+  /// Values from CHRE_MESSAGE_PERMISSION_*
+  uint32_t required_permissions() const {
+    return GetField<uint32_t>(VT_REQUIRED_PERMISSIONS, 0);
+  }
+  bool mutate_required_permissions(uint32_t _required_permissions) {
+    return SetField<uint32_t>(VT_REQUIRED_PERMISSIONS, _required_permissions, 0);
+  }
+  const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>> *services() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>> *>(VT_SERVICES);
+  }
+  flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>> *mutable_services() {
+    return GetPointer<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>> *>(VT_SERVICES);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_ID) &&
+           verifier.VerifyTable(id()) &&
+           VerifyField<uint8_t>(verifier, VT_TYPE) &&
+           VerifyOffset(verifier, VT_NAME) &&
+           verifier.VerifyVector(name()) &&
+           VerifyField<uint32_t>(verifier, VT_VERSION) &&
+           VerifyField<uint32_t>(verifier, VT_REQUIRED_PERMISSIONS) &&
+           VerifyOffset(verifier, VT_SERVICES) &&
+           verifier.VerifyVector(services()) &&
+           verifier.VerifyVectorOfTables(services()) &&
+           verifier.EndTable();
+  }
+  EndpointInfoT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(EndpointInfoT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<EndpointInfo> Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointInfoT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct EndpointInfoBuilder {
+  typedef EndpointInfo Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(flatbuffers::Offset<chre::fbs::EndpointId> id) {
+    fbb_.AddOffset(EndpointInfo::VT_ID, id);
+  }
+  void add_type(chre::fbs::EndpointType type) {
+    fbb_.AddElement<uint8_t>(EndpointInfo::VT_TYPE, static_cast<uint8_t>(type), 0);
+  }
+  void add_name(flatbuffers::Offset<flatbuffers::Vector<int8_t>> name) {
+    fbb_.AddOffset(EndpointInfo::VT_NAME, name);
+  }
+  void add_version(uint32_t version) {
+    fbb_.AddElement<uint32_t>(EndpointInfo::VT_VERSION, version, 0);
+  }
+  void add_required_permissions(uint32_t required_permissions) {
+    fbb_.AddElement<uint32_t>(EndpointInfo::VT_REQUIRED_PERMISSIONS, required_permissions, 0);
+  }
+  void add_services(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>>> services) {
+    fbb_.AddOffset(EndpointInfo::VT_SERVICES, services);
+  }
+  explicit EndpointInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointInfoBuilder &operator=(const EndpointInfoBuilder &);
+  flatbuffers::Offset<EndpointInfo> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointInfo>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointInfo> CreateEndpointInfo(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::EndpointId> id = 0,
+    chre::fbs::EndpointType type = chre::fbs::EndpointType::INVALID,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> name = 0,
+    uint32_t version = 0,
+    uint32_t required_permissions = 0,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>>> services = 0) {
+  EndpointInfoBuilder builder_(_fbb);
+  builder_.add_services(services);
+  builder_.add_required_permissions(required_permissions);
+  builder_.add_version(version);
+  builder_.add_name(name);
+  builder_.add_id(id);
+  builder_.add_type(type);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<EndpointInfo> CreateEndpointInfoDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::EndpointId> id = 0,
+    chre::fbs::EndpointType type = chre::fbs::EndpointType::INVALID,
+    const std::vector<int8_t> *name = nullptr,
+    uint32_t version = 0,
+    uint32_t required_permissions = 0,
+    const std::vector<flatbuffers::Offset<chre::fbs::Service>> *services = nullptr) {
+  auto name__ = name ? _fbb.CreateVector<int8_t>(*name) : 0;
+  auto services__ = services ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::Service>>(*services) : 0;
+  return chre::fbs::CreateEndpointInfo(
+      _fbb,
+      id,
+      type,
+      name__,
+      version,
+      required_permissions,
+      services__);
+}
+
+flatbuffers::Offset<EndpointInfo> CreateEndpointInfo(flatbuffers::FlatBufferBuilder &_fbb, const EndpointInfoT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct RegisterEndpointT : public flatbuffers::NativeTable {
+  typedef RegisterEndpoint TableType;
+  std::unique_ptr<chre::fbs::EndpointInfoT> endpoint;
+  RegisterEndpointT() {
+  }
+};
+
+struct RegisterEndpoint FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef RegisterEndpointT NativeTableType;
+  typedef RegisterEndpointBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ENDPOINT = 4
+  };
+  const chre::fbs::EndpointInfo *endpoint() const {
+    return GetPointer<const chre::fbs::EndpointInfo *>(VT_ENDPOINT);
+  }
+  chre::fbs::EndpointInfo *mutable_endpoint() {
+    return GetPointer<chre::fbs::EndpointInfo *>(VT_ENDPOINT);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_ENDPOINT) &&
+           verifier.VerifyTable(endpoint()) &&
+           verifier.EndTable();
+  }
+  RegisterEndpointT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(RegisterEndpointT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<RegisterEndpoint> Pack(flatbuffers::FlatBufferBuilder &_fbb, const RegisterEndpointT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct RegisterEndpointBuilder {
+  typedef RegisterEndpoint Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_endpoint(flatbuffers::Offset<chre::fbs::EndpointInfo> endpoint) {
+    fbb_.AddOffset(RegisterEndpoint::VT_ENDPOINT, endpoint);
+  }
+  explicit RegisterEndpointBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  RegisterEndpointBuilder &operator=(const RegisterEndpointBuilder &);
+  flatbuffers::Offset<RegisterEndpoint> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<RegisterEndpoint>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<RegisterEndpoint> CreateRegisterEndpoint(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::EndpointInfo> endpoint = 0) {
+  RegisterEndpointBuilder builder_(_fbb);
+  builder_.add_endpoint(endpoint);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<RegisterEndpoint> CreateRegisterEndpoint(flatbuffers::FlatBufferBuilder &_fbb, const RegisterEndpointT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct UnregisterEndpointT : public flatbuffers::NativeTable {
+  typedef UnregisterEndpoint TableType;
+  std::unique_ptr<chre::fbs::EndpointIdT> endpoint;
+  UnregisterEndpointT() {
+  }
+};
+
+struct UnregisterEndpoint FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef UnregisterEndpointT NativeTableType;
+  typedef UnregisterEndpointBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ENDPOINT = 4
+  };
+  const chre::fbs::EndpointId *endpoint() const {
+    return GetPointer<const chre::fbs::EndpointId *>(VT_ENDPOINT);
+  }
+  chre::fbs::EndpointId *mutable_endpoint() {
+    return GetPointer<chre::fbs::EndpointId *>(VT_ENDPOINT);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_ENDPOINT) &&
+           verifier.VerifyTable(endpoint()) &&
+           verifier.EndTable();
+  }
+  UnregisterEndpointT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(UnregisterEndpointT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<UnregisterEndpoint> Pack(flatbuffers::FlatBufferBuilder &_fbb, const UnregisterEndpointT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct UnregisterEndpointBuilder {
+  typedef UnregisterEndpoint Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_endpoint(flatbuffers::Offset<chre::fbs::EndpointId> endpoint) {
+    fbb_.AddOffset(UnregisterEndpoint::VT_ENDPOINT, endpoint);
+  }
+  explicit UnregisterEndpointBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  UnregisterEndpointBuilder &operator=(const UnregisterEndpointBuilder &);
+  flatbuffers::Offset<UnregisterEndpoint> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<UnregisterEndpoint>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<UnregisterEndpoint> CreateUnregisterEndpoint(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::EndpointId> endpoint = 0) {
+  UnregisterEndpointBuilder builder_(_fbb);
+  builder_.add_endpoint(endpoint);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<UnregisterEndpoint> CreateUnregisterEndpoint(flatbuffers::FlatBufferBuilder &_fbb, const UnregisterEndpointT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct GetMessageHubsAndEndpointsRequestT : public flatbuffers::NativeTable {
+  typedef GetMessageHubsAndEndpointsRequest TableType;
+  GetMessageHubsAndEndpointsRequestT() {
+  }
+};
+
+/// HAL->CHRE, indicates the HAL is coming up
+struct GetMessageHubsAndEndpointsRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef GetMessageHubsAndEndpointsRequestT NativeTableType;
+  typedef GetMessageHubsAndEndpointsRequestBuilder Builder;
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           verifier.EndTable();
+  }
+  GetMessageHubsAndEndpointsRequestT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(GetMessageHubsAndEndpointsRequestT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<GetMessageHubsAndEndpointsRequest> Pack(flatbuffers::FlatBufferBuilder &_fbb, const GetMessageHubsAndEndpointsRequestT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct GetMessageHubsAndEndpointsRequestBuilder {
+  typedef GetMessageHubsAndEndpointsRequest Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  explicit GetMessageHubsAndEndpointsRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  GetMessageHubsAndEndpointsRequestBuilder &operator=(const GetMessageHubsAndEndpointsRequestBuilder &);
+  flatbuffers::Offset<GetMessageHubsAndEndpointsRequest> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<GetMessageHubsAndEndpointsRequest>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsRequest> CreateGetMessageHubsAndEndpointsRequest(
+    flatbuffers::FlatBufferBuilder &_fbb) {
+  GetMessageHubsAndEndpointsRequestBuilder builder_(_fbb);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<GetMessageHubsAndEndpointsRequest> CreateGetMessageHubsAndEndpointsRequest(flatbuffers::FlatBufferBuilder &_fbb, const GetMessageHubsAndEndpointsRequestT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct GetMessageHubsAndEndpointsResponseT : public flatbuffers::NativeTable {
+  typedef GetMessageHubsAndEndpointsResponse TableType;
+  std::vector<std::unique_ptr<chre::fbs::MessageHubT>> hubs;
+  std::vector<std::unique_ptr<chre::fbs::EndpointInfoT>> endpoints;
+  GetMessageHubsAndEndpointsResponseT() {
+  }
+};
+
+struct GetMessageHubsAndEndpointsResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef GetMessageHubsAndEndpointsResponseT NativeTableType;
+  typedef GetMessageHubsAndEndpointsResponseBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_HUBS = 4,
+    VT_ENDPOINTS = 6
+  };
+  const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>> *hubs() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>> *>(VT_HUBS);
+  }
+  flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>> *mutable_hubs() {
+    return GetPointer<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>> *>(VT_HUBS);
+  }
+  const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>> *endpoints() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>> *>(VT_ENDPOINTS);
+  }
+  flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>> *mutable_endpoints() {
+    return GetPointer<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>> *>(VT_ENDPOINTS);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_HUBS) &&
+           verifier.VerifyVector(hubs()) &&
+           verifier.VerifyVectorOfTables(hubs()) &&
+           VerifyOffset(verifier, VT_ENDPOINTS) &&
+           verifier.VerifyVector(endpoints()) &&
+           verifier.VerifyVectorOfTables(endpoints()) &&
+           verifier.EndTable();
+  }
+  GetMessageHubsAndEndpointsResponseT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(GetMessageHubsAndEndpointsResponseT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> Pack(flatbuffers::FlatBufferBuilder &_fbb, const GetMessageHubsAndEndpointsResponseT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct GetMessageHubsAndEndpointsResponseBuilder {
+  typedef GetMessageHubsAndEndpointsResponse Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_hubs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>>> hubs) {
+    fbb_.AddOffset(GetMessageHubsAndEndpointsResponse::VT_HUBS, hubs);
+  }
+  void add_endpoints(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>>> endpoints) {
+    fbb_.AddOffset(GetMessageHubsAndEndpointsResponse::VT_ENDPOINTS, endpoints);
+  }
+  explicit GetMessageHubsAndEndpointsResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  GetMessageHubsAndEndpointsResponseBuilder &operator=(const GetMessageHubsAndEndpointsResponseBuilder &);
+  flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<GetMessageHubsAndEndpointsResponse>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> CreateGetMessageHubsAndEndpointsResponse(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>>> hubs = 0,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>>> endpoints = 0) {
+  GetMessageHubsAndEndpointsResponseBuilder builder_(_fbb);
+  builder_.add_endpoints(endpoints);
+  builder_.add_hubs(hubs);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> CreateGetMessageHubsAndEndpointsResponseDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    const std::vector<flatbuffers::Offset<chre::fbs::MessageHub>> *hubs = nullptr,
+    const std::vector<flatbuffers::Offset<chre::fbs::EndpointInfo>> *endpoints = nullptr) {
+  auto hubs__ = hubs ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::MessageHub>>(*hubs) : 0;
+  auto endpoints__ = endpoints ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::EndpointInfo>>(*endpoints) : 0;
+  return chre::fbs::CreateGetMessageHubsAndEndpointsResponse(
+      _fbb,
+      hubs__,
+      endpoints__);
+}
+
+flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> CreateGetMessageHubsAndEndpointsResponse(flatbuffers::FlatBufferBuilder &_fbb, const GetMessageHubsAndEndpointsResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct OpenEndpointSessionRequestT : public flatbuffers::NativeTable {
+  typedef OpenEndpointSessionRequest TableType;
+  uint16_t id;
+  std::unique_ptr<chre::fbs::EndpointIdT> fromEndpoint;
+  std::unique_ptr<chre::fbs::EndpointIdT> toEndpoint;
+  std::vector<int8_t> serviceDescriptor;
+  OpenEndpointSessionRequestT()
+      : id(0) {
+  }
+};
+
+struct OpenEndpointSessionRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef OpenEndpointSessionRequestT NativeTableType;
+  typedef OpenEndpointSessionRequestBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4,
+    VT_FROMENDPOINT = 6,
+    VT_TOENDPOINT = 8,
+    VT_SERVICEDESCRIPTOR = 10
+  };
+  uint16_t id() const {
+    return GetField<uint16_t>(VT_ID, 0);
+  }
+  bool mutate_id(uint16_t _id) {
+    return SetField<uint16_t>(VT_ID, _id, 0);
+  }
+  const chre::fbs::EndpointId *fromEndpoint() const {
+    return GetPointer<const chre::fbs::EndpointId *>(VT_FROMENDPOINT);
+  }
+  chre::fbs::EndpointId *mutable_fromEndpoint() {
+    return GetPointer<chre::fbs::EndpointId *>(VT_FROMENDPOINT);
+  }
+  const chre::fbs::EndpointId *toEndpoint() const {
+    return GetPointer<const chre::fbs::EndpointId *>(VT_TOENDPOINT);
+  }
+  chre::fbs::EndpointId *mutable_toEndpoint() {
+    return GetPointer<chre::fbs::EndpointId *>(VT_TOENDPOINT);
+  }
+  /// If present, describes the service definition used over the session
+  const flatbuffers::Vector<int8_t> *serviceDescriptor() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_SERVICEDESCRIPTOR);
+  }
+  flatbuffers::Vector<int8_t> *mutable_serviceDescriptor() {
+    return GetPointer<flatbuffers::Vector<int8_t> *>(VT_SERVICEDESCRIPTOR);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_ID) &&
+           VerifyOffset(verifier, VT_FROMENDPOINT) &&
+           verifier.VerifyTable(fromEndpoint()) &&
+           VerifyOffset(verifier, VT_TOENDPOINT) &&
+           verifier.VerifyTable(toEndpoint()) &&
+           VerifyOffset(verifier, VT_SERVICEDESCRIPTOR) &&
+           verifier.VerifyVector(serviceDescriptor()) &&
+           verifier.EndTable();
+  }
+  OpenEndpointSessionRequestT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(OpenEndpointSessionRequestT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<OpenEndpointSessionRequest> Pack(flatbuffers::FlatBufferBuilder &_fbb, const OpenEndpointSessionRequestT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct OpenEndpointSessionRequestBuilder {
+  typedef OpenEndpointSessionRequest Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(uint16_t id) {
+    fbb_.AddElement<uint16_t>(OpenEndpointSessionRequest::VT_ID, id, 0);
+  }
+  void add_fromEndpoint(flatbuffers::Offset<chre::fbs::EndpointId> fromEndpoint) {
+    fbb_.AddOffset(OpenEndpointSessionRequest::VT_FROMENDPOINT, fromEndpoint);
+  }
+  void add_toEndpoint(flatbuffers::Offset<chre::fbs::EndpointId> toEndpoint) {
+    fbb_.AddOffset(OpenEndpointSessionRequest::VT_TOENDPOINT, toEndpoint);
+  }
+  void add_serviceDescriptor(flatbuffers::Offset<flatbuffers::Vector<int8_t>> serviceDescriptor) {
+    fbb_.AddOffset(OpenEndpointSessionRequest::VT_SERVICEDESCRIPTOR, serviceDescriptor);
+  }
+  explicit OpenEndpointSessionRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  OpenEndpointSessionRequestBuilder &operator=(const OpenEndpointSessionRequestBuilder &);
+  flatbuffers::Offset<OpenEndpointSessionRequest> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<OpenEndpointSessionRequest>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<OpenEndpointSessionRequest> CreateOpenEndpointSessionRequest(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t id = 0,
+    flatbuffers::Offset<chre::fbs::EndpointId> fromEndpoint = 0,
+    flatbuffers::Offset<chre::fbs::EndpointId> toEndpoint = 0,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> serviceDescriptor = 0) {
+  OpenEndpointSessionRequestBuilder builder_(_fbb);
+  builder_.add_serviceDescriptor(serviceDescriptor);
+  builder_.add_toEndpoint(toEndpoint);
+  builder_.add_fromEndpoint(fromEndpoint);
+  builder_.add_id(id);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<OpenEndpointSessionRequest> CreateOpenEndpointSessionRequestDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t id = 0,
+    flatbuffers::Offset<chre::fbs::EndpointId> fromEndpoint = 0,
+    flatbuffers::Offset<chre::fbs::EndpointId> toEndpoint = 0,
+    const std::vector<int8_t> *serviceDescriptor = nullptr) {
+  auto serviceDescriptor__ = serviceDescriptor ? _fbb.CreateVector<int8_t>(*serviceDescriptor) : 0;
+  return chre::fbs::CreateOpenEndpointSessionRequest(
+      _fbb,
+      id,
+      fromEndpoint,
+      toEndpoint,
+      serviceDescriptor__);
+}
+
+flatbuffers::Offset<OpenEndpointSessionRequest> CreateOpenEndpointSessionRequest(flatbuffers::FlatBufferBuilder &_fbb, const OpenEndpointSessionRequestT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct EndpointSessionOpenedT : public flatbuffers::NativeTable {
+  typedef EndpointSessionOpened TableType;
+  uint16_t id;
+  EndpointSessionOpenedT()
+      : id(0) {
+  }
+};
+
+struct EndpointSessionOpened FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointSessionOpenedT NativeTableType;
+  typedef EndpointSessionOpenedBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4
+  };
+  uint16_t id() const {
+    return GetField<uint16_t>(VT_ID, 0);
+  }
+  bool mutate_id(uint16_t _id) {
+    return SetField<uint16_t>(VT_ID, _id, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_ID) &&
+           verifier.EndTable();
+  }
+  EndpointSessionOpenedT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(EndpointSessionOpenedT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<EndpointSessionOpened> Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionOpenedT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct EndpointSessionOpenedBuilder {
+  typedef EndpointSessionOpened Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(uint16_t id) {
+    fbb_.AddElement<uint16_t>(EndpointSessionOpened::VT_ID, id, 0);
+  }
+  explicit EndpointSessionOpenedBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointSessionOpenedBuilder &operator=(const EndpointSessionOpenedBuilder &);
+  flatbuffers::Offset<EndpointSessionOpened> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointSessionOpened>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointSessionOpened> CreateEndpointSessionOpened(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t id = 0) {
+  EndpointSessionOpenedBuilder builder_(_fbb);
+  builder_.add_id(id);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<EndpointSessionOpened> CreateEndpointSessionOpened(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionOpenedT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct EndpointSessionClosedT : public flatbuffers::NativeTable {
+  typedef EndpointSessionClosed TableType;
+  uint16_t id;
+  chre::fbs::Reason reason;
+  EndpointSessionClosedT()
+      : id(0),
+        reason(chre::fbs::Reason::UNSPECIFIED) {
+  }
+};
+
+struct EndpointSessionClosed FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointSessionClosedT NativeTableType;
+  typedef EndpointSessionClosedBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4,
+    VT_REASON = 6
+  };
+  uint16_t id() const {
+    return GetField<uint16_t>(VT_ID, 0);
+  }
+  bool mutate_id(uint16_t _id) {
+    return SetField<uint16_t>(VT_ID, _id, 0);
+  }
+  chre::fbs::Reason reason() const {
+    return static_cast<chre::fbs::Reason>(GetField<uint8_t>(VT_REASON, 0));
+  }
+  bool mutate_reason(chre::fbs::Reason _reason) {
+    return SetField<uint8_t>(VT_REASON, static_cast<uint8_t>(_reason), 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_ID) &&
+           VerifyField<uint8_t>(verifier, VT_REASON) &&
+           verifier.EndTable();
+  }
+  EndpointSessionClosedT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(EndpointSessionClosedT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<EndpointSessionClosed> Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionClosedT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct EndpointSessionClosedBuilder {
+  typedef EndpointSessionClosed Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(uint16_t id) {
+    fbb_.AddElement<uint16_t>(EndpointSessionClosed::VT_ID, id, 0);
+  }
+  void add_reason(chre::fbs::Reason reason) {
+    fbb_.AddElement<uint8_t>(EndpointSessionClosed::VT_REASON, static_cast<uint8_t>(reason), 0);
+  }
+  explicit EndpointSessionClosedBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointSessionClosedBuilder &operator=(const EndpointSessionClosedBuilder &);
+  flatbuffers::Offset<EndpointSessionClosed> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointSessionClosed>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointSessionClosed> CreateEndpointSessionClosed(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t id = 0,
+    chre::fbs::Reason reason = chre::fbs::Reason::UNSPECIFIED) {
+  EndpointSessionClosedBuilder builder_(_fbb);
+  builder_.add_id(id);
+  builder_.add_reason(reason);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<EndpointSessionClosed> CreateEndpointSessionClosed(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionClosedT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct EndpointSessionMessageT : public flatbuffers::NativeTable {
+  typedef EndpointSessionMessage TableType;
+  uint16_t session_id;
+  uint32_t type;
+  uint32_t permissions;
+  std::vector<uint8_t> data;
+  uint32_t flags;
+  uint32_t sequence_number;
+  EndpointSessionMessageT()
+      : session_id(0),
+        type(0),
+        permissions(0),
+        flags(0),
+        sequence_number(0) {
+  }
+};
+
+struct EndpointSessionMessage FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointSessionMessageT NativeTableType;
+  typedef EndpointSessionMessageBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SESSION_ID = 4,
+    VT_TYPE = 6,
+    VT_PERMISSIONS = 8,
+    VT_DATA = 10,
+    VT_FLAGS = 12,
+    VT_SEQUENCE_NUMBER = 14
+  };
+  /// Id of session this message is being sent within
+  uint16_t session_id() const {
+    return GetField<uint16_t>(VT_SESSION_ID, 0);
+  }
+  bool mutate_session_id(uint16_t _session_id) {
+    return SetField<uint16_t>(VT_SESSION_ID, _session_id, 0);
+  }
+  /// Type of the message, specific to the Session protocol
+  uint32_t type() const {
+    return GetField<uint32_t>(VT_TYPE, 0);
+  }
+  bool mutate_type(uint32_t _type) {
+    return SetField<uint32_t>(VT_TYPE, _type, 0);
+  }
+  /// Values from CHRE_MESSAGE_PERMISSION_*. Permissions required to read the
+  /// message.
+  uint32_t permissions() const {
+    return GetField<uint32_t>(VT_PERMISSIONS, 0);
+  }
+  bool mutate_permissions(uint32_t _permissions) {
+    return SetField<uint32_t>(VT_PERMISSIONS, _permissions, 0);
+  }
+  const flatbuffers::Vector<uint8_t> *data() const {
+    return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_DATA);
+  }
+  flatbuffers::Vector<uint8_t> *mutable_data() {
+    return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_DATA);
+  }
+  /// Bitmask of additional flags applied to the message:
+  /// - 0x1: Message delivery status required within 1s
+  uint32_t flags() const {
+    return GetField<uint32_t>(VT_FLAGS, 0);
+  }
+  bool mutate_flags(uint32_t _flags) {
+    return SetField<uint32_t>(VT_FLAGS, _flags, 0);
+  }
+  uint32_t sequence_number() const {
+    return GetField<uint32_t>(VT_SEQUENCE_NUMBER, 0);
+  }
+  bool mutate_sequence_number(uint32_t _sequence_number) {
+    return SetField<uint32_t>(VT_SEQUENCE_NUMBER, _sequence_number, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_SESSION_ID) &&
+           VerifyField<uint32_t>(verifier, VT_TYPE) &&
+           VerifyField<uint32_t>(verifier, VT_PERMISSIONS) &&
+           VerifyOffset(verifier, VT_DATA) &&
+           verifier.VerifyVector(data()) &&
+           VerifyField<uint32_t>(verifier, VT_FLAGS) &&
+           VerifyField<uint32_t>(verifier, VT_SEQUENCE_NUMBER) &&
+           verifier.EndTable();
+  }
+  EndpointSessionMessageT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(EndpointSessionMessageT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<EndpointSessionMessage> Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionMessageT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct EndpointSessionMessageBuilder {
+  typedef EndpointSessionMessage Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_session_id(uint16_t session_id) {
+    fbb_.AddElement<uint16_t>(EndpointSessionMessage::VT_SESSION_ID, session_id, 0);
+  }
+  void add_type(uint32_t type) {
+    fbb_.AddElement<uint32_t>(EndpointSessionMessage::VT_TYPE, type, 0);
+  }
+  void add_permissions(uint32_t permissions) {
+    fbb_.AddElement<uint32_t>(EndpointSessionMessage::VT_PERMISSIONS, permissions, 0);
+  }
+  void add_data(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data) {
+    fbb_.AddOffset(EndpointSessionMessage::VT_DATA, data);
+  }
+  void add_flags(uint32_t flags) {
+    fbb_.AddElement<uint32_t>(EndpointSessionMessage::VT_FLAGS, flags, 0);
+  }
+  void add_sequence_number(uint32_t sequence_number) {
+    fbb_.AddElement<uint32_t>(EndpointSessionMessage::VT_SEQUENCE_NUMBER, sequence_number, 0);
+  }
+  explicit EndpointSessionMessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointSessionMessageBuilder &operator=(const EndpointSessionMessageBuilder &);
+  flatbuffers::Offset<EndpointSessionMessage> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointSessionMessage>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointSessionMessage> CreateEndpointSessionMessage(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t session_id = 0,
+    uint32_t type = 0,
+    uint32_t permissions = 0,
+    flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data = 0,
+    uint32_t flags = 0,
+    uint32_t sequence_number = 0) {
+  EndpointSessionMessageBuilder builder_(_fbb);
+  builder_.add_sequence_number(sequence_number);
+  builder_.add_flags(flags);
+  builder_.add_data(data);
+  builder_.add_permissions(permissions);
+  builder_.add_type(type);
+  builder_.add_session_id(session_id);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<EndpointSessionMessage> CreateEndpointSessionMessageDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t session_id = 0,
+    uint32_t type = 0,
+    uint32_t permissions = 0,
+    const std::vector<uint8_t> *data = nullptr,
+    uint32_t flags = 0,
+    uint32_t sequence_number = 0) {
+  auto data__ = data ? _fbb.CreateVector<uint8_t>(*data) : 0;
+  return chre::fbs::CreateEndpointSessionMessage(
+      _fbb,
+      session_id,
+      type,
+      permissions,
+      data__,
+      flags,
+      sequence_number);
+}
+
+flatbuffers::Offset<EndpointSessionMessage> CreateEndpointSessionMessage(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionMessageT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+struct EndpointSessionMessageDeliveryStatusT : public flatbuffers::NativeTable {
+  typedef EndpointSessionMessageDeliveryStatus TableType;
+  uint16_t session_id;
+  std::unique_ptr<chre::fbs::MessageDeliveryStatusT> status;
+  EndpointSessionMessageDeliveryStatusT()
+      : session_id(0) {
+  }
+};
+
+struct EndpointSessionMessageDeliveryStatus FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointSessionMessageDeliveryStatusT NativeTableType;
+  typedef EndpointSessionMessageDeliveryStatusBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SESSION_ID = 4,
+    VT_STATUS = 6
+  };
+  /// Id of session the message was sent within
+  uint16_t session_id() const {
+    return GetField<uint16_t>(VT_SESSION_ID, 0);
+  }
+  bool mutate_session_id(uint16_t _session_id) {
+    return SetField<uint16_t>(VT_SESSION_ID, _session_id, 0);
+  }
+  const chre::fbs::MessageDeliveryStatus *status() const {
+    return GetPointer<const chre::fbs::MessageDeliveryStatus *>(VT_STATUS);
+  }
+  chre::fbs::MessageDeliveryStatus *mutable_status() {
+    return GetPointer<chre::fbs::MessageDeliveryStatus *>(VT_STATUS);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_SESSION_ID) &&
+           VerifyOffset(verifier, VT_STATUS) &&
+           verifier.VerifyTable(status()) &&
+           verifier.EndTable();
+  }
+  EndpointSessionMessageDeliveryStatusT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(EndpointSessionMessageDeliveryStatusT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<EndpointSessionMessageDeliveryStatus> Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionMessageDeliveryStatusT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct EndpointSessionMessageDeliveryStatusBuilder {
+  typedef EndpointSessionMessageDeliveryStatus Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_session_id(uint16_t session_id) {
+    fbb_.AddElement<uint16_t>(EndpointSessionMessageDeliveryStatus::VT_SESSION_ID, session_id, 0);
+  }
+  void add_status(flatbuffers::Offset<chre::fbs::MessageDeliveryStatus> status) {
+    fbb_.AddOffset(EndpointSessionMessageDeliveryStatus::VT_STATUS, status);
+  }
+  explicit EndpointSessionMessageDeliveryStatusBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointSessionMessageDeliveryStatusBuilder &operator=(const EndpointSessionMessageDeliveryStatusBuilder &);
+  flatbuffers::Offset<EndpointSessionMessageDeliveryStatus> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointSessionMessageDeliveryStatus>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointSessionMessageDeliveryStatus> CreateEndpointSessionMessageDeliveryStatus(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t session_id = 0,
+    flatbuffers::Offset<chre::fbs::MessageDeliveryStatus> status = 0) {
+  EndpointSessionMessageDeliveryStatusBuilder builder_(_fbb);
+  builder_.add_status(status);
+  builder_.add_session_id(session_id);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<EndpointSessionMessageDeliveryStatus> CreateEndpointSessionMessageDeliveryStatus(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionMessageDeliveryStatusT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
 struct MessageContainerT : public flatbuffers::NativeTable {
   typedef MessageContainer TableType;
   chre::fbs::ChreMessageUnion message;
@@ -4010,6 +6696,51 @@
   const chre::fbs::MessageDeliveryStatus *message_as_MessageDeliveryStatus() const {
     return message_type() == chre::fbs::ChreMessage::MessageDeliveryStatus ? static_cast<const chre::fbs::MessageDeliveryStatus *>(message()) : nullptr;
   }
+  const chre::fbs::BtSocketOpen *message_as_BtSocketOpen() const {
+    return message_type() == chre::fbs::ChreMessage::BtSocketOpen ? static_cast<const chre::fbs::BtSocketOpen *>(message()) : nullptr;
+  }
+  const chre::fbs::BtSocketOpenResponse *message_as_BtSocketOpenResponse() const {
+    return message_type() == chre::fbs::ChreMessage::BtSocketOpenResponse ? static_cast<const chre::fbs::BtSocketOpenResponse *>(message()) : nullptr;
+  }
+  const chre::fbs::BtSocketClose *message_as_BtSocketClose() const {
+    return message_type() == chre::fbs::ChreMessage::BtSocketClose ? static_cast<const chre::fbs::BtSocketClose *>(message()) : nullptr;
+  }
+  const chre::fbs::BtSocketCloseResponse *message_as_BtSocketCloseResponse() const {
+    return message_type() == chre::fbs::ChreMessage::BtSocketCloseResponse ? static_cast<const chre::fbs::BtSocketCloseResponse *>(message()) : nullptr;
+  }
+  const chre::fbs::GetMessageHubsAndEndpointsRequest *message_as_GetMessageHubsAndEndpointsRequest() const {
+    return message_type() == chre::fbs::ChreMessage::GetMessageHubsAndEndpointsRequest ? static_cast<const chre::fbs::GetMessageHubsAndEndpointsRequest *>(message()) : nullptr;
+  }
+  const chre::fbs::GetMessageHubsAndEndpointsResponse *message_as_GetMessageHubsAndEndpointsResponse() const {
+    return message_type() == chre::fbs::ChreMessage::GetMessageHubsAndEndpointsResponse ? static_cast<const chre::fbs::GetMessageHubsAndEndpointsResponse *>(message()) : nullptr;
+  }
+  const chre::fbs::RegisterMessageHub *message_as_RegisterMessageHub() const {
+    return message_type() == chre::fbs::ChreMessage::RegisterMessageHub ? static_cast<const chre::fbs::RegisterMessageHub *>(message()) : nullptr;
+  }
+  const chre::fbs::UnregisterMessageHub *message_as_UnregisterMessageHub() const {
+    return message_type() == chre::fbs::ChreMessage::UnregisterMessageHub ? static_cast<const chre::fbs::UnregisterMessageHub *>(message()) : nullptr;
+  }
+  const chre::fbs::RegisterEndpoint *message_as_RegisterEndpoint() const {
+    return message_type() == chre::fbs::ChreMessage::RegisterEndpoint ? static_cast<const chre::fbs::RegisterEndpoint *>(message()) : nullptr;
+  }
+  const chre::fbs::UnregisterEndpoint *message_as_UnregisterEndpoint() const {
+    return message_type() == chre::fbs::ChreMessage::UnregisterEndpoint ? static_cast<const chre::fbs::UnregisterEndpoint *>(message()) : nullptr;
+  }
+  const chre::fbs::OpenEndpointSessionRequest *message_as_OpenEndpointSessionRequest() const {
+    return message_type() == chre::fbs::ChreMessage::OpenEndpointSessionRequest ? static_cast<const chre::fbs::OpenEndpointSessionRequest *>(message()) : nullptr;
+  }
+  const chre::fbs::EndpointSessionOpened *message_as_EndpointSessionOpened() const {
+    return message_type() == chre::fbs::ChreMessage::EndpointSessionOpened ? static_cast<const chre::fbs::EndpointSessionOpened *>(message()) : nullptr;
+  }
+  const chre::fbs::EndpointSessionClosed *message_as_EndpointSessionClosed() const {
+    return message_type() == chre::fbs::ChreMessage::EndpointSessionClosed ? static_cast<const chre::fbs::EndpointSessionClosed *>(message()) : nullptr;
+  }
+  const chre::fbs::EndpointSessionMessage *message_as_EndpointSessionMessage() const {
+    return message_type() == chre::fbs::ChreMessage::EndpointSessionMessage ? static_cast<const chre::fbs::EndpointSessionMessage *>(message()) : nullptr;
+  }
+  const chre::fbs::EndpointSessionMessageDeliveryStatus *message_as_EndpointSessionMessageDeliveryStatus() const {
+    return message_type() == chre::fbs::ChreMessage::EndpointSessionMessageDeliveryStatus ? static_cast<const chre::fbs::EndpointSessionMessageDeliveryStatus *>(message()) : nullptr;
+  }
   void *mutable_message() {
     return GetPointer<void *>(VT_MESSAGE);
   }
@@ -4166,6 +6897,66 @@
   return message_as_MessageDeliveryStatus();
 }
 
+template<> inline const chre::fbs::BtSocketOpen *MessageContainer::message_as<chre::fbs::BtSocketOpen>() const {
+  return message_as_BtSocketOpen();
+}
+
+template<> inline const chre::fbs::BtSocketOpenResponse *MessageContainer::message_as<chre::fbs::BtSocketOpenResponse>() const {
+  return message_as_BtSocketOpenResponse();
+}
+
+template<> inline const chre::fbs::BtSocketClose *MessageContainer::message_as<chre::fbs::BtSocketClose>() const {
+  return message_as_BtSocketClose();
+}
+
+template<> inline const chre::fbs::BtSocketCloseResponse *MessageContainer::message_as<chre::fbs::BtSocketCloseResponse>() const {
+  return message_as_BtSocketCloseResponse();
+}
+
+template<> inline const chre::fbs::GetMessageHubsAndEndpointsRequest *MessageContainer::message_as<chre::fbs::GetMessageHubsAndEndpointsRequest>() const {
+  return message_as_GetMessageHubsAndEndpointsRequest();
+}
+
+template<> inline const chre::fbs::GetMessageHubsAndEndpointsResponse *MessageContainer::message_as<chre::fbs::GetMessageHubsAndEndpointsResponse>() const {
+  return message_as_GetMessageHubsAndEndpointsResponse();
+}
+
+template<> inline const chre::fbs::RegisterMessageHub *MessageContainer::message_as<chre::fbs::RegisterMessageHub>() const {
+  return message_as_RegisterMessageHub();
+}
+
+template<> inline const chre::fbs::UnregisterMessageHub *MessageContainer::message_as<chre::fbs::UnregisterMessageHub>() const {
+  return message_as_UnregisterMessageHub();
+}
+
+template<> inline const chre::fbs::RegisterEndpoint *MessageContainer::message_as<chre::fbs::RegisterEndpoint>() const {
+  return message_as_RegisterEndpoint();
+}
+
+template<> inline const chre::fbs::UnregisterEndpoint *MessageContainer::message_as<chre::fbs::UnregisterEndpoint>() const {
+  return message_as_UnregisterEndpoint();
+}
+
+template<> inline const chre::fbs::OpenEndpointSessionRequest *MessageContainer::message_as<chre::fbs::OpenEndpointSessionRequest>() const {
+  return message_as_OpenEndpointSessionRequest();
+}
+
+template<> inline const chre::fbs::EndpointSessionOpened *MessageContainer::message_as<chre::fbs::EndpointSessionOpened>() const {
+  return message_as_EndpointSessionOpened();
+}
+
+template<> inline const chre::fbs::EndpointSessionClosed *MessageContainer::message_as<chre::fbs::EndpointSessionClosed>() const {
+  return message_as_EndpointSessionClosed();
+}
+
+template<> inline const chre::fbs::EndpointSessionMessage *MessageContainer::message_as<chre::fbs::EndpointSessionMessage>() const {
+  return message_as_EndpointSessionMessage();
+}
+
+template<> inline const chre::fbs::EndpointSessionMessageDeliveryStatus *MessageContainer::message_as<chre::fbs::EndpointSessionMessageDeliveryStatus>() const {
+  return message_as_EndpointSessionMessageDeliveryStatus();
+}
+
 struct MessageContainerBuilder {
   typedef MessageContainer Table;
   flatbuffers::FlatBufferBuilder &fbb_;
@@ -5217,6 +8008,672 @@
       _fbb);
 }
 
+inline LeCocChannelInfoT *LeCocChannelInfo::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::LeCocChannelInfoT> _o = std::unique_ptr<chre::fbs::LeCocChannelInfoT>(new LeCocChannelInfoT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void LeCocChannelInfo::UnPackTo(LeCocChannelInfoT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = localCid(); _o->localCid = _e; }
+  { auto _e = remoteCid(); _o->remoteCid = _e; }
+  { auto _e = psm(); _o->psm = _e; }
+  { auto _e = localMtu(); _o->localMtu = _e; }
+  { auto _e = remoteMtu(); _o->remoteMtu = _e; }
+  { auto _e = localMps(); _o->localMps = _e; }
+  { auto _e = remoteMps(); _o->remoteMps = _e; }
+  { auto _e = initialRxCredits(); _o->initialRxCredits = _e; }
+  { auto _e = initialTxCredits(); _o->initialTxCredits = _e; }
+}
+
+inline flatbuffers::Offset<LeCocChannelInfo> LeCocChannelInfo::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LeCocChannelInfoT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateLeCocChannelInfo(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<LeCocChannelInfo> CreateLeCocChannelInfo(flatbuffers::FlatBufferBuilder &_fbb, const LeCocChannelInfoT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const LeCocChannelInfoT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _localCid = _o->localCid;
+  auto _remoteCid = _o->remoteCid;
+  auto _psm = _o->psm;
+  auto _localMtu = _o->localMtu;
+  auto _remoteMtu = _o->remoteMtu;
+  auto _localMps = _o->localMps;
+  auto _remoteMps = _o->remoteMps;
+  auto _initialRxCredits = _o->initialRxCredits;
+  auto _initialTxCredits = _o->initialTxCredits;
+  return chre::fbs::CreateLeCocChannelInfo(
+      _fbb,
+      _localCid,
+      _remoteCid,
+      _psm,
+      _localMtu,
+      _remoteMtu,
+      _localMps,
+      _remoteMps,
+      _initialRxCredits,
+      _initialTxCredits);
+}
+
+inline BtSocketOpenT *BtSocketOpen::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::BtSocketOpenT> _o = std::unique_ptr<chre::fbs::BtSocketOpenT>(new BtSocketOpenT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void BtSocketOpen::UnPackTo(BtSocketOpenT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = socketId(); _o->socketId = _e; }
+  { auto _e = name(); if (_e) { _o->name.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->name[_i] = _e->Get(_i); } } }
+  { auto _e = aclConnectionHandle(); _o->aclConnectionHandle = _e; }
+  { auto _e = channelInfo_type(); _o->channelInfo.type = _e; }
+  { auto _e = channelInfo(); if (_e) _o->channelInfo.value = chre::fbs::ChannelInfoUnion::UnPack(_e, channelInfo_type(), _resolver); }
+  { auto _e = hubId(); _o->hubId = _e; }
+  { auto _e = endpointId(); _o->endpointId = _e; }
+}
+
+inline flatbuffers::Offset<BtSocketOpen> BtSocketOpen::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketOpenT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateBtSocketOpen(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<BtSocketOpen> CreateBtSocketOpen(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketOpenT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const BtSocketOpenT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _socketId = _o->socketId;
+  auto _name = _o->name.size() ? _fbb.CreateVector(_o->name) : 0;
+  auto _aclConnectionHandle = _o->aclConnectionHandle;
+  auto _channelInfo_type = _o->channelInfo.type;
+  auto _channelInfo = _o->channelInfo.Pack(_fbb);
+  auto _hubId = _o->hubId;
+  auto _endpointId = _o->endpointId;
+  return chre::fbs::CreateBtSocketOpen(
+      _fbb,
+      _socketId,
+      _name,
+      _aclConnectionHandle,
+      _channelInfo_type,
+      _channelInfo,
+      _hubId,
+      _endpointId);
+}
+
+inline BtSocketOpenResponseT *BtSocketOpenResponse::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::BtSocketOpenResponseT> _o = std::unique_ptr<chre::fbs::BtSocketOpenResponseT>(new BtSocketOpenResponseT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void BtSocketOpenResponse::UnPackTo(BtSocketOpenResponseT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = socketId(); _o->socketId = _e; }
+  { auto _e = status(); _o->status = _e; }
+  { auto _e = reason(); if (_e) { _o->reason.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->reason[_i] = _e->Get(_i); } } }
+}
+
+inline flatbuffers::Offset<BtSocketOpenResponse> BtSocketOpenResponse::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketOpenResponseT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateBtSocketOpenResponse(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<BtSocketOpenResponse> CreateBtSocketOpenResponse(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketOpenResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const BtSocketOpenResponseT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _socketId = _o->socketId;
+  auto _status = _o->status;
+  auto _reason = _o->reason.size() ? _fbb.CreateVector(_o->reason) : 0;
+  return chre::fbs::CreateBtSocketOpenResponse(
+      _fbb,
+      _socketId,
+      _status,
+      _reason);
+}
+
+inline BtSocketCloseT *BtSocketClose::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::BtSocketCloseT> _o = std::unique_ptr<chre::fbs::BtSocketCloseT>(new BtSocketCloseT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void BtSocketClose::UnPackTo(BtSocketCloseT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = socketId(); _o->socketId = _e; }
+  { auto _e = reason(); if (_e) { _o->reason.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->reason[_i] = _e->Get(_i); } } }
+}
+
+inline flatbuffers::Offset<BtSocketClose> BtSocketClose::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketCloseT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateBtSocketClose(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<BtSocketClose> CreateBtSocketClose(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketCloseT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const BtSocketCloseT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _socketId = _o->socketId;
+  auto _reason = _o->reason.size() ? _fbb.CreateVector(_o->reason) : 0;
+  return chre::fbs::CreateBtSocketClose(
+      _fbb,
+      _socketId,
+      _reason);
+}
+
+inline BtSocketCloseResponseT *BtSocketCloseResponse::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::BtSocketCloseResponseT> _o = std::unique_ptr<chre::fbs::BtSocketCloseResponseT>(new BtSocketCloseResponseT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void BtSocketCloseResponse::UnPackTo(BtSocketCloseResponseT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = socketId(); _o->socketId = _e; }
+}
+
+inline flatbuffers::Offset<BtSocketCloseResponse> BtSocketCloseResponse::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketCloseResponseT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateBtSocketCloseResponse(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<BtSocketCloseResponse> CreateBtSocketCloseResponse(flatbuffers::FlatBufferBuilder &_fbb, const BtSocketCloseResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const BtSocketCloseResponseT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _socketId = _o->socketId;
+  return chre::fbs::CreateBtSocketCloseResponse(
+      _fbb,
+      _socketId);
+}
+
+inline VendorHubInfoT *VendorHubInfo::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::VendorHubInfoT> _o = std::unique_ptr<chre::fbs::VendorHubInfoT>(new VendorHubInfoT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void VendorHubInfo::UnPackTo(VendorHubInfoT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = name(); if (_e) { _o->name.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->name[_i] = _e->Get(_i); } } }
+  { auto _e = version(); _o->version = _e; }
+  { auto _e = extended_info(); if (_e) { _o->extended_info.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->extended_info[_i] = _e->Get(_i); } } }
+}
+
+inline flatbuffers::Offset<VendorHubInfo> VendorHubInfo::Pack(flatbuffers::FlatBufferBuilder &_fbb, const VendorHubInfoT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateVendorHubInfo(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<VendorHubInfo> CreateVendorHubInfo(flatbuffers::FlatBufferBuilder &_fbb, const VendorHubInfoT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const VendorHubInfoT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _name = _o->name.size() ? _fbb.CreateVector(_o->name) : 0;
+  auto _version = _o->version;
+  auto _extended_info = _o->extended_info.size() ? _fbb.CreateVector(_o->extended_info) : 0;
+  return chre::fbs::CreateVendorHubInfo(
+      _fbb,
+      _name,
+      _version,
+      _extended_info);
+}
+
+inline MessageHubT *MessageHub::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::MessageHubT> _o = std::unique_ptr<chre::fbs::MessageHubT>(new MessageHubT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void MessageHub::UnPackTo(MessageHubT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = id(); _o->id = _e; }
+  { auto _e = details_type(); _o->details.type = _e; }
+  { auto _e = details(); if (_e) _o->details.value = chre::fbs::MessageHubDetailsUnion::UnPack(_e, details_type(), _resolver); }
+}
+
+inline flatbuffers::Offset<MessageHub> MessageHub::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MessageHubT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateMessageHub(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<MessageHub> CreateMessageHub(flatbuffers::FlatBufferBuilder &_fbb, const MessageHubT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const MessageHubT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _id = _o->id;
+  auto _details_type = _o->details.type;
+  auto _details = _o->details.Pack(_fbb);
+  return chre::fbs::CreateMessageHub(
+      _fbb,
+      _id,
+      _details_type,
+      _details);
+}
+
+inline RegisterMessageHubT *RegisterMessageHub::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::RegisterMessageHubT> _o = std::unique_ptr<chre::fbs::RegisterMessageHubT>(new RegisterMessageHubT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void RegisterMessageHub::UnPackTo(RegisterMessageHubT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = hub(); if (_e) _o->hub = std::unique_ptr<chre::fbs::MessageHubT>(_e->UnPack(_resolver)); }
+}
+
+inline flatbuffers::Offset<RegisterMessageHub> RegisterMessageHub::Pack(flatbuffers::FlatBufferBuilder &_fbb, const RegisterMessageHubT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateRegisterMessageHub(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<RegisterMessageHub> CreateRegisterMessageHub(flatbuffers::FlatBufferBuilder &_fbb, const RegisterMessageHubT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const RegisterMessageHubT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _hub = _o->hub ? CreateMessageHub(_fbb, _o->hub.get(), _rehasher) : 0;
+  return chre::fbs::CreateRegisterMessageHub(
+      _fbb,
+      _hub);
+}
+
+inline UnregisterMessageHubT *UnregisterMessageHub::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::UnregisterMessageHubT> _o = std::unique_ptr<chre::fbs::UnregisterMessageHubT>(new UnregisterMessageHubT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void UnregisterMessageHub::UnPackTo(UnregisterMessageHubT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = id(); _o->id = _e; }
+}
+
+inline flatbuffers::Offset<UnregisterMessageHub> UnregisterMessageHub::Pack(flatbuffers::FlatBufferBuilder &_fbb, const UnregisterMessageHubT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateUnregisterMessageHub(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<UnregisterMessageHub> CreateUnregisterMessageHub(flatbuffers::FlatBufferBuilder &_fbb, const UnregisterMessageHubT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const UnregisterMessageHubT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _id = _o->id;
+  return chre::fbs::CreateUnregisterMessageHub(
+      _fbb,
+      _id);
+}
+
+inline EndpointIdT *EndpointId::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::EndpointIdT> _o = std::unique_ptr<chre::fbs::EndpointIdT>(new EndpointIdT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void EndpointId::UnPackTo(EndpointIdT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = hubId(); _o->hubId = _e; }
+  { auto _e = id(); _o->id = _e; }
+}
+
+inline flatbuffers::Offset<EndpointId> EndpointId::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointIdT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateEndpointId(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<EndpointId> CreateEndpointId(flatbuffers::FlatBufferBuilder &_fbb, const EndpointIdT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const EndpointIdT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _hubId = _o->hubId;
+  auto _id = _o->id;
+  return chre::fbs::CreateEndpointId(
+      _fbb,
+      _hubId,
+      _id);
+}
+
+inline ServiceT *Service::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::ServiceT> _o = std::unique_ptr<chre::fbs::ServiceT>(new ServiceT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void Service::UnPackTo(ServiceT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = format(); _o->format = _e; }
+  { auto _e = descriptor(); if (_e) { _o->descriptor.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->descriptor[_i] = _e->Get(_i); } } }
+  { auto _e = major_version(); _o->major_version = _e; }
+  { auto _e = minor_version(); _o->minor_version = _e; }
+}
+
+inline flatbuffers::Offset<Service> Service::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ServiceT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateService(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<Service> CreateService(flatbuffers::FlatBufferBuilder &_fbb, const ServiceT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const ServiceT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _format = _o->format;
+  auto _descriptor = _o->descriptor.size() ? _fbb.CreateVector(_o->descriptor) : 0;
+  auto _major_version = _o->major_version;
+  auto _minor_version = _o->minor_version;
+  return chre::fbs::CreateService(
+      _fbb,
+      _format,
+      _descriptor,
+      _major_version,
+      _minor_version);
+}
+
+inline EndpointInfoT *EndpointInfo::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::EndpointInfoT> _o = std::unique_ptr<chre::fbs::EndpointInfoT>(new EndpointInfoT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void EndpointInfo::UnPackTo(EndpointInfoT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = id(); if (_e) _o->id = std::unique_ptr<chre::fbs::EndpointIdT>(_e->UnPack(_resolver)); }
+  { auto _e = type(); _o->type = _e; }
+  { auto _e = name(); if (_e) { _o->name.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->name[_i] = _e->Get(_i); } } }
+  { auto _e = version(); _o->version = _e; }
+  { auto _e = required_permissions(); _o->required_permissions = _e; }
+  { auto _e = services(); if (_e) { _o->services.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->services[_i] = std::unique_ptr<chre::fbs::ServiceT>(_e->Get(_i)->UnPack(_resolver)); } } }
+}
+
+inline flatbuffers::Offset<EndpointInfo> EndpointInfo::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointInfoT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateEndpointInfo(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<EndpointInfo> CreateEndpointInfo(flatbuffers::FlatBufferBuilder &_fbb, const EndpointInfoT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const EndpointInfoT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _id = _o->id ? CreateEndpointId(_fbb, _o->id.get(), _rehasher) : 0;
+  auto _type = _o->type;
+  auto _name = _o->name.size() ? _fbb.CreateVector(_o->name) : 0;
+  auto _version = _o->version;
+  auto _required_permissions = _o->required_permissions;
+  auto _services = _o->services.size() ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::Service>> (_o->services.size(), [](size_t i, _VectorArgs *__va) { return CreateService(*__va->__fbb, __va->__o->services[i].get(), __va->__rehasher); }, &_va ) : 0;
+  return chre::fbs::CreateEndpointInfo(
+      _fbb,
+      _id,
+      _type,
+      _name,
+      _version,
+      _required_permissions,
+      _services);
+}
+
+inline RegisterEndpointT *RegisterEndpoint::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::RegisterEndpointT> _o = std::unique_ptr<chre::fbs::RegisterEndpointT>(new RegisterEndpointT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void RegisterEndpoint::UnPackTo(RegisterEndpointT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = endpoint(); if (_e) _o->endpoint = std::unique_ptr<chre::fbs::EndpointInfoT>(_e->UnPack(_resolver)); }
+}
+
+inline flatbuffers::Offset<RegisterEndpoint> RegisterEndpoint::Pack(flatbuffers::FlatBufferBuilder &_fbb, const RegisterEndpointT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateRegisterEndpoint(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<RegisterEndpoint> CreateRegisterEndpoint(flatbuffers::FlatBufferBuilder &_fbb, const RegisterEndpointT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const RegisterEndpointT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _endpoint = _o->endpoint ? CreateEndpointInfo(_fbb, _o->endpoint.get(), _rehasher) : 0;
+  return chre::fbs::CreateRegisterEndpoint(
+      _fbb,
+      _endpoint);
+}
+
+inline UnregisterEndpointT *UnregisterEndpoint::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::UnregisterEndpointT> _o = std::unique_ptr<chre::fbs::UnregisterEndpointT>(new UnregisterEndpointT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void UnregisterEndpoint::UnPackTo(UnregisterEndpointT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = endpoint(); if (_e) _o->endpoint = std::unique_ptr<chre::fbs::EndpointIdT>(_e->UnPack(_resolver)); }
+}
+
+inline flatbuffers::Offset<UnregisterEndpoint> UnregisterEndpoint::Pack(flatbuffers::FlatBufferBuilder &_fbb, const UnregisterEndpointT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateUnregisterEndpoint(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<UnregisterEndpoint> CreateUnregisterEndpoint(flatbuffers::FlatBufferBuilder &_fbb, const UnregisterEndpointT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const UnregisterEndpointT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _endpoint = _o->endpoint ? CreateEndpointId(_fbb, _o->endpoint.get(), _rehasher) : 0;
+  return chre::fbs::CreateUnregisterEndpoint(
+      _fbb,
+      _endpoint);
+}
+
+inline GetMessageHubsAndEndpointsRequestT *GetMessageHubsAndEndpointsRequest::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::GetMessageHubsAndEndpointsRequestT> _o = std::unique_ptr<chre::fbs::GetMessageHubsAndEndpointsRequestT>(new GetMessageHubsAndEndpointsRequestT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void GetMessageHubsAndEndpointsRequest::UnPackTo(GetMessageHubsAndEndpointsRequestT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+}
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsRequest> GetMessageHubsAndEndpointsRequest::Pack(flatbuffers::FlatBufferBuilder &_fbb, const GetMessageHubsAndEndpointsRequestT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateGetMessageHubsAndEndpointsRequest(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsRequest> CreateGetMessageHubsAndEndpointsRequest(flatbuffers::FlatBufferBuilder &_fbb, const GetMessageHubsAndEndpointsRequestT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const GetMessageHubsAndEndpointsRequestT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  return chre::fbs::CreateGetMessageHubsAndEndpointsRequest(
+      _fbb);
+}
+
+inline GetMessageHubsAndEndpointsResponseT *GetMessageHubsAndEndpointsResponse::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::GetMessageHubsAndEndpointsResponseT> _o = std::unique_ptr<chre::fbs::GetMessageHubsAndEndpointsResponseT>(new GetMessageHubsAndEndpointsResponseT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void GetMessageHubsAndEndpointsResponse::UnPackTo(GetMessageHubsAndEndpointsResponseT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = hubs(); if (_e) { _o->hubs.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->hubs[_i] = std::unique_ptr<chre::fbs::MessageHubT>(_e->Get(_i)->UnPack(_resolver)); } } }
+  { auto _e = endpoints(); if (_e) { _o->endpoints.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->endpoints[_i] = std::unique_ptr<chre::fbs::EndpointInfoT>(_e->Get(_i)->UnPack(_resolver)); } } }
+}
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> GetMessageHubsAndEndpointsResponse::Pack(flatbuffers::FlatBufferBuilder &_fbb, const GetMessageHubsAndEndpointsResponseT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateGetMessageHubsAndEndpointsResponse(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> CreateGetMessageHubsAndEndpointsResponse(flatbuffers::FlatBufferBuilder &_fbb, const GetMessageHubsAndEndpointsResponseT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const GetMessageHubsAndEndpointsResponseT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _hubs = _o->hubs.size() ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::MessageHub>> (_o->hubs.size(), [](size_t i, _VectorArgs *__va) { return CreateMessageHub(*__va->__fbb, __va->__o->hubs[i].get(), __va->__rehasher); }, &_va ) : 0;
+  auto _endpoints = _o->endpoints.size() ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::EndpointInfo>> (_o->endpoints.size(), [](size_t i, _VectorArgs *__va) { return CreateEndpointInfo(*__va->__fbb, __va->__o->endpoints[i].get(), __va->__rehasher); }, &_va ) : 0;
+  return chre::fbs::CreateGetMessageHubsAndEndpointsResponse(
+      _fbb,
+      _hubs,
+      _endpoints);
+}
+
+inline OpenEndpointSessionRequestT *OpenEndpointSessionRequest::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::OpenEndpointSessionRequestT> _o = std::unique_ptr<chre::fbs::OpenEndpointSessionRequestT>(new OpenEndpointSessionRequestT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void OpenEndpointSessionRequest::UnPackTo(OpenEndpointSessionRequestT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = id(); _o->id = _e; }
+  { auto _e = fromEndpoint(); if (_e) _o->fromEndpoint = std::unique_ptr<chre::fbs::EndpointIdT>(_e->UnPack(_resolver)); }
+  { auto _e = toEndpoint(); if (_e) _o->toEndpoint = std::unique_ptr<chre::fbs::EndpointIdT>(_e->UnPack(_resolver)); }
+  { auto _e = serviceDescriptor(); if (_e) { _o->serviceDescriptor.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->serviceDescriptor[_i] = _e->Get(_i); } } }
+}
+
+inline flatbuffers::Offset<OpenEndpointSessionRequest> OpenEndpointSessionRequest::Pack(flatbuffers::FlatBufferBuilder &_fbb, const OpenEndpointSessionRequestT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateOpenEndpointSessionRequest(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<OpenEndpointSessionRequest> CreateOpenEndpointSessionRequest(flatbuffers::FlatBufferBuilder &_fbb, const OpenEndpointSessionRequestT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const OpenEndpointSessionRequestT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _id = _o->id;
+  auto _fromEndpoint = _o->fromEndpoint ? CreateEndpointId(_fbb, _o->fromEndpoint.get(), _rehasher) : 0;
+  auto _toEndpoint = _o->toEndpoint ? CreateEndpointId(_fbb, _o->toEndpoint.get(), _rehasher) : 0;
+  auto _serviceDescriptor = _o->serviceDescriptor.size() ? _fbb.CreateVector(_o->serviceDescriptor) : 0;
+  return chre::fbs::CreateOpenEndpointSessionRequest(
+      _fbb,
+      _id,
+      _fromEndpoint,
+      _toEndpoint,
+      _serviceDescriptor);
+}
+
+inline EndpointSessionOpenedT *EndpointSessionOpened::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::EndpointSessionOpenedT> _o = std::unique_ptr<chre::fbs::EndpointSessionOpenedT>(new EndpointSessionOpenedT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void EndpointSessionOpened::UnPackTo(EndpointSessionOpenedT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = id(); _o->id = _e; }
+}
+
+inline flatbuffers::Offset<EndpointSessionOpened> EndpointSessionOpened::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionOpenedT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateEndpointSessionOpened(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<EndpointSessionOpened> CreateEndpointSessionOpened(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionOpenedT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const EndpointSessionOpenedT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _id = _o->id;
+  return chre::fbs::CreateEndpointSessionOpened(
+      _fbb,
+      _id);
+}
+
+inline EndpointSessionClosedT *EndpointSessionClosed::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::EndpointSessionClosedT> _o = std::unique_ptr<chre::fbs::EndpointSessionClosedT>(new EndpointSessionClosedT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void EndpointSessionClosed::UnPackTo(EndpointSessionClosedT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = id(); _o->id = _e; }
+  { auto _e = reason(); _o->reason = _e; }
+}
+
+inline flatbuffers::Offset<EndpointSessionClosed> EndpointSessionClosed::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionClosedT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateEndpointSessionClosed(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<EndpointSessionClosed> CreateEndpointSessionClosed(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionClosedT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const EndpointSessionClosedT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _id = _o->id;
+  auto _reason = _o->reason;
+  return chre::fbs::CreateEndpointSessionClosed(
+      _fbb,
+      _id,
+      _reason);
+}
+
+inline EndpointSessionMessageT *EndpointSessionMessage::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::EndpointSessionMessageT> _o = std::unique_ptr<chre::fbs::EndpointSessionMessageT>(new EndpointSessionMessageT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void EndpointSessionMessage::UnPackTo(EndpointSessionMessageT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = session_id(); _o->session_id = _e; }
+  { auto _e = type(); _o->type = _e; }
+  { auto _e = permissions(); _o->permissions = _e; }
+  { auto _e = data(); if (_e) { _o->data.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->data[_i] = _e->Get(_i); } } }
+  { auto _e = flags(); _o->flags = _e; }
+  { auto _e = sequence_number(); _o->sequence_number = _e; }
+}
+
+inline flatbuffers::Offset<EndpointSessionMessage> EndpointSessionMessage::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionMessageT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateEndpointSessionMessage(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<EndpointSessionMessage> CreateEndpointSessionMessage(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionMessageT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const EndpointSessionMessageT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _session_id = _o->session_id;
+  auto _type = _o->type;
+  auto _permissions = _o->permissions;
+  auto _data = _o->data.size() ? _fbb.CreateVector(_o->data) : 0;
+  auto _flags = _o->flags;
+  auto _sequence_number = _o->sequence_number;
+  return chre::fbs::CreateEndpointSessionMessage(
+      _fbb,
+      _session_id,
+      _type,
+      _permissions,
+      _data,
+      _flags,
+      _sequence_number);
+}
+
+inline EndpointSessionMessageDeliveryStatusT *EndpointSessionMessageDeliveryStatus::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  std::unique_ptr<chre::fbs::EndpointSessionMessageDeliveryStatusT> _o = std::unique_ptr<chre::fbs::EndpointSessionMessageDeliveryStatusT>(new EndpointSessionMessageDeliveryStatusT());
+  UnPackTo(_o.get(), _resolver);
+  return _o.release();
+}
+
+inline void EndpointSessionMessageDeliveryStatus::UnPackTo(EndpointSessionMessageDeliveryStatusT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = session_id(); _o->session_id = _e; }
+  { auto _e = status(); if (_e) _o->status = std::unique_ptr<chre::fbs::MessageDeliveryStatusT>(_e->UnPack(_resolver)); }
+}
+
+inline flatbuffers::Offset<EndpointSessionMessageDeliveryStatus> EndpointSessionMessageDeliveryStatus::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionMessageDeliveryStatusT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateEndpointSessionMessageDeliveryStatus(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<EndpointSessionMessageDeliveryStatus> CreateEndpointSessionMessageDeliveryStatus(flatbuffers::FlatBufferBuilder &_fbb, const EndpointSessionMessageDeliveryStatusT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const EndpointSessionMessageDeliveryStatusT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _session_id = _o->session_id;
+  auto _status = _o->status ? CreateMessageDeliveryStatus(_fbb, _o->status.get(), _rehasher) : 0;
+  return chre::fbs::CreateEndpointSessionMessageDeliveryStatus(
+      _fbb,
+      _session_id,
+      _status);
+}
+
 inline MessageContainerT *MessageContainer::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
   std::unique_ptr<chre::fbs::MessageContainerT> _o = std::unique_ptr<chre::fbs::MessageContainerT>(new MessageContainerT());
   UnPackTo(_o.get(), _resolver);
@@ -5249,6 +8706,165 @@
       _host_addr);
 }
 
+inline bool VerifyChannelInfo(flatbuffers::Verifier &verifier, const void *obj, ChannelInfo type) {
+  switch (type) {
+    case ChannelInfo::NONE: {
+      return true;
+    }
+    case ChannelInfo::LeCocChannelInfo: {
+      auto ptr = reinterpret_cast<const chre::fbs::LeCocChannelInfo *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    default: return true;
+  }
+}
+
+inline bool VerifyChannelInfoVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
+  if (!values || !types) return !values && !types;
+  if (values->size() != types->size()) return false;
+  for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
+    if (!VerifyChannelInfo(
+        verifier,  values->Get(i), types->GetEnum<ChannelInfo>(i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+inline void *ChannelInfoUnion::UnPack(const void *obj, ChannelInfo type, const flatbuffers::resolver_function_t *resolver) {
+  switch (type) {
+    case ChannelInfo::LeCocChannelInfo: {
+      auto ptr = reinterpret_cast<const chre::fbs::LeCocChannelInfo *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    default: return nullptr;
+  }
+}
+
+inline flatbuffers::Offset<void> ChannelInfoUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher) const {
+  switch (type) {
+    case ChannelInfo::LeCocChannelInfo: {
+      auto ptr = reinterpret_cast<const chre::fbs::LeCocChannelInfoT *>(value);
+      return CreateLeCocChannelInfo(_fbb, ptr, _rehasher).Union();
+    }
+    default: return 0;
+  }
+}
+
+inline ChannelInfoUnion::ChannelInfoUnion(const ChannelInfoUnion &u) : type(u.type), value(nullptr) {
+  switch (type) {
+    case ChannelInfo::LeCocChannelInfo: {
+      value = new chre::fbs::LeCocChannelInfoT(*reinterpret_cast<chre::fbs::LeCocChannelInfoT *>(u.value));
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+inline void ChannelInfoUnion::Reset() {
+  switch (type) {
+    case ChannelInfo::LeCocChannelInfo: {
+      auto ptr = reinterpret_cast<chre::fbs::LeCocChannelInfoT *>(value);
+      delete ptr;
+      break;
+    }
+    default: break;
+  }
+  value = nullptr;
+  type = ChannelInfo::NONE;
+}
+
+inline bool VerifyMessageHubDetails(flatbuffers::Verifier &verifier, const void *obj, MessageHubDetails type) {
+  switch (type) {
+    case MessageHubDetails::NONE: {
+      return true;
+    }
+    case MessageHubDetails::HubInfoResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::HubInfoResponse *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case MessageHubDetails::VendorHubInfo: {
+      auto ptr = reinterpret_cast<const chre::fbs::VendorHubInfo *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    default: return true;
+  }
+}
+
+inline bool VerifyMessageHubDetailsVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
+  if (!values || !types) return !values && !types;
+  if (values->size() != types->size()) return false;
+  for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
+    if (!VerifyMessageHubDetails(
+        verifier,  values->Get(i), types->GetEnum<MessageHubDetails>(i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+inline void *MessageHubDetailsUnion::UnPack(const void *obj, MessageHubDetails type, const flatbuffers::resolver_function_t *resolver) {
+  switch (type) {
+    case MessageHubDetails::HubInfoResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::HubInfoResponse *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case MessageHubDetails::VendorHubInfo: {
+      auto ptr = reinterpret_cast<const chre::fbs::VendorHubInfo *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    default: return nullptr;
+  }
+}
+
+inline flatbuffers::Offset<void> MessageHubDetailsUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, const flatbuffers::rehasher_function_t *_rehasher) const {
+  switch (type) {
+    case MessageHubDetails::HubInfoResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::HubInfoResponseT *>(value);
+      return CreateHubInfoResponse(_fbb, ptr, _rehasher).Union();
+    }
+    case MessageHubDetails::VendorHubInfo: {
+      auto ptr = reinterpret_cast<const chre::fbs::VendorHubInfoT *>(value);
+      return CreateVendorHubInfo(_fbb, ptr, _rehasher).Union();
+    }
+    default: return 0;
+  }
+}
+
+inline MessageHubDetailsUnion::MessageHubDetailsUnion(const MessageHubDetailsUnion &u) : type(u.type), value(nullptr) {
+  switch (type) {
+    case MessageHubDetails::HubInfoResponse: {
+      value = new chre::fbs::HubInfoResponseT(*reinterpret_cast<chre::fbs::HubInfoResponseT *>(u.value));
+      break;
+    }
+    case MessageHubDetails::VendorHubInfo: {
+      value = new chre::fbs::VendorHubInfoT(*reinterpret_cast<chre::fbs::VendorHubInfoT *>(u.value));
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+inline void MessageHubDetailsUnion::Reset() {
+  switch (type) {
+    case MessageHubDetails::HubInfoResponse: {
+      auto ptr = reinterpret_cast<chre::fbs::HubInfoResponseT *>(value);
+      delete ptr;
+      break;
+    }
+    case MessageHubDetails::VendorHubInfo: {
+      auto ptr = reinterpret_cast<chre::fbs::VendorHubInfoT *>(value);
+      delete ptr;
+      break;
+    }
+    default: break;
+  }
+  value = nullptr;
+  type = MessageHubDetails::NONE;
+}
+
 inline bool VerifyChreMessage(flatbuffers::Verifier &verifier, const void *obj, ChreMessage type) {
   switch (type) {
     case ChreMessage::NONE: {
@@ -5382,6 +8998,66 @@
       auto ptr = reinterpret_cast<const chre::fbs::MessageDeliveryStatus *>(obj);
       return verifier.VerifyTable(ptr);
     }
+    case ChreMessage::BtSocketOpen: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketOpen *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::BtSocketOpenResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketOpenResponse *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::BtSocketClose: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketClose *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::BtSocketCloseResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketCloseResponse *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsRequest: {
+      auto ptr = reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsRequest *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsResponse *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::RegisterMessageHub: {
+      auto ptr = reinterpret_cast<const chre::fbs::RegisterMessageHub *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::UnregisterMessageHub: {
+      auto ptr = reinterpret_cast<const chre::fbs::UnregisterMessageHub *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::RegisterEndpoint: {
+      auto ptr = reinterpret_cast<const chre::fbs::RegisterEndpoint *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::UnregisterEndpoint: {
+      auto ptr = reinterpret_cast<const chre::fbs::UnregisterEndpoint *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::OpenEndpointSessionRequest: {
+      auto ptr = reinterpret_cast<const chre::fbs::OpenEndpointSessionRequest *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::EndpointSessionOpened: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionOpened *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::EndpointSessionClosed: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionClosed *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::EndpointSessionMessage: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionMessage *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::EndpointSessionMessageDeliveryStatus: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionMessageDeliveryStatus *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
     default: return true;
   }
 }
@@ -5528,6 +9204,66 @@
       auto ptr = reinterpret_cast<const chre::fbs::MessageDeliveryStatus *>(obj);
       return ptr->UnPack(resolver);
     }
+    case ChreMessage::BtSocketOpen: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketOpen *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::BtSocketOpenResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketOpenResponse *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::BtSocketClose: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketClose *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::BtSocketCloseResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketCloseResponse *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsRequest: {
+      auto ptr = reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsRequest *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsResponse *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::RegisterMessageHub: {
+      auto ptr = reinterpret_cast<const chre::fbs::RegisterMessageHub *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::UnregisterMessageHub: {
+      auto ptr = reinterpret_cast<const chre::fbs::UnregisterMessageHub *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::RegisterEndpoint: {
+      auto ptr = reinterpret_cast<const chre::fbs::RegisterEndpoint *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::UnregisterEndpoint: {
+      auto ptr = reinterpret_cast<const chre::fbs::UnregisterEndpoint *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::OpenEndpointSessionRequest: {
+      auto ptr = reinterpret_cast<const chre::fbs::OpenEndpointSessionRequest *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::EndpointSessionOpened: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionOpened *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::EndpointSessionClosed: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionClosed *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::EndpointSessionMessage: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionMessage *>(obj);
+      return ptr->UnPack(resolver);
+    }
+    case ChreMessage::EndpointSessionMessageDeliveryStatus: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionMessageDeliveryStatus *>(obj);
+      return ptr->UnPack(resolver);
+    }
     default: return nullptr;
   }
 }
@@ -5662,6 +9398,66 @@
       auto ptr = reinterpret_cast<const chre::fbs::MessageDeliveryStatusT *>(value);
       return CreateMessageDeliveryStatus(_fbb, ptr, _rehasher).Union();
     }
+    case ChreMessage::BtSocketOpen: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketOpenT *>(value);
+      return CreateBtSocketOpen(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::BtSocketOpenResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketOpenResponseT *>(value);
+      return CreateBtSocketOpenResponse(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::BtSocketClose: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketCloseT *>(value);
+      return CreateBtSocketClose(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::BtSocketCloseResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketCloseResponseT *>(value);
+      return CreateBtSocketCloseResponse(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsRequest: {
+      auto ptr = reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsRequestT *>(value);
+      return CreateGetMessageHubsAndEndpointsRequest(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsResponseT *>(value);
+      return CreateGetMessageHubsAndEndpointsResponse(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::RegisterMessageHub: {
+      auto ptr = reinterpret_cast<const chre::fbs::RegisterMessageHubT *>(value);
+      return CreateRegisterMessageHub(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::UnregisterMessageHub: {
+      auto ptr = reinterpret_cast<const chre::fbs::UnregisterMessageHubT *>(value);
+      return CreateUnregisterMessageHub(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::RegisterEndpoint: {
+      auto ptr = reinterpret_cast<const chre::fbs::RegisterEndpointT *>(value);
+      return CreateRegisterEndpoint(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::UnregisterEndpoint: {
+      auto ptr = reinterpret_cast<const chre::fbs::UnregisterEndpointT *>(value);
+      return CreateUnregisterEndpoint(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::OpenEndpointSessionRequest: {
+      auto ptr = reinterpret_cast<const chre::fbs::OpenEndpointSessionRequestT *>(value);
+      return CreateOpenEndpointSessionRequest(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::EndpointSessionOpened: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionOpenedT *>(value);
+      return CreateEndpointSessionOpened(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::EndpointSessionClosed: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionClosedT *>(value);
+      return CreateEndpointSessionClosed(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::EndpointSessionMessage: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionMessageT *>(value);
+      return CreateEndpointSessionMessage(_fbb, ptr, _rehasher).Union();
+    }
+    case ChreMessage::EndpointSessionMessageDeliveryStatus: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionMessageDeliveryStatusT *>(value);
+      return CreateEndpointSessionMessageDeliveryStatus(_fbb, ptr, _rehasher).Union();
+    }
     default: return 0;
   }
 }
@@ -5796,6 +9592,66 @@
       value = new chre::fbs::MessageDeliveryStatusT(*reinterpret_cast<chre::fbs::MessageDeliveryStatusT *>(u.value));
       break;
     }
+    case ChreMessage::BtSocketOpen: {
+      value = new chre::fbs::BtSocketOpenT(*reinterpret_cast<chre::fbs::BtSocketOpenT *>(u.value));
+      break;
+    }
+    case ChreMessage::BtSocketOpenResponse: {
+      value = new chre::fbs::BtSocketOpenResponseT(*reinterpret_cast<chre::fbs::BtSocketOpenResponseT *>(u.value));
+      break;
+    }
+    case ChreMessage::BtSocketClose: {
+      value = new chre::fbs::BtSocketCloseT(*reinterpret_cast<chre::fbs::BtSocketCloseT *>(u.value));
+      break;
+    }
+    case ChreMessage::BtSocketCloseResponse: {
+      value = new chre::fbs::BtSocketCloseResponseT(*reinterpret_cast<chre::fbs::BtSocketCloseResponseT *>(u.value));
+      break;
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsRequest: {
+      value = new chre::fbs::GetMessageHubsAndEndpointsRequestT(*reinterpret_cast<chre::fbs::GetMessageHubsAndEndpointsRequestT *>(u.value));
+      break;
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsResponse: {
+      FLATBUFFERS_ASSERT(false);  // chre::fbs::GetMessageHubsAndEndpointsResponseT not copyable.
+      break;
+    }
+    case ChreMessage::RegisterMessageHub: {
+      FLATBUFFERS_ASSERT(false);  // chre::fbs::RegisterMessageHubT not copyable.
+      break;
+    }
+    case ChreMessage::UnregisterMessageHub: {
+      value = new chre::fbs::UnregisterMessageHubT(*reinterpret_cast<chre::fbs::UnregisterMessageHubT *>(u.value));
+      break;
+    }
+    case ChreMessage::RegisterEndpoint: {
+      FLATBUFFERS_ASSERT(false);  // chre::fbs::RegisterEndpointT not copyable.
+      break;
+    }
+    case ChreMessage::UnregisterEndpoint: {
+      FLATBUFFERS_ASSERT(false);  // chre::fbs::UnregisterEndpointT not copyable.
+      break;
+    }
+    case ChreMessage::OpenEndpointSessionRequest: {
+      FLATBUFFERS_ASSERT(false);  // chre::fbs::OpenEndpointSessionRequestT not copyable.
+      break;
+    }
+    case ChreMessage::EndpointSessionOpened: {
+      value = new chre::fbs::EndpointSessionOpenedT(*reinterpret_cast<chre::fbs::EndpointSessionOpenedT *>(u.value));
+      break;
+    }
+    case ChreMessage::EndpointSessionClosed: {
+      value = new chre::fbs::EndpointSessionClosedT(*reinterpret_cast<chre::fbs::EndpointSessionClosedT *>(u.value));
+      break;
+    }
+    case ChreMessage::EndpointSessionMessage: {
+      value = new chre::fbs::EndpointSessionMessageT(*reinterpret_cast<chre::fbs::EndpointSessionMessageT *>(u.value));
+      break;
+    }
+    case ChreMessage::EndpointSessionMessageDeliveryStatus: {
+      FLATBUFFERS_ASSERT(false);  // chre::fbs::EndpointSessionMessageDeliveryStatusT not copyable.
+      break;
+    }
     default:
       break;
   }
@@ -5963,6 +9819,81 @@
       delete ptr;
       break;
     }
+    case ChreMessage::BtSocketOpen: {
+      auto ptr = reinterpret_cast<chre::fbs::BtSocketOpenT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::BtSocketOpenResponse: {
+      auto ptr = reinterpret_cast<chre::fbs::BtSocketOpenResponseT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::BtSocketClose: {
+      auto ptr = reinterpret_cast<chre::fbs::BtSocketCloseT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::BtSocketCloseResponse: {
+      auto ptr = reinterpret_cast<chre::fbs::BtSocketCloseResponseT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsRequest: {
+      auto ptr = reinterpret_cast<chre::fbs::GetMessageHubsAndEndpointsRequestT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsResponse: {
+      auto ptr = reinterpret_cast<chre::fbs::GetMessageHubsAndEndpointsResponseT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::RegisterMessageHub: {
+      auto ptr = reinterpret_cast<chre::fbs::RegisterMessageHubT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::UnregisterMessageHub: {
+      auto ptr = reinterpret_cast<chre::fbs::UnregisterMessageHubT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::RegisterEndpoint: {
+      auto ptr = reinterpret_cast<chre::fbs::RegisterEndpointT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::UnregisterEndpoint: {
+      auto ptr = reinterpret_cast<chre::fbs::UnregisterEndpointT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::OpenEndpointSessionRequest: {
+      auto ptr = reinterpret_cast<chre::fbs::OpenEndpointSessionRequestT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::EndpointSessionOpened: {
+      auto ptr = reinterpret_cast<chre::fbs::EndpointSessionOpenedT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::EndpointSessionClosed: {
+      auto ptr = reinterpret_cast<chre::fbs::EndpointSessionClosedT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::EndpointSessionMessage: {
+      auto ptr = reinterpret_cast<chre::fbs::EndpointSessionMessageT *>(value);
+      delete ptr;
+      break;
+    }
+    case ChreMessage::EndpointSessionMessageDeliveryStatus: {
+      auto ptr = reinterpret_cast<chre::fbs::EndpointSessionMessageDeliveryStatusT *>(value);
+      delete ptr;
+      break;
+    }
     default: break;
   }
   value = nullptr;
diff --git a/host/common/include/chre_host/hal_error.h b/host/common/include/chre_host/hal_error.h
index 987ab40..9b3100d 100644
--- a/host/common/include/chre_host/hal_error.h
+++ b/host/common/include/chre_host/hal_error.h
@@ -28,6 +28,7 @@
   OPERATION_FAILED = -1,
   INVALID_RESULT = -2,
   INVALID_ARGUMENT = -3,
+  CHRE_NOT_READY = -4,
 
   // Hal client errors
   BINDER_CONNECTION_FAILED = -100,
diff --git a/host/common/include/chre_host/host_protocol_host.h b/host/common/include/chre_host/host_protocol_host.h
index 35f1ced..80c0b04 100644
--- a/host/common/include/chre_host/host_protocol_host.h
+++ b/host/common/include/chre_host/host_protocol_host.h
@@ -74,6 +74,11 @@
 
   virtual void handleSelfTestResponse(
       const ::chre::fbs::SelfTestResponseT & /*response*/){};
+
+  virtual bool handleContextHubV4Message(
+      const ::chre::fbs::ChreMessageUnion & /*msg*/) {
+    return false;
+  };
 };
 
 /**
diff --git a/host/common/include/chre_host/preloaded_nanoapp_loader.h b/host/common/include/chre_host/preloaded_nanoapp_loader.h
index e2c83bf..c830178 100644
--- a/host/common/include/chre_host/preloaded_nanoapp_loader.h
+++ b/host/common/include/chre_host/preloaded_nanoapp_loader.h
@@ -101,6 +101,13 @@
     size_t fragmentId;
   };
 
+  /** The possible results of verification of a fragment load response. */
+  enum class ResponseVerificationResult {
+    SUCCESS = 0,
+    FAILURE = 1,
+    IGNORED = 2,
+  };
+
   /**
    * Loads a preloaded nanoapp.
    *
@@ -130,10 +137,11 @@
                                          const FragmentedLoadRequest &request);
 
   /** Verifies the response of a loading request. */
-  [[nodiscard]] bool verifyFragmentLoadResponse(
+  [[nodiscard]] ResponseVerificationResult verifyFragmentLoadResponse(
       const ::chre::fbs::LoadNanoappResponseT &response) const;
 
-  Transaction mPreloadedNanoappPendingTransaction{0, 0};
+  Transaction mPendingTransaction{/* transactionId= */ 0,
+                                  /* fragmentId= */ 0};
 
   /** The value of this promise carries the result in the load response. */
   std::optional<std::promise<bool>> mFragmentedLoadPromise = std::nullopt;
diff --git a/host/common/include/chre_host/wifi_ext_hal_handler.h b/host/common/include/chre_host/wifi_ext_hal_handler.h
index d83a625..ee0f6e9 100644
--- a/host/common/include/chre_host/wifi_ext_hal_handler.h
+++ b/host/common/include/chre_host/wifi_ext_hal_handler.h
@@ -28,7 +28,6 @@
 #include <string>
 #include <thread>
 
-#include <aidl/android/hardware/wifi/WifiStatusCode.h>
 #include <aidl/vendor/google/wifi_ext/BnWifiExtChreCallback.h>
 #include <aidl/vendor/google/wifi_ext/IWifiExt.h>
 #include <android/binder_manager.h>
@@ -44,7 +43,6 @@
  */
 class WifiExtHalHandler {
  public:
-  using WifiStatusCode = aidl::android::hardware::wifi::WifiStatusCode;
   using IWifiExt = aidl::vendor::google::wifi_ext::IWifiExt;
   using BnWifiExtChreNanCallback =
       aidl::vendor::google::wifi_ext::BnWifiExtChreCallback;
diff --git a/host/common/log_message_parser.cc b/host/common/log_message_parser.cc
index 2499123..8477157 100644
--- a/host/common/log_message_parser.cc
+++ b/host/common/log_message_parser.cc
@@ -268,7 +268,7 @@
   size_t bufferIndex = 0;
   const LogMessageV2 *message = nullptr;
   size_t maxLogMessageLen = 0;
-  while (bufferIndex + kLogHeaderSize <= logBufferSize) {
+  while (bufferIndex + kLogHeaderSize < logBufferSize) {
     message = reinterpret_cast<const LogMessageV2 *>(&logBuffer[bufferIndex]);
     maxLogMessageLen = (logBufferSize - bufferIndex) - kLogHeaderSize;
     logMessageSize = std::nullopt;
diff --git a/host/common/preloaded_nanoapp_loader.cc b/host/common/preloaded_nanoapp_loader.cc
index 7d198a5..5e5d89f 100644
--- a/host/common/preloaded_nanoapp_loader.cc
+++ b/host/common/preloaded_nanoapp_loader.cc
@@ -197,21 +197,35 @@
   return success;
 }
 
-bool PreloadedNanoappLoader::verifyFragmentLoadResponse(
+PreloadedNanoappLoader::ResponseVerificationResult
+PreloadedNanoappLoader::verifyFragmentLoadResponse(
     const ::chre::fbs::LoadNanoappResponseT &response) const {
-  if (!response.success) {
-    LOGE("Loading nanoapp binary fragment %d of transaction %u failed.",
-         response.fragment_id, response.transaction_id);
-    return false;
+  // Allow seen fragment ids to be ignored to tolerate duplicated responses.
+  if (response.fragment_id >= 0 &&
+      response.fragment_id < mPendingTransaction.fragmentId) {
+    LOGW(
+        "Fragmented load response has a fragment id %u while %zu is expected. "
+        "Ignored",
+        response.fragment_id, mPendingTransaction.fragmentId);
+    return ResponseVerificationResult::IGNORED;
   }
-  if (mPreloadedNanoappPendingTransaction.fragmentId != response.fragment_id) {
+
+  // Future or negative fragment ids are not acceptable.
+  if (response.fragment_id != mPendingTransaction.fragmentId) {
     LOGE(
         "Fragmented load response with unexpected fragment id %u while "
         "%zu is expected",
-        response.fragment_id, mPreloadedNanoappPendingTransaction.fragmentId);
-    return false;
+        response.fragment_id, mPendingTransaction.fragmentId);
+    return ResponseVerificationResult::FAILURE;
   }
-  return true;
+
+  // Once fragment id is matched the result is taken.
+  if (!response.success) {
+    LOGE("Loading nanoapp binary fragment %d of transaction %u failed.",
+         response.fragment_id, response.transaction_id);
+    return ResponseVerificationResult::FAILURE;
+  }
+  return ResponseVerificationResult::SUCCESS;
 }
 
 bool PreloadedNanoappLoader::onLoadNanoappResponse(
@@ -225,19 +239,23 @@
         response.transaction_id, response.fragment_id);
     return false;
   }
-  if (mPreloadedNanoappPendingTransaction.transactionId !=
-      response.transaction_id) {
+  if (mPendingTransaction.transactionId != response.transaction_id) {
     LOGE(
         "Fragmented load response with transactionId %u but transactionId "
         "%u is expected. Ignored.",
-        response.transaction_id,
-        mPreloadedNanoappPendingTransaction.transactionId);
+        response.transaction_id, mPendingTransaction.transactionId);
     return false;
   }
+
   // set value for the future instance.
-  mFragmentedLoadPromise->set_value(verifyFragmentLoadResponse(response));
-  // reset the promise as the value can only be retrieved once from it.
-  mFragmentedLoadPromise = std::nullopt;
+  ResponseVerificationResult result = verifyFragmentLoadResponse(response);
+  if (result != ResponseVerificationResult::IGNORED) {
+    mFragmentedLoadPromise->set_value(result ==
+                                      ResponseVerificationResult::SUCCESS);
+    // reset the promise as the value can only be retrieved once from it.
+    mFragmentedLoadPromise = std::nullopt;
+  }
+
   return true;
 }
 
@@ -257,7 +275,7 @@
     // Returns an invalid future to indicate the failure
     return std::future<bool>{};
   }
-  mPreloadedNanoappPendingTransaction = {
+  mPendingTransaction = {
       .transactionId = request.transactionId,
       .fragmentId = request.fragmentId,
   };
diff --git a/host/hal_generic/Android.bp b/host/hal_generic/Android.bp
index f844ec2..b9edabd 100644
--- a/host/hal_generic/Android.bp
+++ b/host/hal_generic/Android.bp
@@ -31,10 +31,7 @@
     defaults: ["chre_aidl_hal_generic_defaults"],
     vendor: true,
     relative_install_path: "hw",
-    srcs: [
-        ":hal_aidl_generic_srcs",
-        "aidl/service.cc",
-    ],
+    srcs: ["aidl/service.cc"],
     init_rc: ["aidl/android.hardware.contexthub-service.generic.rc"],
     vintf_fragments: ["aidl/android.hardware.contexthub-service.generic.xml"],
     visibility: ["//visibility:public"],
@@ -51,42 +48,22 @@
 
 cc_defaults {
     name: "chre_aidl_hal_generic_defaults",
+    defaults: [
+        "//system/chre:contexthub_hal_defaults",
+        "//system/chre:pw_rpc_cflags_chre",
+        "//system/chre:pw_rpc_nanopb_lib_dependencies",
+    ],
     vendor: true,
-    include_dirs: [
-        "system/chre/host/hal_generic/common/",
-        "system/chre/util/include",
+    srcs: [
+        "//system/chre/host/hal_generic:hal_aidl_generic_srcs",
+        "//system/chre:contexthub_hal_client_srcs",
     ],
     cflags: [
         "-DCHRE_HAL_SOCKET_METRICS_ENABLED",
-        "-DCHRE_IS_HOST_BUILD",
-        "-DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4000", // Needed to import CHRE APIs.
-        "-Wall",
-        "-Werror",
-    ],
-    header_libs: [
-        "chre_api",
-    ],
-    shared_libs: [
-        "android.frameworks.stats-V2-ndk",
-        "android.hardware.contexthub-V3-ndk",
-        "chre_atoms_log",
-        "chremetrics-cpp",
-        "libbase",
-        "libbinder_ndk",
-        "libcutils",
-        "libjsoncpp",
-        "liblog",
-        "libprotobuf-cpp-lite",
-        "libutils",
-        "server_configurable_flags",
-        "libaconfig_storage_read_api_cc",
+        "-Wthread-safety", // Need to be explicitly set
     ],
     static_libs: [
-        "chre_client",
         "chre_config_util",
-        "chre_flags_c_lib",
-        "chre_metrics_reporter",
-        "event_logger",
     ],
 }
 
diff --git a/host/hal_generic/aidl/android.hardware.contexthub-service.generic.xml b/host/hal_generic/aidl/android.hardware.contexthub-service.generic.xml
index 2f8ddc8..359bb0a 100644
--- a/host/hal_generic/aidl/android.hardware.contexthub-service.generic.xml
+++ b/host/hal_generic/aidl/android.hardware.contexthub-service.generic.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.contexthub</name>
-        <version>3</version>
+        <version>4</version>
         <interface>
             <name>IContextHub</name>
             <instance>default</instance>
diff --git a/host/hal_generic/aidl/generic_context_hub_aidl.cc b/host/hal_generic/aidl/generic_context_hub_aidl.cc
index de13bc0..e72e3ed 100644
--- a/host/hal_generic/aidl/generic_context_hub_aidl.cc
+++ b/host/hal_generic/aidl/generic_context_hub_aidl.cc
@@ -351,6 +351,73 @@
   return ndk::ScopedAStatus::ok();
 }
 
+ScopedAStatus ContextHub::getHubs(std::vector<HubInfo> *hubs) {
+  if (mV4Impl) return mV4Impl->getHubs(hubs);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::getEndpoints(std::vector<EndpointInfo> *endpoints) {
+  if (mV4Impl) return mV4Impl->getEndpoints(endpoints);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::registerEndpoint(const EndpointInfo &endpoint) {
+  if (mV4Impl) return mV4Impl->registerEndpoint(endpoint);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::unregisterEndpoint(const EndpointInfo &endpoint) {
+  if (mV4Impl) return mV4Impl->unregisterEndpoint(endpoint);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::registerEndpointCallback(
+    const std::shared_ptr<IEndpointCallback> &callback) {
+  if (mV4Impl) return mV4Impl->registerEndpointCallback(callback);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::requestSessionIdRange(int32_t size,
+                                                std::vector<int32_t> *ids) {
+  if (mV4Impl) return mV4Impl->requestSessionIdRange(size, ids);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::openEndpointSession(
+    int32_t sessionId, const EndpointId &destination,
+    const EndpointId &initiator,
+    const std::optional<std::string> &serviceDescriptor) {
+  if (mV4Impl) {
+    return mV4Impl->openEndpointSession(sessionId, destination, initiator,
+                                        serviceDescriptor);
+  }
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::sendMessageToEndpoint(int32_t sessionId,
+                                                const Message &msg) {
+  if (mV4Impl) return mV4Impl->sendMessageToEndpoint(sessionId, msg);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::sendMessageDeliveryStatusToEndpoint(
+    int32_t sessionId, const MessageDeliveryStatus &msgStatus) {
+  if (mV4Impl)
+    return mV4Impl->sendMessageDeliveryStatusToEndpoint(sessionId, msgStatus);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::closeEndpointSession(int32_t sessionId,
+                                               Reason reason) {
+  if (mV4Impl) return mV4Impl->closeEndpointSession(sessionId, reason);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHub::endpointSessionOpenComplete(int32_t sessionId) {
+  if (mV4Impl) return mV4Impl->endpointSessionOpenComplete(sessionId);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
 void ContextHub::onNanoappMessage(const ::chre::fbs::NanoappMessageT &message) {
   std::lock_guard<std::mutex> lock(mCallbackMutex);
   if (mCallback != nullptr) {
@@ -461,6 +528,12 @@
   debugDumpComplete();
 }
 
+bool ContextHub::onContextHubV4Message(
+    const ::chre::fbs::ChreMessageUnion &message) {
+  if (mV4Impl) return mV4Impl->handleMessageFromChre(message);
+  return false;
+}
+
 void ContextHub::handleServiceDeath() {
   LOGI("Context Hub Service died ...");
   {
diff --git a/host/hal_generic/aidl/generic_context_hub_aidl.h b/host/hal_generic/aidl/generic_context_hub_aidl.h
index 024cb1e..7dd85b0 100644
--- a/host/hal_generic/aidl/generic_context_hub_aidl.h
+++ b/host/hal_generic/aidl/generic_context_hub_aidl.h
@@ -18,8 +18,10 @@
 #define ANDROID_HARDWARE_CONTEXTHUB_AIDL_CONTEXTHUB_H
 
 #include <aidl/android/hardware/contexthub/BnContextHub.h>
+#include <android_chre_flags.h>
 #include <log/log.h>
 #include <atomic>
+#include <functional>
 #include <future>
 #include <map>
 #include <mutex>
@@ -27,6 +29,7 @@
 #include <unordered_set>
 
 #include "chre_host/napp_header.h"
+#include "context_hub_v4_impl.h"
 #include "debug_dump_helper.h"
 #include "event_logger.h"
 #include "hal_chre_socket_connection.h"
@@ -56,7 +59,13 @@
  public:
   ContextHub()
       : mDeathRecipient(
-            AIBinder_DeathRecipient_new(ContextHub::onServiceDied)) {}
+            AIBinder_DeathRecipient_new(ContextHub::onServiceDied)) {
+    if (::android::chre::flags::offload_implementation()) {
+      mV4Impl.emplace([this](uint8_t *data, size_t size) {
+        return mConnection.sendRawMessage(data, size);
+      });
+    }
+  }
   ::ndk::ScopedAStatus getContextHubs(
       std::vector<ContextHubInfo> *out_contextHubInfos) override;
   ::ndk::ScopedAStatus loadNanoapp(int32_t contextHubId,
@@ -88,6 +97,27 @@
       char16_t in_hostEndpointId) override;
   ::ndk::ScopedAStatus onNanSessionStateChanged(
       const NanSessionStateUpdate &in_update) override;
+  ::ndk::ScopedAStatus getHubs(std::vector<HubInfo> *hubs) override;
+  ::ndk::ScopedAStatus getEndpoints(
+      std::vector<EndpointInfo> *endpoints) override;
+  ::ndk::ScopedAStatus registerEndpoint(const EndpointInfo &endpoint) override;
+  ::ndk::ScopedAStatus unregisterEndpoint(
+      const EndpointInfo &endpoint) override;
+  ::ndk::ScopedAStatus registerEndpointCallback(
+      const std::shared_ptr<IEndpointCallback> &callback) override;
+  ::ndk::ScopedAStatus requestSessionIdRange(
+      int32_t size, std::vector<int32_t> *ids) override;
+  ::ndk::ScopedAStatus openEndpointSession(
+      int32_t sessionId, const EndpointId &destination,
+      const EndpointId &initiator,
+      const std::optional<std::string> &serviceDescriptor) override;
+  ::ndk::ScopedAStatus sendMessageToEndpoint(int32_t sessionId,
+                                             const Message &msg) override;
+  ::ndk::ScopedAStatus sendMessageDeliveryStatusToEndpoint(
+      int32_t sessionId, const MessageDeliveryStatus &msgStatus) override;
+  ::ndk::ScopedAStatus closeEndpointSession(int32_t sessionId,
+                                            Reason reason) override;
+  ::ndk::ScopedAStatus endpointSessionOpenComplete(int32_t sessionId) override;
 
   void onNanoappMessage(const ::chre::fbs::NanoappMessageT &message) override;
 
@@ -103,6 +133,9 @@
   void onDebugDumpComplete(
       const ::chre::fbs::DebugDumpResponseT &response) override;
 
+  bool onContextHubV4Message(
+      const ::chre::fbs::ChreMessageUnion &message) override;
+
   void handleServiceDeath();
   static void onServiceDied(void *cookie);
 
@@ -238,6 +271,11 @@
 
   ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
 
+  // Implementation of the V4 API.
+  std::optional<
+      ::android::hardware::contexthub::common::implementation::ContextHubV4Impl>
+      mV4Impl{};
+
   std::map<Setting, bool> mSettingEnabled;
   std::optional<bool> mIsWifiAvailable;
   std::optional<bool> mIsBleAvailable;
diff --git a/host/hal_generic/common/chre_connection_callback.h b/host/hal_generic/common/chre_connection_callback.h
index e751db6..b318836 100644
--- a/host/hal_generic/common/chre_connection_callback.h
+++ b/host/hal_generic/common/chre_connection_callback.h
@@ -32,7 +32,10 @@
 
   /** This method should be called when CHRE is reconnected to HAL and ready to
    * accept new messages. */
-  virtual void onChreRestarted(){};
+  virtual void onChreRestarted() {};
+
+  /** This method should be called when CHRE is disconnected from HAL. */
+  virtual void onChreDisconnected() {};
 };
 }  // namespace android::hardware::contexthub::common::implementation
 #endif  // ANDROID_HARDWARE_CONTEXTHUB_COMMON_CHRE_CONNECTION_CALLBACK_H_
\ No newline at end of file
diff --git a/host/hal_generic/common/context_hub_v4_impl.cc b/host/hal_generic/common/context_hub_v4_impl.cc
new file mode 100644
index 0000000..91fdde6
--- /dev/null
+++ b/host/hal_generic/common/context_hub_v4_impl.cc
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "context_hub_v4_impl.h"
+
+#include <assert.h>
+#include <inttypes.h>
+
+#include <functional>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <aidl/android/hardware/contexthub/BnContextHub.h>
+#include <chre_host/generated/host_messages_generated.h>
+#include <chre_host/log.h>
+
+namespace android::hardware::contexthub::common::implementation {
+
+using ::aidl::android::hardware::contexthub::BnContextHub;
+using ::chre::fbs::ChreMessage;
+using HostHub = MessageHubManager::HostHub;
+
+void ContextHubV4Impl::init() {
+  // TODO(b/378545373): Send message to get hubs/endpoints state dump to
+  // initialize mManager.
+}
+
+namespace {
+
+ScopedAStatus fromPwStatus(pw::Status status) {
+  switch (status.code()) {
+    case PW_STATUS_OK:
+      return ScopedAStatus::ok();
+    case PW_STATUS_NOT_FOUND:
+      [[fallthrough]];
+    case PW_STATUS_ALREADY_EXISTS:
+      [[fallthrough]];
+    case PW_STATUS_OUT_OF_RANGE:
+      [[fallthrough]];
+    case PW_STATUS_PERMISSION_DENIED:
+      [[fallthrough]];
+    case PW_STATUS_INVALID_ARGUMENT:
+      return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    case PW_STATUS_UNIMPLEMENTED:
+      return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    default:
+      return ScopedAStatus::fromServiceSpecificError(
+          BnContextHub::EX_CONTEXT_HUB_UNSPECIFIED);
+  }
+}
+
+}  // namespace
+
+ScopedAStatus ContextHubV4Impl::getHubs(std::vector<HubInfo> *hubs) {
+  *hubs = mManager.getEmbeddedHubs();
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus ContextHubV4Impl::getEndpoints(
+    std::vector<EndpointInfo> *endpoints) {
+  *endpoints = mManager.getEmbeddedEndpoints();
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus ContextHubV4Impl::registerEndpoint(const EndpointInfo &endpoint) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  if (auto status = hub->addEndpoint(hub, endpoint); !status.ok()) {
+    LOGE("Failed to register endpoint %" PRId64 " on hub %" PRId64
+         " with %" PRId32,
+         endpoint.id.id, hub->id(), status.code());
+    return fromPwStatus(status);
+  }
+  // TODO(b/378545373): Send the endpoint info to CHRE.
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHubV4Impl::unregisterEndpoint(
+    const EndpointInfo &endpoint) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  if (auto status = hub->removeEndpoint(endpoint.id); !status.ok()) {
+    LOGE("Failed to unregister endpoint %" PRId32 " on hub %" PRId32
+         " with %" PRId32,
+         endpoint.id.id, hub->id(), status.code());
+    return fromPwStatus(status);
+  }
+  // TODO(b/378545373): Send the endpoint info to CHRE.
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHubV4Impl::registerEndpointCallback(
+    const std::shared_ptr<IEndpointCallback> &callback) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  return fromPwStatus(hub->setCallback(callback));
+}
+
+ScopedAStatus ContextHubV4Impl::requestSessionIdRange(
+    int32_t size, std::vector<int32_t> *ids) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  auto statusOrRange = hub->reserveSessionIdRange(size);
+  if (!statusOrRange.ok()) {
+    LOGE("Failed to reserve %" PRId32 " session ids on hub %" PRId64
+         " with %" PRId32,
+         size, hub->id(), statusOrRange.status().code());
+    return fromPwStatus(statusOrRange.status());
+  }
+  ids->resize(2);
+  (*ids)[0] = statusOrRange->first;
+  (*ids)[1] = statusOrRange->second;
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus ContextHubV4Impl::openEndpointSession(
+    int32_t sessionId, const EndpointId &destination,
+    const EndpointId &initiator,
+    const std::optional<std::string> & /*serviceDescriptor*/) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  pw::Result<std::shared_ptr<HostHub>> statusOrPruneHub =
+      hub->openSession(hub, initiator, destination, sessionId);
+  if (!statusOrPruneHub.ok()) {
+    LOGE("Failed to open session %" PRId32 " from (%" PRId64 ", %" PRId64
+         ") to (%" PRId64 ", %" PRId64 ") with %" PRId32,
+         sessionId, initiator.hubId, initiator.id, destination.hubId,
+         destination.id, statusOrPruneHub.status().code());
+    return fromPwStatus(statusOrPruneHub.status());
+  } else if (*statusOrPruneHub) {
+    // Send a closed session notification on the hub that hosted the pruned
+    // session.
+    auto status = (*statusOrPruneHub)->closeSession(sessionId);
+    LOGD("Pruning session %" PRIu16 " with status %" PRId32, sessionId,
+         status.code());
+  }
+  // TODO(b/378545373): Send the session open request to CHRE.
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHubV4Impl::sendMessageToEndpoint(int32_t sessionId,
+                                                      const Message & /*msg*/) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  if (auto status = hub->checkSessionOpen(sessionId); !status.ok()) {
+    if (status.IsUnavailable()) {
+      hub->getCallback()->onCloseEndpointSession(sessionId,
+                                                 Reason::ENDPOINT_GONE);
+    } else {
+      LOGE("Failed to verify session %" PRId32 " on hub %" PRId64
+           " with %" PRId32,
+           sessionId, hub->id(), status.code());
+    }
+    return fromPwStatus(status);
+  }
+  // TODO(b/378545373): Handle reliable messages.
+  // TODO(b/378545373): Send the message to CHRE.
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHubV4Impl::sendMessageDeliveryStatusToEndpoint(
+    int32_t sessionId, const MessageDeliveryStatus & /*msgStatus*/) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  if (auto status = hub->checkSessionOpen(sessionId); !status.ok()) {
+    if (status.IsUnavailable()) {
+      hub->getCallback()->onCloseEndpointSession(sessionId,
+                                                 Reason::ENDPOINT_GONE);
+    } else {
+      LOGE("Failed to verify session %" PRId32 " on hub %" PRId64
+           " with %" PRId32,
+           sessionId, hub->id(), status.code());
+    }
+    return fromPwStatus(status);
+  }
+  // TODO(b/378545373): Send the message to CHRE.
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHubV4Impl::closeEndpointSession(int32_t sessionId,
+                                                     Reason /*reason*/) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  if (auto status = hub->closeSession(sessionId); !status.ok()) {
+    LOGE("Failed to close session %" PRId32 " on hub %" PRId64 " with %" PRId32,
+         sessionId, hub->id(), status.code());
+    return fromPwStatus(status);
+  }
+  hub->getCallback()->onCloseEndpointSession(
+      sessionId, Reason::CLOSE_ENDPOINT_SESSION_REQUESTED);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus ContextHubV4Impl::endpointSessionOpenComplete(int32_t sessionId) {
+  std::shared_ptr<HostHub> hub =
+      mManager.getHostHubByPid(AIBinder_getCallingPid());
+  assert(hub != nullptr);
+  if (auto status = hub->ackSession(sessionId); !status.ok()) {
+    if (status.IsUnavailable()) {
+      hub->getCallback()->onCloseEndpointSession(sessionId,
+                                                 Reason::ENDPOINT_GONE);
+    } else {
+      LOGE("Failed to verify session %" PRId32 " on hub %" PRId64
+           " with %" PRId32,
+           sessionId, hub->id(), status.code());
+    }
+    return fromPwStatus(status);
+  }
+  // TODO(b/378545373): Send the session id to CHRE.
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+bool ContextHubV4Impl::handleMessageFromChre(
+    const ::chre::fbs::ChreMessageUnion &message) {
+  switch (message.type) {
+    case ChreMessage::GetMessageHubsAndEndpointsResponse:
+      onGetMessageHubsAndEndpointsResponse(
+          *message.AsGetMessageHubsAndEndpointsResponse());
+      break;
+    case ChreMessage::RegisterMessageHub:
+      onRegisterMessageHub(*message.AsRegisterMessageHub());
+      break;
+    case ChreMessage::UnregisterMessageHub:
+      onUnregisterMessageHub(*message.AsUnregisterMessageHub());
+      break;
+    case ChreMessage::RegisterEndpoint:
+      onRegisterEndpoint(*message.AsRegisterEndpoint());
+      break;
+    case ChreMessage::OpenEndpointSessionRequest:
+      onOpenEndpointSessionRequest(*message.AsOpenEndpointSessionRequest());
+      break;
+    case ChreMessage::EndpointSessionOpened:
+      onEndpointSessionOpened(*message.AsEndpointSessionOpened());
+      break;
+    case ChreMessage::EndpointSessionClosed:
+      onEndpointSessionClosed(*message.AsEndpointSessionClosed());
+      break;
+    case ChreMessage::EndpointSessionMessage:
+      onEndpointSessionMessage(*message.AsEndpointSessionMessage());
+      break;
+    case ChreMessage::EndpointSessionMessageDeliveryStatus:
+      onEndpointSessionMessageDeliveryStatus(
+          *message.AsEndpointSessionMessageDeliveryStatus());
+      break;
+    default:
+      LOGW("Got unexpected message type %" PRIu8,
+           static_cast<uint8_t>(message.type));
+      return false;
+  }
+  return true;
+}
+
+void ContextHubV4Impl::onGetMessageHubsAndEndpointsResponse(
+    const ::chre::fbs::GetMessageHubsAndEndpointsResponseT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  std::vector<HubInfo> hubs;
+  std::vector<EndpointInfo> endpoints;
+  LOGI("Initializing embedded message hub cache");
+  mManager.initEmbeddedHubsAndEndpoints(hubs, endpoints);
+}
+
+void ContextHubV4Impl::onRegisterMessageHub(
+    const ::chre::fbs::RegisterMessageHubT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  HubInfo hub;
+  LOGI("Embedded message hub %" PRId64 " registered", hub.hubId);
+  mManager.addEmbeddedHub(hub);
+}
+
+void ContextHubV4Impl::onUnregisterMessageHub(
+    const ::chre::fbs::UnregisterMessageHubT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  int64_t id = 0;
+  LOGI("Embedded message hub %" PRId64 " unregistered", id);
+  std::vector<EndpointId> endpoints = mManager.removeEmbeddedHub(id);
+  if (!endpoints.empty()) {
+    mManager.forEachHostHub([&endpoints](HostHub &hub) {
+      hub.getCallback()->onEndpointStopped(endpoints, Reason::HUB_RESET);
+    });
+  }
+}
+
+void ContextHubV4Impl::onRegisterEndpoint(
+    const ::chre::fbs::RegisterEndpointT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  EndpointInfo endpoint;
+  LOGI("Adding embedded endpoint (%" PRId64 ", %" PRId64 ")", endpoint.id.hubId,
+       endpoint.id.id);
+  mManager.addEmbeddedEndpoint(endpoint);
+  mManager.forEachHostHub([&endpoint](HostHub &hub) {
+    hub.getCallback()->onEndpointStarted({endpoint});
+  });
+}
+
+void ContextHubV4Impl::onUnregisterEndpoint(
+    const ::chre::fbs::UnregisterEndpointT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  EndpointId endpoint;
+  LOGI("Removing embedded endpoint (%" PRId64 ", %" PRId64 ")", endpoint.hubId,
+       endpoint.id);
+  mManager.removeEmbeddedEndpoint(endpoint);
+  mManager.forEachHostHub([&endpoint](HostHub &hub) {
+    hub.getCallback()->onEndpointStopped({endpoint}, Reason::ENDPOINT_GONE);
+  });
+}
+
+void ContextHubV4Impl::onOpenEndpointSessionRequest(
+    const ::chre::fbs::OpenEndpointSessionRequestT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  std::optional<std::string> serviceDescriptor;
+  EndpointId local, remote;
+  uint16_t sessionId = 0;
+  LOGD("New session (%" PRIu16 ") request from (%" PRId64 ", %" PRId64
+       ") to "
+       "(%" PRId64 ", %" PRId64 ")",
+       sessionId, remote.hubId, remote.id, local.hubId, local.id);
+
+  // Look up the host hub based on the host endpoint.
+  std::shared_ptr<HostHub> hub = mManager.getHostHubByEndpointId(local);
+  if (!hub) {
+    LOGW("Unable to find host hub");
+    return;
+  }
+
+  // Record the open session request and pass it on to the appropriate client.
+  auto statusOrPruneHub = hub->openSession(hub, local, remote, sessionId);
+  if (!statusOrPruneHub.ok()) {
+    LOGE("Failed to request session %" PRIu16 " with %" PRId32, sessionId,
+         statusOrPruneHub.status().code());
+    return;
+  } else if (*statusOrPruneHub) {
+    // Send a closed session notification on the hub that hosted the pruned
+    // session.
+    auto status = (*statusOrPruneHub)->closeSession(sessionId);
+    LOGD("Pruning session %" PRIu16 " with status %" PRId32, sessionId,
+         status.code());
+  }
+  hub->getCallback()->onEndpointSessionOpenRequest(
+      sessionId, local, remote, std::move(serviceDescriptor));
+}
+
+namespace {
+
+void logGetHubFailure(pw::Status status, int32_t sessionId) {
+  if (status.IsUnavailable()) {
+    LOGD("Session %" PRId32 " was pruned.", sessionId);
+  } else {
+    LOGE("Failed to operate on session %" PRId32 " with %" PRId32, sessionId,
+         status.code());
+  }
+}
+
+}  // namespace
+
+void ContextHubV4Impl::onEndpointSessionOpened(
+    const ::chre::fbs::EndpointSessionOpenedT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  int32_t id = 0;
+  LOGD("New session ack for id %" PRId32, id);
+  auto statusOrHub = mManager.ackSessionAndGetHostHub(id);
+  if (!statusOrHub.ok()) {
+    logGetHubFailure(statusOrHub.status(), id);
+    // TODO(b/378545373): Send a notification back to CHRE.
+    return;
+  }
+
+  // Only send a session open complete message to the host hub client if it was
+  // the initiator.
+  if (static_cast<uint16_t>(id) >= MessageHubManager::kHostSessionIdBase)
+    (*statusOrHub)->getCallback()->onEndpointSessionOpenComplete(id);
+}
+
+void ContextHubV4Impl::onEndpointSessionClosed(
+    const ::chre::fbs::EndpointSessionClosedT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  int32_t id = 0;
+  Reason reason = Reason::UNSPECIFIED;
+  LOGD("Closing session id %" PRId32 " for %" PRIu8, id, reason);
+  auto statusOrHub = mManager.checkSessionOpenAndGetHostHub(id);
+  if (!statusOrHub.ok()) {
+    logGetHubFailure(statusOrHub.status(), id);
+    return;
+  }
+  (*statusOrHub)->getCallback()->onCloseEndpointSession(id, reason);
+}
+
+void ContextHubV4Impl::onEndpointSessionMessage(
+    const ::chre::fbs::EndpointSessionMessageT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  Message message;
+  int32_t sessionId = 0;
+  auto statusOrHub = mManager.checkSessionOpenAndGetHostHub(sessionId);
+  if (!statusOrHub.ok()) {
+    logGetHubFailure(statusOrHub.status(), sessionId);
+    // TODO(b/378545373): Send a notification back to CHRE.
+    return;
+  }
+  (*statusOrHub)->getCallback()->onMessageReceived(sessionId, message);
+}
+
+void ContextHubV4Impl::onEndpointSessionMessageDeliveryStatus(
+    const ::chre::fbs::EndpointSessionMessageDeliveryStatusT & /*msg*/) {
+  // TODO(b/378545373): Parse flatbuffer message
+  MessageDeliveryStatus status;
+  int32_t sessionId = 0;
+  auto statusOrHub = mManager.checkSessionOpenAndGetHostHub(sessionId);
+  if (!statusOrHub.ok()) {
+    logGetHubFailure(statusOrHub.status(), sessionId);
+    // TODO(b/378545373): Send a notification back to CHRE.
+    return;
+  }
+  // TODO(b/378545373): Handle reliable messages.
+  (*statusOrHub)
+      ->getCallback()
+      ->onMessageDeliveryStatusReceived(sessionId, status);
+}
+
+void ContextHubV4Impl::onHostHubDown(int64_t /*id*/) {
+  // TODO(b/378545373): Send an UnregisterMessageHub message to CHRE with id.
+}
+
+}  // namespace android::hardware::contexthub::common::implementation
diff --git a/host/hal_generic/common/context_hub_v4_impl.h b/host/hal_generic/common/context_hub_v4_impl.h
new file mode 100644
index 0000000..b07dc34
--- /dev/null
+++ b/host/hal_generic/common/context_hub_v4_impl.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <aidl/android/hardware/contexthub/BnContextHub.h>
+#include <chre_host/generated/host_messages_generated.h>
+
+#include "message_hub_manager.h"
+
+namespace android::hardware::contexthub::common::implementation {
+
+using ::aidl::android::hardware::contexthub::EndpointId;
+using ::aidl::android::hardware::contexthub::EndpointInfo;
+using ::aidl::android::hardware::contexthub::HubInfo;
+using ::aidl::android::hardware::contexthub::IEndpointCallback;
+using ::aidl::android::hardware::contexthub::Message;
+using ::aidl::android::hardware::contexthub::MessageDeliveryStatus;
+using ::aidl::android::hardware::contexthub::Reason;
+using ::ndk::ScopedAStatus;
+
+class ContextHubV4Impl {
+ public:
+  using SendMessageFn = std::function<bool(uint8_t *data, size_t size)>;
+
+  explicit ContextHubV4Impl(SendMessageFn sendMessageFn)
+      : mManager([this](int64_t id) { onHostHubDown(id); }),
+        mSendMessageFn(std::move(sendMessageFn)) {}
+  ~ContextHubV4Impl() = default;
+
+  /**
+   * Initializes the implementation.
+   *
+   * This should be called once a connection with CHRE has been established.
+   * Requests a dump of embedded hubs and endpoints from CHRE.
+   */
+  void init();
+
+  // ContextHub V4 API implementation.
+  ScopedAStatus getHubs(std::vector<HubInfo> *hubs);
+  ScopedAStatus getEndpoints(std::vector<EndpointInfo> *endpoints);
+  ScopedAStatus registerEndpoint(const EndpointInfo &endpoint);
+  ScopedAStatus unregisterEndpoint(const EndpointInfo &endpoint);
+  ScopedAStatus registerEndpointCallback(
+      const std::shared_ptr<IEndpointCallback> &callback);
+  ScopedAStatus requestSessionIdRange(int32_t size, std::vector<int32_t> *ids);
+  ScopedAStatus openEndpointSession(
+      int32_t sessionId, const EndpointId &destination,
+      const EndpointId &initiator,
+      const std::optional<std::string> &serviceDescriptor);
+  ScopedAStatus sendMessageToEndpoint(int32_t sessionId, const Message &msg);
+  ScopedAStatus sendMessageDeliveryStatusToEndpoint(
+      int32_t sessionId, const MessageDeliveryStatus &msgStatus);
+  ScopedAStatus closeEndpointSession(int32_t sessionId, Reason reason);
+  ScopedAStatus endpointSessionOpenComplete(int32_t sessionId);
+
+  /**
+   * Handles a CHRE message that is part of the V4 implementation.
+   *
+   * @param message Validated union of the various message types.
+   * @return true if the message could be handled
+   */
+  bool handleMessageFromChre(const ::chre::fbs::ChreMessageUnion &message);
+
+ private:
+  // Callbacks for each message type from CHRE.
+  void onGetMessageHubsAndEndpointsResponse(
+      const ::chre::fbs::GetMessageHubsAndEndpointsResponseT &msg);
+  void onRegisterMessageHub(const ::chre::fbs::RegisterMessageHubT &msg);
+  void onUnregisterMessageHub(const ::chre::fbs::UnregisterMessageHubT &msg);
+  void onRegisterEndpoint(const ::chre::fbs::RegisterEndpointT &msg);
+  void onUnregisterEndpoint(const ::chre::fbs::UnregisterEndpointT &msg);
+  void onOpenEndpointSessionRequest(
+      const ::chre::fbs::OpenEndpointSessionRequestT &msg);
+  void onEndpointSessionOpened(const ::chre::fbs::EndpointSessionOpenedT &msg);
+  void onEndpointSessionClosed(const ::chre::fbs::EndpointSessionClosedT &msg);
+  void onEndpointSessionMessage(
+      const ::chre::fbs::EndpointSessionMessageT &msg);
+  void onEndpointSessionMessageDeliveryStatus(
+      const ::chre::fbs::EndpointSessionMessageDeliveryStatusT &msg);
+
+  // Callback invoked when a HAL client associated with a host hub goes down.
+  void onHostHubDown(int64_t id);
+
+  MessageHubManager mManager;
+  SendMessageFn mSendMessageFn;
+};
+
+}  // namespace android::hardware::contexthub::common::implementation
diff --git a/host/hal_generic/common/generic_context_hub_base.h b/host/hal_generic/common/generic_context_hub_base.h
index 64b4a24..b7513d5 100644
--- a/host/hal_generic/common/generic_context_hub_base.h
+++ b/host/hal_generic/common/generic_context_hub_base.h
@@ -350,6 +350,11 @@
     debugDumpComplete();
   }
 
+  bool onContextHubV4Message(
+      const ::chre::fbs::ChreMessageUnion & /* message */) override {
+    return false;
+  }
+
   // Write a string to the debug file.
   void writeToDebugFile(const char *str) override {
     if (checkDebugFd()) {
diff --git a/host/hal_generic/common/hal_chre_socket_connection.cc b/host/hal_generic/common/hal_chre_socket_connection.cc
index 4d6b7b7..1c72bde 100644
--- a/host/hal_generic/common/hal_chre_socket_connection.cc
+++ b/host/hal_generic/common/hal_chre_socket_connection.cc
@@ -146,6 +146,10 @@
   return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
 }
 
+bool HalChreSocketConnection::sendRawMessage(uint8_t *data, size_t size) {
+  return mClient.sendMessage(data, size);
+}
+
 bool HalChreSocketConnection::sendSettingChangedNotification(
     ::chre::fbs::Setting fbsSetting, ::chre::fbs::SettingState fbsState) {
   FlatBufferBuilder builder(64);
@@ -312,6 +316,11 @@
   mCallback->onDebugDumpComplete(response);
 }
 
+bool HalChreSocketConnection::SocketCallbacks::handleContextHubV4Message(
+    const ::chre::fbs::ChreMessageUnion &message) {
+  return mCallback->onContextHubV4Message(message);
+}
+
 bool HalChreSocketConnection::isExpectedLoadResponseLocked(
     const ::chre::fbs::LoadNanoappResponseT &response) {
   return mPendingLoadTransaction.has_value() &&
diff --git a/host/hal_generic/common/hal_chre_socket_connection.h b/host/hal_generic/common/hal_chre_socket_connection.h
index 5a3be43..a62ced8 100644
--- a/host/hal_generic/common/hal_chre_socket_connection.h
+++ b/host/hal_generic/common/hal_chre_socket_connection.h
@@ -89,6 +89,15 @@
    */
   virtual void onDebugDumpComplete(
       const ::chre::fbs::DebugDumpResponseT &response) = 0;
+
+  /**
+   * Handles a ContextHub V4+ message or returns false.
+   *
+   * @param message The union of possible messages.
+   * @return true on successful handling
+   */
+  virtual bool onContextHubV4Message(
+      const ::chre::fbs::ChreMessageUnion &message) = 0;
 };
 
 /**
@@ -117,6 +126,8 @@
   bool sendSettingChangedNotification(::chre::fbs::Setting fbsSetting,
                                       ::chre::fbs::SettingState fbsState);
 
+  bool sendRawMessage(uint8_t *data, size_t size);
+
   bool onHostEndpointConnected(uint16_t hostEndpointId, uint8_t type,
                                const std::string &package_name,
                                const std::string &attribution_tag);
@@ -155,6 +166,8 @@
     void handleDebugDumpData(const ::chre::fbs::DebugDumpDataT &data) override;
     void handleDebugDumpResponse(
         const ::chre::fbs::DebugDumpResponseT &response) override;
+    bool handleContextHubV4Message(
+        const ::chre::fbs::ChreMessageUnion &message) override;
 
    private:
     HalChreSocketConnection &mParent;
diff --git a/host/hal_generic/common/hal_client_manager.cc b/host/hal_generic/common/hal_client_manager.cc
index ec75fdc..f300cde 100644
--- a/host/hal_generic/common/hal_client_manager.cc
+++ b/host/hal_generic/common/hal_client_manager.cc
@@ -554,13 +554,20 @@
 }
 
 void HalClientManager::handleChreRestart() {
+  const std::lock_guard<std::mutex> lock(mLock);
+  mPendingLoadTransaction.reset();
+  mPendingUnloadTransaction.reset();
+  for (Client &client : mClients) {
+    client.endpointIds.clear();
+  }
+}
+
+std::vector<std::shared_ptr<IContextHubCallback>>
+HalClientManager::getCallbacks() {
   std::vector<std::shared_ptr<IContextHubCallback>> callbacks;
   {
     const std::lock_guard<std::mutex> lock(mLock);
-    mPendingLoadTransaction.reset();
-    mPendingUnloadTransaction.reset();
     for (Client &client : mClients) {
-      client.endpointIds.clear();
       if (client.callback != nullptr) {
         // Create a copy of the callback and call it later without holding the
         // lock to avoid deadlocks.
@@ -568,10 +575,7 @@
       }
     }
   }
-
-  for (const auto &callback : callbacks) {
-    callback->handleContextHubAsyncEvent(AsyncEventType::RESTARTED);
-  }
+  return callbacks;
 }
 
 void HalClientManager::updateClientIdMappingFile() {
diff --git a/host/hal_generic/common/hal_client_manager.h b/host/hal_generic/common/hal_client_manager.h
index 0073da2..5dec2e3 100644
--- a/host/hal_generic/common/hal_client_manager.h
+++ b/host/hal_generic/common/hal_client_manager.h
@@ -65,7 +65,7 @@
  *     identifies a host app that communicates with a HAL client.
  *
  * For a host endpoint connected to ContextHubService, its endpoint id is kept
- *in the form below during the communication with CHRE.
+ * in the form below during the communication with CHRE.
  *
  *  0                   1
  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
@@ -182,6 +182,13 @@
   std::shared_ptr<IContextHubCallback> getCallback(HalClientId clientId);
 
   /**
+   * Gets all the callbacks and postpone any API calls to the caller.
+   *
+   * @return all the non-null callback pointers
+   */
+  std::vector<std::shared_ptr<IContextHubCallback>> getCallbacks();
+
+  /**
    * Registers a IContextHubCallback function mapped to the current client's
    * client id. @p deathRecipient and @p deathRecipientCookie are used to unlink
    * the previous registered callback for the same client, if any.
diff --git a/host/hal_generic/common/message_hub_manager.cc b/host/hal_generic/common/message_hub_manager.cc
new file mode 100644
index 0000000..6306eca
--- /dev/null
+++ b/host/hal_generic/common/message_hub_manager.cc
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "message_hub_manager.h"
+
+#include <unistd.h>
+
+#include <cstdint>
+#include <functional>
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/contexthub/BnContextHub.h>
+
+#include "chre_host/log.h"
+#include "pw_result/result.h"
+#include "pw_status/status.h"
+#include "pw_status/try.h"
+
+namespace android::hardware::contexthub::common::implementation {
+
+using HostHub = MessageHubManager::HostHub;
+
+HostHub::~HostHub() {
+  std::lock_guard lock(mManager.mLock);
+  unlinkCallbackIfNecessaryLocked();
+}
+
+pw::Status HostHub::setCallback(std::shared_ptr<IEndpointCallback> callback) {
+  std::lock_guard lock(mManager.mLock);
+  auto *cookie = new DeathRecipientCookie{&mManager, kPid};
+  if (AIBinder_linkToDeath(callback->asBinder().get(),
+                           mManager.mDeathRecipient.get(),
+                           cookie) != STATUS_OK) {
+    LOGE("Failed to link callback for hub %ld (pid %d) to death recipient", kId,
+         kPid);
+    delete cookie;
+    return pw::Status::Internal();
+  }
+  unlinkCallbackIfNecessaryLocked();
+  mCallback = std::move(callback);
+  mCookie = cookie;
+  return pw::OkStatus();
+}
+
+std::shared_ptr<IEndpointCallback> HostHub::getCallback() const {
+  std::lock_guard lock(mManager.mLock);
+  return mCallback;
+}
+
+pw::Status HostHub::addEndpoint(std::weak_ptr<HostHub> self,
+                                const EndpointInfo &info) {
+  std::lock_guard lock(mManager.mLock);
+  PW_TRY(checkValidLocked());
+  int64_t id = info.id.id;
+  if (auto it = mIdToEndpoint.find(id); it != mIdToEndpoint.end()) {
+    LOGE("Endpoint %ld already exists in hub %ld (pid %d)", id, kId, kPid);
+    return pw::Status::AlreadyExists();
+  }
+  if (kId == kHubIdInvalid) {
+    // If this is the hub's first endpoint, store its hub id and register it
+    // with the manager.
+    if (info.id.hubId == kContextHubServiceHubId &&
+        AIBinder_getCallingUid() != kSystemServerUid) {
+      LOGW(
+          "Non-systemserver client (pid %d) trying to register as "
+          "ContextHubService",
+          kPid);
+      return pw::Status::InvalidArgument();
+    }
+    kId = info.id.hubId;
+    mManager.mIdToHostHub.insert({kId, self});
+  }
+  mIdToEndpoint.insert({id, std::make_unique<EndpointInfo>(info)});
+  return pw::OkStatus();
+}
+
+pw::Status HostHub::removeEndpoint(const EndpointId &id) {
+  std::lock_guard lock(mManager.mLock);
+  PW_TRY(checkValidLocked());
+  if (auto it = mIdToEndpoint.find(id.id); it != mIdToEndpoint.end()) {
+    mIdToEndpoint.erase(it);
+    return pw::OkStatus();
+  }
+  LOGE("Client (hub %ld, pid %d) tried to remove unknown endpoint %ld", kId,
+       kPid, id.id);
+  return pw::Status::NotFound();
+}
+
+pw::Result<std::pair<uint16_t, uint16_t>> HostHub::reserveSessionIdRange(
+    uint16_t size) {
+  std::lock_guard lock(mManager.mLock);
+  PW_TRY(checkValidLocked());
+  if (!size || size > kSessionIdMaxRange) {
+    LOGE("Client (hub %ld, pid %d) tried to allocate %hu session ids", kId,
+         kPid, size);
+    return pw::Status::InvalidArgument();
+  }
+  if (USHRT_MAX - mManager.mNextSessionId + 1 < size) {
+    LOGW("Could not allocate %hu session ids, ids exhausted", size);
+    return pw::Status::ResourceExhausted();
+  }
+  mSessionIdRanges.push_back(
+      {mManager.mNextSessionId, mManager.mNextSessionId + size - 1});
+  mManager.mNextSessionId += size;
+  return mSessionIdRanges.back();
+}
+
+pw::Result<std::shared_ptr<HostHub>> HostHub::openSession(
+    std::weak_ptr<HostHub> self, const EndpointId &localId,
+    const EndpointId &remoteId, uint16_t sessionId) {
+  std::lock_guard lock(mManager.mLock);
+  PW_TRY(checkValidLocked());
+
+  // Lookup the endpoints.
+  PW_TRY_ASSIGN(std::shared_ptr<EndpointInfo> local,
+                getEndpointLocked(localId));
+  PW_TRY_ASSIGN(std::shared_ptr<EndpointInfo> remote,
+                mManager.getEmbeddedEndpointLocked(remoteId));
+
+  // Validate the session id.
+  bool hostInitiated = AIBinder_isHandlingTransaction();
+  if (hostInitiated) {
+    if (!sessionIdInRangeLocked(sessionId)) {
+      LOGE("Session id %hu out of range for hub %ld", sessionId, kId);
+      return pw::Status::OutOfRange();
+    }
+  } else if (sessionId >= kHostSessionIdBase) {
+    LOGE(
+        "Remote endpoint (%ld, %ld) attempting to start session with "
+        "invalid id %hu",
+        remoteId.hubId, remoteId.id, sessionId);
+    return pw::Status::InvalidArgument();
+  }
+
+  // Prune a stale session with this id if present.
+  std::shared_ptr<HostHub> prunedHostHub;
+  if (auto it = mManager.mIdToSession.find(sessionId);
+      it != mManager.mIdToSession.end()) {
+    SessionStrongRef session(it->second);
+    if (session) {
+      // If the session is in a valid state, prune it if it was not host
+      // initiated and is pending a final ack from message router.
+      if (!hostInitiated && !session.pendingDestination &&
+          session.pendingMessageRouter) {
+        prunedHostHub = std::move(session.hub);
+      } else if (hostInitiated && session.local->id == localId) {
+        LOGE("Hub %ld trying to override its own session %hu", kId, sessionId);
+        return pw::Status::InvalidArgument();
+      } else {
+        LOGE("(host? %d) trying to override session id %hu, hub %ld",
+             hostInitiated, sessionId, kId);
+        return pw::Status::AlreadyExists();
+      }
+    }
+    mManager.mIdToSession.erase(it);
+  }
+
+  // Create and map the new session.
+  mManager.mIdToSession.emplace(
+      std::piecewise_construct, std::forward_as_tuple(sessionId),
+      std::forward_as_tuple(self, local, remote, hostInitiated));
+  return prunedHostHub;
+}
+
+pw::Status HostHub::closeSession(uint16_t id) {
+  std::lock_guard lock(mManager.mLock);
+  PW_TRY(checkValidLocked());
+  auto it = mManager.mIdToSession.find(id);
+  if (it == mManager.mIdToSession.end()) {
+    LOGE("Closing unopened session %hu", id);
+    return pw::Status::NotFound();
+  }
+  SessionStrongRef session(it->second);
+  if (session && session.hub->kPid != kPid) {
+    LOGE("Trying to close session %hu for client %d from client %d (hub %ld)",
+         id, session.hub->kPid, kPid, kId);
+    return pw::Status::PermissionDenied();
+  }
+  mManager.mIdToSession.erase(it);
+  return pw::OkStatus();
+}
+
+pw::Status HostHub::ackSession(uint16_t id) {
+  return mManager.ackSessionAndGetHostHub(id).status();
+}
+
+pw::Status HostHub::checkSessionOpen(uint16_t id) {
+  return mManager.checkSessionOpenAndGetHostHub(id).status();
+}
+
+int64_t HostHub::id() const {
+  std::lock_guard lock(mManager.mLock);
+  return kId;
+}
+
+int64_t MessageHubManager::HostHub::unlinkFromManager() {
+  std::lock_guard lock(mManager.mLock);
+  // TODO(b/378545373): Release the session id range.
+  if (kId != kHubIdInvalid) mManager.mIdToHostHub.erase(kId);
+  mManager.mPidToHostHub.erase(kPid);
+  mUnlinked = true;
+  return kId;
+}
+
+void HostHub::unlinkCallbackIfNecessaryLocked() {
+  if (!mCallback) return;
+  if (AIBinder_unlinkToDeath(mCallback->asBinder().get(),
+                             mManager.mDeathRecipient.get(),
+                             mCookie) != STATUS_OK) {
+    LOGW("Failed to unlink client (pid: %d, hub id: %ld)", kPid, kId);
+  }
+  mCallback.reset();
+  mCookie = nullptr;
+}
+
+pw::Status HostHub::checkValidLocked() {
+  if (!mCallback) {
+    ALOGW("Endpoint APIs invoked by client %d before callback registered",
+          kPid);
+    return pw::Status::FailedPrecondition();
+  } else if (mUnlinked) {
+    ALOGW("Client %d went down mid-operation", kPid);
+    return pw::Status::Aborted();
+  }
+  return pw::OkStatus();
+}
+
+pw::Result<std::shared_ptr<EndpointInfo>> HostHub::getEndpointLocked(
+    const EndpointId &id) {
+  if (id.hubId != kId) {
+    LOGE("Rejecting lookup on unowned endpoint (%ld, %ld) from hub %ld",
+         id.hubId, id.id, kId);
+    return pw::Status::InvalidArgument();
+  }
+  if (auto it = mIdToEndpoint.find(id.id); it != mIdToEndpoint.end())
+    return it->second;
+  return pw::Status::NotFound();
+}
+
+bool HostHub::sessionIdInRangeLocked(uint16_t id) {
+  for (auto range : mSessionIdRanges) {
+    if (id >= range.first && id <= range.second) return true;
+  }
+  return false;
+}
+
+MessageHubManager::MessageHubManager(HostHubDownCb cb)
+    : mHostHubDownCb(std::move(cb)) {
+  mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(
+      AIBinder_DeathRecipient_new(onClientDeath));
+  AIBinder_DeathRecipient_setOnUnlinked(
+      mDeathRecipient.get(), /*onUnlinked= */ [](void *cookie) {
+        LOGI("Callback is unlinked. Releasing the death recipient cookie.");
+        delete static_cast<HostHub::DeathRecipientCookie *>(cookie);
+      });
+}
+
+std::shared_ptr<HostHub> MessageHubManager::getHostHubByPid(pid_t pid) {
+  std::lock_guard lock(mLock);
+  if (auto it = mPidToHostHub.find(pid); it != mPidToHostHub.end())
+    return it->second;
+  std::shared_ptr<HostHub> hub(new HostHub(*this, pid));
+  mPidToHostHub.insert({pid, hub});
+  return hub;
+}
+
+std::shared_ptr<HostHub> MessageHubManager::getHostHubByEndpointId(
+    const EndpointId &id) {
+  std::lock_guard lock(mLock);
+  if (auto it = mIdToHostHub.find(id.hubId); it != mIdToHostHub.end())
+    return it->second.lock();
+  return {};
+}
+
+pw::Result<std::shared_ptr<HostHub>>
+MessageHubManager::checkSessionOpenAndGetHostHub(uint16_t id) {
+  std::lock_guard lock(mLock);
+  PW_TRY_ASSIGN(SessionStrongRef session, checkSessionLocked(id));
+  if (AIBinder_getCallingPid() != session.hub->kPid) {
+    LOGE("Trying to check unowned session %hu", id);
+    return pw::Status::PermissionDenied();
+  }
+  if (!session.pendingDestination && !session.pendingMessageRouter)
+    return std::move(session.hub);
+  LOGE("Session %hu is pending", id);
+  return pw::Status::FailedPrecondition();
+}
+
+pw::Result<std::shared_ptr<HostHub>> MessageHubManager::ackSessionAndGetHostHub(
+    uint16_t id) {
+  std::lock_guard lock(mLock);
+  PW_TRY_ASSIGN(SessionStrongRef session, checkSessionLocked(id));
+  bool isBinderCall = AIBinder_isHandlingTransaction();
+  bool isHostSession = id >= kHostSessionIdBase;
+  if (isBinderCall && AIBinder_getCallingPid() != session.hub->kPid) {
+    LOGE("Trying to ack unowned session %hu", id);
+    return pw::Status::PermissionDenied();
+  } else if (session.pendingDestination) {
+    if (isHostSession == isBinderCall) {
+      LOGE("Session %hu must be acked by other side (host? %d)", id,
+           !isBinderCall);
+      return pw::Status::PermissionDenied();
+    }
+    session.pendingDestination = false;
+  } else if (session.pendingMessageRouter) {
+    if (isBinderCall) {
+      LOGE("Message router must ack session %hu", id);
+      return pw::Status::PermissionDenied();
+    }
+    session.pendingMessageRouter = false;
+  } else {
+    LOGE("Received unexpected ack on session %hu, host: %d", id, isBinderCall);
+  }
+  return std::move(session.hub);
+}
+
+void MessageHubManager::forEachHostHub(std::function<void(HostHub &hub)> fn) {
+  std::list<std::shared_ptr<HostHub>> hubs;
+  {
+    std::lock_guard lock(mLock);
+    for (auto &[pid, hub] : mPidToHostHub) hubs.push_back(hub);
+  }
+  for (auto &hub : hubs) fn(*hub);
+}
+
+pw::Result<MessageHubManager::SessionStrongRef>
+MessageHubManager::checkSessionLocked(uint16_t id) {
+  auto sessionIt = mIdToSession.find(id);
+  if (sessionIt == mIdToSession.end()) {
+    LOGE("Did not find expected session %hu", id);
+    return pw::Status::NotFound();
+  }
+  SessionStrongRef session(sessionIt->second);
+  if (!session) {
+    LOGD(
+        "Pruning session %hu due to one or more of host hub, host endpoint, "
+        "or embedded endpoint going down.",
+        id);
+    mIdToSession.erase(sessionIt);
+    return pw::Status::Unavailable();
+  }
+  return std::move(session);
+}
+
+void MessageHubManager::initEmbeddedHubsAndEndpoints(
+    const std::vector<HubInfo> &hubs,
+    const std::vector<EndpointInfo> &endpoints) {
+  std::lock_guard lock(mLock);
+  mIdToEmbeddedHub.clear();
+  for (const auto &hub : hubs) mIdToEmbeddedHub[hub.hubId].info = hub;
+  for (const auto &endpoint : endpoints) addEmbeddedEndpointLocked(endpoint);
+}
+
+void MessageHubManager::addEmbeddedHub(const HubInfo &hub) {
+  std::lock_guard lock(mLock);
+  if (mIdToEmbeddedHub.count(hub.hubId)) return;
+  mIdToEmbeddedHub[hub.hubId].info = hub;
+}
+
+std::vector<EndpointId> MessageHubManager::removeEmbeddedHub(int64_t id) {
+  std::lock_guard lock(mLock);
+  std::vector<EndpointId> endpoints;
+  auto it = mIdToEmbeddedHub.find(id);
+  if (it != mIdToEmbeddedHub.end()) {
+    for (const auto &[endpointId, info] : it->second.idToEndpoint)
+      endpoints.push_back({.id = endpointId, .hubId = id});
+    mIdToEmbeddedHub.erase(it);
+  }
+  return endpoints;
+}
+
+std::vector<HubInfo> MessageHubManager::getEmbeddedHubs() const {
+  std::lock_guard lock(mLock);
+  std::vector<HubInfo> hubs;
+  for (const auto &[id, hub] : mIdToEmbeddedHub) hubs.push_back(hub.info);
+  return hubs;
+}
+
+void MessageHubManager::addEmbeddedEndpoint(const EndpointInfo &endpoint) {
+  std::lock_guard lock(mLock);
+  addEmbeddedEndpointLocked(endpoint);
+}
+
+std::vector<EndpointInfo> MessageHubManager::getEmbeddedEndpoints() const {
+  std::lock_guard lock(mLock);
+  std::vector<EndpointInfo> endpoints;
+  for (const auto &[id, hub] : mIdToEmbeddedHub) {
+    for (const auto &[endptId, endptInfo] : hub.idToEndpoint)
+      endpoints.push_back(*endptInfo);
+  }
+  return endpoints;
+}
+
+void MessageHubManager::onClientDeath(void *cookie) {
+  auto *cookieData = reinterpret_cast<HostHub::DeathRecipientCookie *>(cookie);
+  MessageHubManager *manager = cookieData->manager;
+  std::shared_ptr<HostHub> hub = manager->getHostHubByPid(cookieData->pid);
+  LOGW("Hub %ld (pid %d) died", hub->id(), cookieData->pid);
+  manager->mHostHubDownCb(hub->unlinkFromManager());
+}
+
+void MessageHubManager::addEmbeddedEndpointLocked(
+    const EndpointInfo &endpoint) {
+  auto it = mIdToEmbeddedHub.find(endpoint.id.hubId);
+  if (it == mIdToEmbeddedHub.end()) {
+    LOGW("Could not find hub %ld for endpoint %ld", endpoint.id.hubId,
+         endpoint.id.id);
+    return;
+  }
+  it->second.idToEndpoint.insert(
+      {endpoint.id.id, std::make_shared<EndpointInfo>(endpoint)});
+}
+
+pw::Result<std::shared_ptr<EndpointInfo>>
+MessageHubManager::getEmbeddedEndpointLocked(const EndpointId &id) {
+  auto hubIt = mIdToEmbeddedHub.find(id.hubId);
+  if (hubIt != mIdToEmbeddedHub.end()) {
+    auto it = hubIt->second.idToEndpoint.find(id.id);
+    if (it != hubIt->second.idToEndpoint.end()) return it->second;
+  }
+  LOGW("Could not find remote endpoint (%ld, %ld)", id.hubId, id.id);
+  return pw::Status::NotFound();
+}
+
+}  // namespace android::hardware::contexthub::common::implementation
diff --git a/host/hal_generic/common/message_hub_manager.h b/host/hal_generic/common/message_hub_manager.h
new file mode 100644
index 0000000..5353375
--- /dev/null
+++ b/host/hal_generic/common/message_hub_manager.h
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unistd.h>
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/contexthub/BnContextHub.h>
+#include <android-base/thread_annotations.h>
+
+#include "pw_result/result.h"
+#include "pw_status/status.h"
+
+namespace android::hardware::contexthub::common::implementation {
+
+using ::aidl::android::hardware::contexthub::EndpointId;
+using ::aidl::android::hardware::contexthub::EndpointInfo;
+using ::aidl::android::hardware::contexthub::HubInfo;
+using ::aidl::android::hardware::contexthub::IEndpointCallback;
+
+/**
+ * Stores host and embedded MessageHub objects and maintains global mappings.
+ */
+class MessageHubManager {
+ public:
+  /**
+   * Represents a host-side MessageHub. Clients of the IContextHub (V4+)
+   * interface each get a HostHub instance.
+   */
+  class HostHub {
+   public:
+    ~HostHub();
+
+    /**
+     * Sets the callback for sending endpoint events back to the HAL client
+     *
+     * @param callback The callback provided by the client
+     * @return pw::OkStatus() on success.
+     */
+    pw::Status setCallback(std::shared_ptr<IEndpointCallback> callback)
+        EXCLUDES(mManager.mLock);
+
+    /**
+     * Returns the callback registered in setCallback()
+     *
+     * @return The previously registered callback
+     */
+    std::shared_ptr<IEndpointCallback> getCallback() const
+        EXCLUDES(mManager.mLock);
+
+    /**
+     * Adds an endpoint to this message hub
+     *
+     * @param self Self-reference for mapping from hub id
+     * @param info Description of the endpoint
+     * @return pw::OkStatus() on success
+     */
+    pw::Status addEndpoint(std::weak_ptr<HostHub> self,
+                           const EndpointInfo &info) EXCLUDES(mManager.mLock);
+
+    /**
+     * Removes an endpoint from this message hub
+     *
+     * @param info Id of endpoint to remove
+     * @return pw::OkStatus() on success
+     */
+    pw::Status removeEndpoint(const EndpointId &info) EXCLUDES(mManager.mLock);
+
+    /**
+     * Reserves a session id range to be used by this message hub
+     *
+     * @param size The size of this range, max 1024
+     * @return A pair of the smallest and largest id in the range on success
+     */
+    pw::Result<std::pair<uint16_t, uint16_t>> reserveSessionIdRange(
+        uint16_t size) EXCLUDES(mManager.mLock);
+
+    /**
+     * Opens a session between the given endpoints with given session id
+     *
+     * The session is pending until updated by the destination endpoint.
+     *
+     * @param self Self-reference to be stored in session state
+     * @param localId The id of an endpoint hosted by this hub
+     * @param remoteId The id of the remote endpoint
+     * @param sessionId The id to be used for this session. Must be in the range
+     * allocated to this hub
+     * @return On success, returns a possibly null reference to the HostHub
+     * which hosted an endpoint on a pruned session with the same id. If not
+     * null, the HostHub should be notified that the session has been closed.
+     */
+    pw::Result<std::shared_ptr<HostHub>> openSession(
+        std::weak_ptr<HostHub> self, const EndpointId &localId,
+        const EndpointId &remoteId, uint16_t sessionId)
+        EXCLUDES(mManager.mLock);
+
+    /**
+     * Acks a pending session.
+     *
+     * @param id Session id
+     * @return pw::OkStatus() on success, pw::Status::Unavailable() if the
+     * session is gone due to an endpoint going down.
+     */
+    pw::Status ackSession(uint16_t id) EXCLUDES(mManager.mLock);
+
+    /**
+     * Checks that a session is open.
+     *
+     * @param id Session id
+     * @return pw::OkStatus() on success, pw::Status::Unavailable() if the
+     * session is gone due to an endpoint going down.
+     */
+    pw::Status checkSessionOpen(uint16_t id) EXCLUDES(mManager.mLock);
+
+    /**
+     * Removes the given session and any local and global mappings
+     *
+     * @param id The session id
+     * @return pw::OkStatus() on success
+     */
+    pw::Status closeSession(uint16_t id) EXCLUDES(mManager.mLock);
+
+    /**
+     * Returns the registered id of this message hub.
+     *
+     * @return kId
+     */
+    int64_t id() const;
+
+   private:
+    friend class MessageHubManager;
+
+    // Cookie associated with each registered client callback.
+    struct DeathRecipientCookie {
+      MessageHubManager *manager;
+      pid_t pid;
+    };
+
+    static constexpr uint16_t kSessionIdMaxRange = 1024;
+
+    static constexpr int64_t kHubIdInvalid = 0;
+
+    HostHub(MessageHubManager &manager, pid_t pid)
+        : mManager(manager), kPid(pid) {}
+
+    // Unlinks this hub from the manager, destroying internal references.
+    // Returns the id so that it can be propagated to CHRE.
+    int64_t unlinkFromManager() EXCLUDES(mManager.mLock);
+
+    // Unlink the current callback from the manager's death recipient.
+    void unlinkCallbackIfNecessaryLocked() REQUIRES(mManager.mLock);
+
+    // Returns pw::OkStatus() if the hub is in a valid state.
+    pw::Status checkValidLocked() REQUIRES(mManager.mLock);
+
+    // Returns a shared_ptr to the given endpoint.
+    pw::Result<std::shared_ptr<EndpointInfo>> getEndpointLocked(
+        const EndpointId &id) REQUIRES(mManager.mLock);
+
+    // Returns pw::OkStatus() if the session id is in range for this hub.
+    bool sessionIdInRangeLocked(uint16_t id) REQUIRES(mManager.mLock);
+
+    MessageHubManager &mManager;
+    const pid_t kPid;
+
+    // Hub id, set when the first endpoint is registered.
+    int64_t kId GUARDED_BY(mManager.mLock) = kHubIdInvalid;
+
+    // Callback to HAL client.
+    std::shared_ptr<IEndpointCallback> mCallback GUARDED_BY(mManager.mLock);
+
+    // Cookie associated with mCallback.
+    DeathRecipientCookie *mCookie GUARDED_BY(mManager.mLock);
+
+    // Used to lookup a host endpoint. Owns the associated EndpointInfo.
+    std::unordered_map<int64_t, std::shared_ptr<EndpointInfo>> mIdToEndpoint
+        GUARDED_BY(mManager.mLock);
+
+    // Session id ranges allocated to this HostHub. The ranges are stored as a
+    // pair of the lowest and highest id in the range.
+    std::vector<std::pair<uint16_t, uint16_t>> mSessionIdRanges
+        GUARDED_BY(mManager.mLock);
+
+    // Set in unlinkFromManager().
+    bool mUnlinked GUARDED_BY(mManager.mLock) = false;
+  };
+
+  // Callback registered to pass up the id of a host hub which disconnected.
+  using HostHubDownCb = std::function<void(int64_t hubId)>;
+
+  // The base session id for sessions initiated from host endpoints.
+  static constexpr uint16_t kHostSessionIdBase = 0x8000;
+
+  explicit MessageHubManager(HostHubDownCb cb);
+  ~MessageHubManager() = default;
+
+  /**
+   * Retrieves the HostHub instance for the calling process
+   *
+   * This API should be used for any HostHub lookup coming from the
+   * IContextHub interface. The first call to this API by any client process
+   * will trigger the creation of a HostHub for that client.
+   *
+   * @param pid The caller's system process id
+   * @return shared_ptr to the HostHub instance
+   */
+  std::shared_ptr<HostHub> getHostHubByPid(pid_t pid) EXCLUDES(mLock);
+
+  /**
+   * Retrieves the HostHub instance for the given EndpointId
+   *
+   * @param id The endpoint id hosted by the returned hub
+   * @return shared_ptr to the HostHub instance
+   */
+  std::shared_ptr<HostHub> getHostHubByEndpointId(const EndpointId &id)
+      EXCLUDES(mLock);
+
+  /**
+   * Checks that a given session is open and returns its HostHub.
+   *
+   * @param id Session id
+   * @return A strong reference to the HostHub. pw::Status::Unavailable()
+   * indicates that the session has been pruned.
+   */
+  pw::Result<std::shared_ptr<HostHub>> checkSessionOpenAndGetHostHub(
+      uint16_t id) EXCLUDES(mLock);
+
+  /**
+   * Acks a session open request.
+   *
+   * This is called both when the destination endpoint approves and also when
+   * MessageRouter gives a final ack on a session initiated from an embedded
+   * endpoint. See the documentation on the Session class.
+   *
+   * @param id Session id
+   * @return A strong reference to the HostHub. pw::Status::Unavailable()
+   * indicates that the session has been pruned.
+   */
+  pw::Result<std::shared_ptr<HostHub>> ackSessionAndGetHostHub(uint16_t id)
+      EXCLUDES(mLock);
+
+  /**
+   * Apply the given function to each host hub.
+   *
+   * @param fn The function to apply.
+   */
+  void forEachHostHub(std::function<void(HostHub &hub)> fn);
+
+  /**
+   * Wipes and initializes the cache of embedded hubs and endpoints
+   *
+   * This should only be called once during startup as it invalidates session
+   * state (i.e. existing sessions will be pruned).
+   *
+   * @param hubs The list of message hubs
+   * @param endpoints The list of endpoints
+   */
+  void initEmbeddedHubsAndEndpoints(const std::vector<HubInfo> &hubs,
+                                    const std::vector<EndpointInfo> &endpoints)
+      EXCLUDES(mLock);
+
+  /**
+   * Adds the given hub to the cache
+   *
+   * Ignored if the hub already exists
+   *
+   * @param hub The hub to add
+   */
+  void addEmbeddedHub(const HubInfo &hub) EXCLUDES(mLock);
+
+  /**
+   * Removes the hub with given id from the cache
+   *
+   * @param id The id of the hub to remove
+   * @return The ids of all endpoints on the embedded hub
+   */
+  std::vector<EndpointId> removeEmbeddedHub(int64_t id) EXCLUDES(mLock);
+
+  /**
+   * Returns the cached list of embedded message hubs
+   *
+   * @return HubInfo for every embedded message hub
+   */
+  std::vector<HubInfo> getEmbeddedHubs() const EXCLUDES(mLock);
+
+  /**
+   * Adds an embedded endpoint to the cache
+   *
+   * Ignored if the endpoint already exists
+   *
+   * @param endpoint The endpoint to add
+   */
+  void addEmbeddedEndpoint(const EndpointInfo &endpoint);
+
+  /**
+   * Removes an embedded endpoint from the cache
+   *
+   * @param id The id of the endpoint to remove
+   */
+  void removeEmbeddedEndpoint(const EndpointId &endpoint);
+
+  /**
+   * Returns a list of embedded endpoints
+   *
+   * @return EndpointInfo for every embedded endpoint
+   */
+  std::vector<EndpointInfo> getEmbeddedEndpoints() const EXCLUDES(mLock);
+
+ private:
+  // Callback invoked when a client goes down.
+  using UnlinkToDeathFn = std::function<bool(
+      const std::shared_ptr<IEndpointCallback> &callback, void *cookie)>;
+
+  // Represents an embedded MessageHub. Stores the hub details as well as a map
+  // of all endpoints hosted by the hub.
+  struct EmbeddedHub {
+    std::unordered_map<int64_t, std::shared_ptr<EndpointInfo>> idToEndpoint;
+    HubInfo info;
+  };
+
+  // Represents a session between a host and embedded endpoint. Only stores weak
+  // references to the endpoints and HostHub owning the host endpoint. Must be
+  // converted to a SessionStrongRef to temporarily access state. The weak
+  // references expire when the associated entity is unregistered. A
+  // SessionStrongRef cannot be created if any reference has expired.
+  //
+  // A Session is created on an openSession() request (triggered either by a
+  // local or remote endpoint) with mPendingDestination unset via a call to
+  // ackSession*() from the destination endpoint. For Sessions started by
+  // embedded endpoints, an additional ackSession*() must be received from the
+  // CHRE MessageRouter after passing it the ack from the destination host
+  // endpoint. This unsets mPendingMessageRouter. A session is only open for
+  // messages once both mPendingDestination and mPendingMessageRouter are unset.
+  struct SessionStrongRef;
+  class Session {
+   public:
+    Session(std::weak_ptr<HostHub> hub, std::weak_ptr<EndpointInfo> local,
+            std::weak_ptr<EndpointInfo> remote, bool hostInitiated)
+        : mHub(hub),
+          mLocal(local),
+          mRemote(remote),
+          mPendingMessageRouter(!hostInitiated) {}
+
+   private:
+    friend struct SessionStrongRef;
+
+    std::weak_ptr<HostHub> mHub;
+    std::weak_ptr<EndpointInfo> mLocal;
+    std::weak_ptr<EndpointInfo> mRemote;
+    bool mPendingDestination = true;
+    bool mPendingMessageRouter;
+  };
+
+  // A strong reference to a Session's underlying endpoints and HostHub as well
+  // as Session metadata. A SessionStrongRef should be created and destroyed
+  // within a single critical section.
+  struct SessionStrongRef {
+    std::shared_ptr<HostHub> hub;
+    std::shared_ptr<EndpointInfo> local;
+    std::shared_ptr<EndpointInfo> remote;
+    bool &pendingDestination;
+    bool &pendingMessageRouter;
+
+    SessionStrongRef(Session &session)
+        : hub(session.mHub.lock()),
+          local(session.mLocal.lock()),
+          remote(session.mRemote.lock()),
+          pendingDestination(session.mPendingDestination),
+          pendingMessageRouter(session.mPendingMessageRouter) {}
+    operator bool() const {
+      return hub && local && remote;
+    }
+  };
+
+  // The hub id reserved for the ContextHub service.
+  static constexpr int64_t kContextHubServiceHubId = 0x416e64726f696400;
+
+  // The Linux uid of the system_server.
+  static constexpr uid_t kSystemServerUid = 1000;
+
+  // Invoked on client death. Cleans up references to the client.
+  static void onClientDeath(void *cookie);
+
+  // Retrieves a strong reference to the session with given id.
+  pw::Result<SessionStrongRef> checkSessionLocked(uint16_t id) REQUIRES(mLock);
+
+  // Adds an embedded endpoint to the cache.
+  void addEmbeddedEndpointLocked(const EndpointInfo &endpoint) REQUIRES(mLock);
+
+  // Returns true if the embedded endpoint with given id is in the cache.
+  pw::Result<std::shared_ptr<EndpointInfo>> getEmbeddedEndpointLocked(
+      const EndpointId &id) REQUIRES(mLock);
+
+  // Callback to pass up the id of a host hub for a client that disconnected.
+  HostHubDownCb mHostHubDownCb;
+
+  // Death recipient handling clients' disconnections.
+  ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+
+  // Guards hub, endpoint, and session state.
+  mutable std::mutex mLock;
+
+  // Map of EmbeddedHubs.
+  std::unordered_map<int64_t, EmbeddedHub> mIdToEmbeddedHub GUARDED_BY(mLock);
+
+  // Used to look up the HostHub associated with the client on IContextHub
+  // calls.
+  std::unordered_map<pid_t, std::shared_ptr<HostHub>> mPidToHostHub
+      GUARDED_BY(mLock);
+
+  // Used when an embedded endpoint wants to start a session with an endpoint
+  // hosted by a specific HostHub.
+  std::unordered_map<int64_t, std::weak_ptr<HostHub>> mIdToHostHub
+      GUARDED_BY(mLock);
+
+  // Used to lookup the host endpoint to receive a message on an endpoint
+  // session.
+  std::unordered_map<uint16_t, Session> mIdToSession GUARDED_BY(mLock);
+
+  // Next session id from which to allocate ranges.
+  uint16_t mNextSessionId GUARDED_BY(mLock) = kHostSessionIdBase;
+};
+
+}  // namespace android::hardware::contexthub::common::implementation
diff --git a/host/hal_generic/common/multi_client_context_hub_base.cc b/host/hal_generic/common/multi_client_context_hub_base.cc
index 8eb92eb..356de2a 100644
--- a/host/hal_generic/common/multi_client_context_hub_base.cc
+++ b/host/hal_generic/common/multi_client_context_hub_base.cc
@@ -39,7 +39,6 @@
 using ::android::chre::Atoms::ChreHalNanoappLoadFailed;
 using ::android::chre::flags::abort_if_no_context_hub_found;
 using ::android::chre::flags::bug_fix_hal_reliable_message_record;
-using ::android::chre::flags::reliable_message_implementation;
 using ::ndk::ScopedAStatus;
 namespace fbs = ::chre::fbs;
 
@@ -172,6 +171,12 @@
 
 ScopedAStatus MultiClientContextHubBase::getContextHubs(
     std::vector<ContextHubInfo> *contextHubInfos) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    // Return ok() here to not crash system server
+    return ScopedAStatus::ok();
+  }
+
   std::unique_lock<std::mutex> lock(mHubInfoMutex);
   if (mContextHubInfo == nullptr) {
     fbs::HubInfoResponseT response;
@@ -200,10 +205,15 @@
 ScopedAStatus MultiClientContextHubBase::loadNanoapp(
     int32_t contextHubId, const NanoappBinary &appBinary,
     int32_t transactionId) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   if (!isValidContextHubId(contextHubId)) {
     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
   }
-  LOGD("Loading nanoapp 0x%" PRIx64, appBinary.nanoappId);
+  LOGD("Loading nanoapp 0x%" PRIx64 ", transaction id=%" PRIi32,
+       appBinary.nanoappId, transactionId);
   uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
                               (appBinary.targetChreApiMinorVersion << 16);
   auto nanoappBuffer =
@@ -254,6 +264,10 @@
 ScopedAStatus MultiClientContextHubBase::unloadNanoapp(int32_t contextHubId,
                                                        int64_t appId,
                                                        int32_t transactionId) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   if (!isValidContextHubId(contextHubId)) {
     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
   }
@@ -296,6 +310,10 @@
 
 ScopedAStatus MultiClientContextHubBase::onSettingChanged(Setting setting,
                                                           bool enabled) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   mSettingEnabled[setting] = enabled;
   fbs::Setting fbsSetting;
   bool isWifiOrBtSetting =
@@ -346,6 +364,10 @@
 }
 
 ScopedAStatus MultiClientContextHubBase::queryNanoapps(int32_t contextHubId) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   if (!isValidContextHubId(contextHubId)) {
     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
   }
@@ -380,6 +402,8 @@
 ScopedAStatus MultiClientContextHubBase::registerCallback(
     int32_t contextHubId,
     const std::shared_ptr<IContextHubCallback> &callback) {
+  // Even CHRE is not ready we should open this API to clients because it allows
+  // us to have a channel to report events back to them.
   if (!isValidContextHubId(contextHubId)) {
     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
   }
@@ -406,6 +430,10 @@
 
 ScopedAStatus MultiClientContextHubBase::sendMessageToHub(
     int32_t contextHubId, const ContextHubMessage &message) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   if (!isValidContextHubId(contextHubId)) {
     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
   }
@@ -416,7 +444,7 @@
     return fromResult(false);
   }
 
-  if (reliable_message_implementation() && message.isReliable) {
+  if (message.isReliable) {
     if (bug_fix_hal_reliable_message_record()) {
       std::lock_guard<std::mutex> lock(mReliableMessageMutex);
       auto iter = std::find_if(
@@ -439,19 +467,12 @@
   }
 
   flatbuffers::FlatBufferBuilder builder(1024);
-  if (reliable_message_implementation()) {
-    HostProtocolHost::encodeNanoappMessage(
-        builder, message.nanoappId, message.messageType, hostEndpointId,
-        message.messageBody.data(), message.messageBody.size(),
-        /* permissions= */ 0,
-        /* messagePermissions= */ 0,
-        /* wokeHost= */ false, message.isReliable,
-        message.messageSequenceNumber);
-  } else {
-    HostProtocolHost::encodeNanoappMessage(
-        builder, message.nanoappId, message.messageType, hostEndpointId,
-        message.messageBody.data(), message.messageBody.size());
-  }
+  HostProtocolHost::encodeNanoappMessage(
+      builder, message.nanoappId, message.messageType, hostEndpointId,
+      message.messageBody.data(), message.messageBody.size(),
+      /* permissions= */ 0,
+      /* messagePermissions= */ 0,
+      /* wokeHost= */ false, message.isReliable, message.messageSequenceNumber);
 
   bool success = mConnection->sendMessage(builder);
   mEventLogger.logMessageToNanoapp(message, success);
@@ -460,6 +481,10 @@
 
 ScopedAStatus MultiClientContextHubBase::onHostEndpointConnected(
     const HostEndpointInfo &info) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   uint8_t type;
   switch (info.type) {
     case HostEndpointInfo::Type::APP:
@@ -491,6 +516,10 @@
 
 ScopedAStatus MultiClientContextHubBase::onHostEndpointDisconnected(
     char16_t in_hostEndpointId) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   HostEndpointId hostEndpointId = in_hostEndpointId;
   pid_t pid = AIBinder_getCallingPid();
   bool isSuccessful = false;
@@ -509,11 +538,19 @@
 
 ScopedAStatus MultiClientContextHubBase::onNanSessionStateChanged(
     const NanSessionStateUpdate & /*in_update*/) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   // TODO(271471342): Add support for NAN session management.
   return ndk::ScopedAStatus::ok();
 }
 
 ScopedAStatus MultiClientContextHubBase::setTestMode(bool enable) {
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
+  }
   if (enable) {
     return fromResult(enableTestMode());
   }
@@ -523,10 +560,10 @@
 
 ScopedAStatus MultiClientContextHubBase::sendMessageDeliveryStatusToHub(
     int32_t contextHubId, const MessageDeliveryStatus &messageDeliveryStatus) {
-  if (!reliable_message_implementation()) {
-    return ScopedAStatus::ok();
+  if (!mIsChreReady) {
+    LOGE("%s() can't be processed as CHRE is not ready", __func__);
+    return fromServiceError(HalError::CHRE_NOT_READY);
   }
-
   if (!isValidContextHubId(contextHubId)) {
     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
   }
@@ -543,6 +580,77 @@
   return fromResult(success);
 }
 
+ScopedAStatus MultiClientContextHubBase::getHubs(std::vector<HubInfo> *hubs) {
+  if (mV4Impl) return mV4Impl->getHubs(hubs);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::getEndpoints(
+    std::vector<EndpointInfo> *endpoints) {
+  if (mV4Impl) return mV4Impl->getEndpoints(endpoints);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::registerEndpoint(
+    const EndpointInfo &endpoint) {
+  if (mV4Impl) return mV4Impl->registerEndpoint(endpoint);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::unregisterEndpoint(
+    const EndpointInfo &endpoint) {
+  if (mV4Impl) return mV4Impl->unregisterEndpoint(endpoint);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::registerEndpointCallback(
+    const std::shared_ptr<IEndpointCallback> &callback) {
+  if (mV4Impl) return mV4Impl->registerEndpointCallback(callback);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::requestSessionIdRange(
+    int32_t size, std::vector<int32_t> *ids) {
+  if (mV4Impl) return mV4Impl->requestSessionIdRange(size, ids);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::openEndpointSession(
+    int32_t sessionId, const EndpointId &destination,
+    const EndpointId &initiator,
+    const std::optional<std::string> &serviceDescriptor) {
+  if (mV4Impl) {
+    return mV4Impl->openEndpointSession(sessionId, destination, initiator,
+                                        serviceDescriptor);
+  }
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::sendMessageToEndpoint(
+    int32_t sessionId, const Message &msg) {
+  if (mV4Impl) return mV4Impl->sendMessageToEndpoint(sessionId, msg);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::sendMessageDeliveryStatusToEndpoint(
+    int32_t sessionId, const MessageDeliveryStatus &msgStatus) {
+  if (mV4Impl)
+    return mV4Impl->sendMessageDeliveryStatusToEndpoint(sessionId, msgStatus);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::closeEndpointSession(int32_t sessionId,
+                                                              Reason reason) {
+  if (mV4Impl) return mV4Impl->closeEndpointSession(sessionId, reason);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus MultiClientContextHubBase::endpointSessionOpenComplete(
+    int32_t sessionId) {
+  if (mV4Impl) return mV4Impl->endpointSessionOpenComplete(sessionId);
+  return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
 bool MultiClientContextHubBase::enableTestMode() {
   std::unique_lock<std::mutex> lock(mTestModeMutex);
   if (mIsTestModeEnabled) {
@@ -688,8 +796,12 @@
       break;
     }
     default:
-      LOGW("Got unexpected message type %" PRIu8,
-           static_cast<uint8_t>(message.type));
+      if (mV4Impl) {
+        mV4Impl->handleMessageFromChre(message);
+      } else {
+        LOGW("Got unexpected message type %" PRIu8,
+             static_cast<uint8_t>(message.type));
+      }
   }
 }
 
@@ -711,7 +823,7 @@
   mContextHubInfo->supportedPermissions = kSupportedPermissions;
 
   mContextHubInfo->supportsReliableMessages =
-      reliable_message_implementation() && response.supports_reliable_messages;
+      response.supports_reliable_messages;
 
   mHubInfoCondition.notify_all();
 }
@@ -889,14 +1001,8 @@
   outMessage.messageType = message.message_type;
   outMessage.messageBody = message.message;
   outMessage.permissions = chreToAndroidPermissions(message.permissions);
-
-  if (reliable_message_implementation()) {
-    outMessage.isReliable = message.is_reliable;
-    outMessage.messageSequenceNumber = message.message_sequence_number;
-  } else {
-    outMessage.isReliable = false;
-    outMessage.messageSequenceNumber = 0;
-  }
+  outMessage.isReliable = message.is_reliable;
+  outMessage.messageSequenceNumber = message.message_sequence_number;
 
   std::string messageSeq = "reliable message seq=" +
                            std::to_string(outMessage.messageSequenceNumber);
@@ -927,10 +1033,6 @@
 
 void MultiClientContextHubBase::onMessageDeliveryStatus(
     const ::chre::fbs::MessageDeliveryStatusT &status) {
-  if (!reliable_message_implementation()) {
-    return;
-  }
-
   HostEndpointId hostEndpointId;
   if (bug_fix_hal_reliable_message_record()) {
     {
@@ -1007,6 +1109,15 @@
   mIsWifiAvailable.reset();
   mEventLogger.logContextHubRestart();
   mHalClientManager->handleChreRestart();
+
+  // Unblock APIs BEFORE informing the clients that CHRE has restarted so that
+  // any API call triggered by handleContextHubAsyncEvent() can come through.
+  mIsChreReady = true;
+  std::vector<std::shared_ptr<IContextHubCallback>> callbacks =
+      mHalClientManager->getCallbacks();
+  for (auto callback : callbacks) {
+    callback->handleContextHubAsyncEvent(AsyncEventType::RESTARTED);
+  }
 }
 
 binder_status_t MultiClientContextHubBase::dump(int fd,
diff --git a/host/hal_generic/common/multi_client_context_hub_base.h b/host/hal_generic/common/multi_client_context_hub_base.h
index c596089..ea7b6f3 100644
--- a/host/hal_generic/common/multi_client_context_hub_base.h
+++ b/host/hal_generic/common/multi_client_context_hub_base.h
@@ -26,6 +26,7 @@
 #include "chre_host/napp_header.h"
 #include "chre_host/preloaded_nanoapp_loader.h"
 #include "chre_host/time_syncer.h"
+#include "context_hub_v4_impl.h"
 #include "debug_dump_helper.h"
 #include "event_logger.h"
 #include "hal_client_id.h"
@@ -33,9 +34,11 @@
 
 #include <chrono>
 #include <deque>
+#include <memory>
 #include <mutex>
 #include <optional>
 #include <unordered_map>
+#include <vector>
 
 namespace android::hardware::contexthub::common::implementation {
 
@@ -89,11 +92,32 @@
   ScopedAStatus sendMessageDeliveryStatusToHub(
       int32_t contextHubId,
       const MessageDeliveryStatus &messageDeliveryStatus) override;
+  ScopedAStatus getHubs(std::vector<HubInfo> *hubs) override;
+  ScopedAStatus getEndpoints(std::vector<EndpointInfo> *endpoints) override;
+  ScopedAStatus registerEndpoint(const EndpointInfo &endpoint) override;
+  ScopedAStatus unregisterEndpoint(const EndpointInfo &endpoint) override;
+  ScopedAStatus registerEndpointCallback(
+      const std::shared_ptr<IEndpointCallback> &callback) override;
+  ScopedAStatus requestSessionIdRange(int32_t size,
+                                      std::vector<int32_t> *ids) override;
+  ScopedAStatus openEndpointSession(
+      int32_t sessionId, const EndpointId &destination,
+      const EndpointId &initiator,
+      const std::optional<std::string> &serviceDescriptor) override;
+  ScopedAStatus sendMessageToEndpoint(int32_t sessionId,
+                                      const Message &msg) override;
+  ScopedAStatus sendMessageDeliveryStatusToEndpoint(
+      int32_t sessionId, const MessageDeliveryStatus &msgStatus) override;
+  ScopedAStatus closeEndpointSession(int32_t sessionId, Reason reason) override;
+  ScopedAStatus endpointSessionOpenComplete(int32_t sessionId) override;
 
   // Functions implementing ChreConnectionCallback.
   void handleMessageFromChre(const unsigned char *messageBuffer,
                              size_t messageLen) override;
   void onChreRestarted() override;
+  void onChreDisconnected() override {
+    mIsChreReady = false;
+  }
 
   // Functions for dumping debug information.
   binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
@@ -197,6 +221,10 @@
   // one instance of a HalClientManager.
   std::unique_ptr<HalClientManager> mHalClientManager{};
 
+  // Implementation of the V4+ API. Should be instantiated by the target HAL
+  // implementation.
+  std::optional<ContextHubV4Impl> mV4Impl{};
+
   std::unique_ptr<PreloadedNanoappLoader> mPreloadedNanoappLoader{};
 
   std::unique_ptr<ContextHubInfo> mContextHubInfo;
@@ -242,6 +270,12 @@
   std::mutex mReliableMessageMutex;
   std::deque<ReliableMessageRecord> mReliableMessageQueue;
 
+  // A thread safe flag indicating if CHRE is ready for operations.
+  // Outside of the constructor, this boolean flag should only be written by
+  // onChreDisconnected and onChreRestarted, the order of which should be
+  // guaranteed by the CHRE's disconnection handler.
+  std::atomic_bool mIsChreReady = true;
+
   // TODO(b/333567700): Remove when cleaning up the bug_fix_hal_reliable_message_record flag
   std::unordered_map<int32_t, HostEndpointId> mReliableMessageMap;
 };
diff --git a/host/test/hal_generic/common/hal_client_manager_test.cc b/host/test/hal_generic/common/hal_client_manager_test.cc
index e40e249..7a57a2c 100644
--- a/host/test/hal_generic/common/hal_client_manager_test.cc
+++ b/host/test/hal_generic/common/hal_client_manager_test.cc
@@ -535,7 +535,7 @@
   EXPECT_THAT(client.endpointIds, IsEmpty());
 }
 
-TEST_F(HalClientManagerTest, handleChreRestartForConnectedClientsOnly) {
+TEST_F(HalClientManagerTest, handleChreRestart) {
   auto halClientManager = std::make_unique<HalClientManagerForTest>(
       mockDeadClientUnlinker, kClientIdMappingFilePath);
   std::shared_ptr<ContextHubCallbackForTest> vendorCallback =
@@ -550,17 +550,35 @@
   EXPECT_TRUE(halClientManager->registerCallback(
       kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr));
 
-  // Only connected clients' handleContextHubAsyncEvent should be called.
+  // Calls to clients' handleContextHubAsyncEvent should be postponed to HAL.
   EXPECT_CALL(*systemCallback,
-              handleContextHubAsyncEvent(AsyncEventType::RESTARTED));
+              handleContextHubAsyncEvent(AsyncEventType::RESTARTED))
+      .Times(0);
   EXPECT_CALL(*vendorCallback,
               handleContextHubAsyncEvent(AsyncEventType::RESTARTED))
       .Times(0);
 
-  // Disconnect the vendor client and handle CHRE restart for the system server
-  halClientManager->handleClientDeath(kVendorPid);
   halClientManager->handleChreRestart();
 }
 
+TEST_F(HalClientManagerTest, getAllConnectedCallbacks) {
+  auto halClientManager = std::make_unique<HalClientManagerForTest>(
+      mockDeadClientUnlinker, kClientIdMappingFilePath);
+  std::shared_ptr<ContextHubCallbackForTest> vendorCallback =
+      ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid);
+  std::shared_ptr<ContextHubCallbackForTest> systemCallback =
+      ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
+          kSystemServerUuid);
+  // Register the system callback
+  EXPECT_TRUE(halClientManager->registerCallback(
+      kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr));
+  // Register the vendor callback
+  EXPECT_TRUE(halClientManager->registerCallback(
+      kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr));
+
+  EXPECT_THAT(halClientManager->getCallbacks(),
+              UnorderedElementsAre(vendorCallback, systemCallback));
+}
+
 }  // namespace
 }  // namespace android::hardware::contexthub::common::implementation
diff --git a/host/tinysys/hal/Android.bp b/host/tinysys/hal/Android.bp
index dc7e51d..ba1e970 100644
--- a/host/tinysys/hal/Android.bp
+++ b/host/tinysys/hal/Android.bp
@@ -28,7 +28,10 @@
 cc_binary {
     name: "android.hardware.contexthub-service.tinysys",
     cpp_std: "c++20",
-    defaults: ["hidl_defaults"],
+    defaults: [
+        "contexthub_hal_defaults",
+        "hidl_defaults",
+    ],
     vendor: true,
     relative_install_path: "hw",
     srcs: [
@@ -38,47 +41,13 @@
         "tinysys_chre_connection.cc",
         "tinysys_context_hub.cc",
     ],
-    include_dirs: [
-        "system/chre/host/common/include/",
-        "system/chre/host/hal_generic/aidl/",
-        "system/chre/host/hal_generic/common/",
-        "system/chre/platform/shared/include/",
-        "system/chre/util/include/",
-    ],
-    cflags: [
-        "-DCHRE_HOST_DEFAULT_FRAGMENT_SIZE=2048",
-        "-DCHRE_IS_HOST_BUILD",
-        "-DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4000",
-        "-DCHRE_ST_LPMA_HANDLER_AIDL",
-        "-Wall",
-        "-Werror",
-    ],
+    cflags: ["-DCHRE_ST_LPMA_HANDLER_AIDL"],
     shared_libs: [
-        "android.frameworks.stats-V2-ndk",
-        "android.hardware.contexthub-V3-ndk",
         "android.hardware.soundtrigger3-V1-ndk",
         "android.media.soundtrigger.types-V1-ndk",
-        "chre_atoms_log",
-        "chremetrics-cpp",
-        "libaconfig_storage_read_api_cc",
-        "libbase",
-        "libbinder_ndk",
-        "libcutils",
-        "libjsoncpp",
-        "liblog",
         "libpower",
-        "libprotobuf-cpp-lite",
-        "libutils",
-        "server_configurable_flags",
-    ],
-    header_libs: [
-        "chre_api",
     ],
     static_libs: [
-        "chre_client",
-        "chre_flags_c_lib",
-        "chre_metrics_reporter",
-        "event_logger",
         "pw_detokenizer",
         "pw_polyfill",
         "pw_span",
diff --git a/host/tinysys/hal/android.hardware.contexthub-service.tinysys.xml b/host/tinysys/hal/android.hardware.contexthub-service.tinysys.xml
index db2e2d7..3d9d4af 100644
--- a/host/tinysys/hal/android.hardware.contexthub-service.tinysys.xml
+++ b/host/tinysys/hal/android.hardware.contexthub-service.tinysys.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.contexthub</name>
-        <version>3</version>
+        <version>4</version>
         <fqname>IContextHub/default</fqname>
     </hal>
 </manifest>
diff --git a/host/tinysys/hal/tinysys_chre_connection.cc b/host/tinysys/hal/tinysys_chre_connection.cc
index ddd8402..777596f 100644
--- a/host/tinysys/hal/tinysys_chre_connection.cc
+++ b/host/tinysys/hal/tinysys_chre_connection.cc
@@ -70,8 +70,8 @@
 
 bool TinysysChreConnection::init() {
   // Make sure the payload size is large enough for nanoapp binary fragment
-  static_assert(kMaxPayloadBytes > CHRE_HOST_DEFAULT_FRAGMENT_SIZE &&
-                kMaxPayloadBytes - CHRE_HOST_DEFAULT_FRAGMENT_SIZE >
+  static_assert(kMaxSendingPayloadBytes > CHRE_HOST_DEFAULT_FRAGMENT_SIZE &&
+                kMaxSendingPayloadBytes - CHRE_HOST_DEFAULT_FRAGMENT_SIZE >
                     kMaxPayloadOverheadBytes);
   mChreFileDescriptor =
       TEMP_FAILURE_RETRY(open(kChreFileDescriptorPath, O_RDWR));
@@ -93,8 +93,8 @@
   auto chreFd = chreConnection->getChreFileDescriptor();
   while (true) {
     {
-      ssize_t payloadSize = TEMP_FAILURE_RETRY(
-          read(chreFd, chreConnection->mPayload.get(), kMaxPayloadBytes));
+      ssize_t payloadSize = TEMP_FAILURE_RETRY(read(
+          chreFd, chreConnection->mPayload.get(), kMaxReceivingPayloadBytes));
       if (payloadSize == 0) {
         // Payload size 0 is a fake signal from kernel which is normal if the
         // device is in sleep.
@@ -161,7 +161,7 @@
 }
 
 bool TinysysChreConnection::sendMessage(void *data, size_t length) {
-  if (length <= 0 || length > kMaxPayloadBytes) {
+  if (length <= 0 || length > kMaxSendingPayloadBytes) {
     LOGE("length %zu is not within the accepted range.", length);
     return false;
   }
diff --git a/host/tinysys/hal/tinysys_chre_connection.h b/host/tinysys/hal/tinysys_chre_connection.h
index d30ba8f..5c771b4 100644
--- a/host/tinysys/hal/tinysys_chre_connection.h
+++ b/host/tinysys/hal/tinysys_chre_connection.h
@@ -44,7 +44,7 @@
  public:
   TinysysChreConnection(ChreConnectionCallback *callback)
       : mCallback(callback), mLpmaHandler(/* allowed= */ true) {
-    mPayload = std::make_unique<uint8_t[]>(kMaxPayloadBytes);
+    mPayload = std::make_unique<uint8_t[]>(kMaxReceivingPayloadBytes);
   };
 
   ~TinysysChreConnection() override {
@@ -104,11 +104,10 @@
   static constexpr char kWakeLock[] = "tinysys_chre_hal_wakelock";
 
   // Max payload size that can be sent to CHRE
-  // TODO(b/277235389): Adjust max payload size (AP -> SCP and SCP -> AP)
-  // as appropriate. This is a temp/quick fix for b/272311907 and b/270758946
-  // setting max payload allowed to CHRE_MESSAGE_TO_HOST_MAX_SIZE + 128 byte
-  // to account for transport overhead.
-  static constexpr uint32_t kMaxPayloadBytes = 4224;  // 4096 + 128
+  static constexpr uint32_t kMaxSendingPayloadBytes = 0x8000;  // 32K
+
+  // Max payload size that can be received from CHRE
+  static constexpr uint32_t kMaxReceivingPayloadBytes = 0x8000;  // 32K
 
   // Max overhead of the nanoapp binary payload caused by the fbs encapsulation
   static constexpr uint32_t kMaxPayloadOverheadBytes = 1024;
@@ -126,10 +125,10 @@
     // security check for proper use of the device node.
     uint32_t magic = 0x67728269;
     uint32_t payloadSize = 0;
-    uint8_t payload[kMaxPayloadBytes];
+    uint8_t payload[kMaxSendingPayloadBytes];
 
     ChreConnectionMessage(void *data, size_t length) {
-      assert(length <= kMaxPayloadBytes);
+      assert(length <= kMaxSendingPayloadBytes);
       memcpy(payload, data, length);
       payloadSize = static_cast<uint32_t>(length);
     }
diff --git a/java/test/audio_diagnostics/src/com/google/android/chre/test/audio_diagnostics/ContextHubAudioDiagnosticsTestExecutor.java b/java/test/audio_diagnostics/src/com/google/android/chre/test/audio_diagnostics/ContextHubAudioDiagnosticsTestExecutor.java
index e67f038..b7defb0 100644
--- a/java/test/audio_diagnostics/src/com/google/android/chre/test/audio_diagnostics/ContextHubAudioDiagnosticsTestExecutor.java
+++ b/java/test/audio_diagnostics/src/com/google/android/chre/test/audio_diagnostics/ContextHubAudioDiagnosticsTestExecutor.java
@@ -149,8 +149,8 @@
                                         "audio_dc_offset_test_data.bin", mContext);
 
         Log.i(TAG, "DC Offset: " + runningSampleAvg);
-        Assert.assertTrue("DC offset " + runningSampleAvg + " >= threshold " + DC_OFFSET_LIMIT,
-                runningSampleAvg < DC_OFFSET_LIMIT);
+        Assert.assertTrue("DC offset " + runningSampleAvg + " > threshold " + DC_OFFSET_LIMIT,
+                runningSampleAvg <= DC_OFFSET_LIMIT);
     }
 
     /**
diff --git a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubPendingIntentTestExecutor.java b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubPendingIntentTestExecutor.java
index 272de45..98320d7 100644
--- a/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubPendingIntentTestExecutor.java
+++ b/java/test/chqts/src/com/google/android/chre/test/chqts/ContextHubPendingIntentTestExecutor.java
@@ -84,7 +84,7 @@
     }
 
     public void init() throws InterruptedException, TimeoutException {
-        mTestHelper.initAndUnloadAllNanoApps();
+        mTestHelper.init();
     }
 
     /**
diff --git a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorBase.java b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorBase.java
index c564127..0727580 100644
--- a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorBase.java
+++ b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorBase.java
@@ -23,14 +23,9 @@
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.NanoAppBinary;
 import android.hardware.location.NanoAppMessage;
-import android.hardware.location.NanoAppState;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-import android.util.Log;
 
 import com.google.android.utils.chre.ChreTestUtil;
 
-import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
@@ -99,9 +94,6 @@
     /**
      * Validate the data from AP and CHRE according to the parameters passed to this cross
      * validator. Should be called in @Test methods of tests.
-     *
-     * @param samplingDurationInMs The amount of time in milliseconds to collect samples from AP and
-     * CHRE.
      */
     public abstract void validate() throws AssertionError, InterruptedException;
 
@@ -120,28 +112,6 @@
     }
 
     /**
-    * Unloads all nanoapps from device. Call before validating data to ensure no inconsistencies
-    * with data received.
-    */
-    private void unloadAllNanoApps() {
-        // We only need to unload all nanoapps when the device has version < U, so the
-        // tests remain the same on those devices. On newer devices, test mode will
-        // handle this.
-        if (VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE) {
-            return;
-        }
-
-        List<NanoAppState> nanoAppStateList =
-                ChreTestUtil.queryNanoAppsAssertSuccess(mContextHubManager, mContextHubInfo);
-
-        for (NanoAppState state : nanoAppStateList) {
-            ChreTestUtil.unloadNanoAppAssertSuccess(
-                    mContextHubManager, mContextHubInfo, state.getNanoAppId());
-            Log.d(TAG, String.format("Unloaded napp: 0x%X", state.getNanoAppId()));
-        }
-    }
-
-    /**
     * Close the context hub client connection.
     */
     private void closeContextHubConnection() {
@@ -163,28 +133,20 @@
     * @return the name of the context hub result.
     */
     protected static String contextHubTransactionResultToString(int result) {
-        switch (result) {
-            case ContextHubTransaction.RESULT_SUCCESS:
-                return "RESULT_SUCCESS";
-            case ContextHubTransaction.RESULT_FAILED_UNKNOWN:
-                return "RESULT_FAILED_UNKNOWN";
-            case ContextHubTransaction.RESULT_FAILED_BAD_PARAMS:
-                return "RESULT_FAILED_BAD_PARAMS";
-            case ContextHubTransaction.RESULT_FAILED_UNINITIALIZED:
-                return "RESULT_FAILED_UNINITIALIZED";
-            case ContextHubTransaction.RESULT_FAILED_BUSY:
-                return "RESULT_FAILED_BUSY";
-            case ContextHubTransaction.RESULT_FAILED_AT_HUB:
-                return "RESULT_FAILED_AT_HUB";
-            case ContextHubTransaction.RESULT_FAILED_TIMEOUT:
-                return "RESULT_FAILED_TIMEOUT";
-            case ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE:
-                return "RESULT_FAILED_SERVICE_INTERNAL_FAILURE";
-            case ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE:
-                return "RESULT_FAILED_HAL_UNAVAILABLE";
-            default:
-                return "UNKNOWN_RESULT";
-        }
+        return switch (result) {
+            case ContextHubTransaction.RESULT_SUCCESS -> "RESULT_SUCCESS";
+            case ContextHubTransaction.RESULT_FAILED_UNKNOWN -> "RESULT_FAILED_UNKNOWN";
+            case ContextHubTransaction.RESULT_FAILED_BAD_PARAMS -> "RESULT_FAILED_BAD_PARAMS";
+            case ContextHubTransaction.RESULT_FAILED_UNINITIALIZED -> "RESULT_FAILED_UNINITIALIZED";
+            case ContextHubTransaction.RESULT_FAILED_BUSY -> "RESULT_FAILED_BUSY";
+            case ContextHubTransaction.RESULT_FAILED_AT_HUB -> "RESULT_FAILED_AT_HUB";
+            case ContextHubTransaction.RESULT_FAILED_TIMEOUT -> "RESULT_FAILED_TIMEOUT";
+            case ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE ->
+                    "RESULT_FAILED_SERVICE_INTERNAL_FAILURE";
+            case ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE ->
+                    "RESULT_FAILED_HAL_UNAVAILABLE";
+            default -> "UNKNOWN_RESULT";
+        };
     }
 
     /**
diff --git a/java/test/utils/src/com/google/android/utils/chre/ChreApiTestUtil.java b/java/test/utils/src/com/google/android/utils/chre/ChreApiTestUtil.java
index 75e56d4..c01b020 100644
--- a/java/test/utils/src/com/google/android/utils/chre/ChreApiTestUtil.java
+++ b/java/test/utils/src/com/google/android/utils/chre/ChreApiTestUtil.java
@@ -548,7 +548,7 @@
      */
     public static void writeDataToFile(byte[] data, String filename,
                 Context context) throws Exception {
-        File file = new File(context.getExternalFilesDir(null), filename);
+        File file = new File(context.getFilesDir(), filename);
         ByteSink sink = Files.asByteSink(file);
         sink.write(data);
     }
diff --git a/java/test/utils/src/com/google/android/utils/chre/ContextHubBroadcastReceiver.java b/java/test/utils/src/com/google/android/utils/chre/ContextHubBroadcastReceiver.java
index 3a34d4f..e626b1a 100644
--- a/java/test/utils/src/com/google/android/utils/chre/ContextHubBroadcastReceiver.java
+++ b/java/test/utils/src/com/google/android/utils/chre/ContextHubBroadcastReceiver.java
@@ -51,7 +51,7 @@
         }
         Log.d(TAG, "Received intent event: " + event);
 
-        Assert.assertEquals("Received too many Intent events", sQueue.size(), 0);
+        Assert.assertEquals("Received too many Intent events", /* expected= */ 0, sQueue.size());
         sQueue.add(event);
     }
 
diff --git a/java/test/utils/src/com/google/android/utils/chre/ContextHubServiceTestHelper.java b/java/test/utils/src/com/google/android/utils/chre/ContextHubServiceTestHelper.java
index 18623a0..9bd5899 100644
--- a/java/test/utils/src/com/google/android/utils/chre/ContextHubServiceTestHelper.java
+++ b/java/test/utils/src/com/google/android/utils/chre/ContextHubServiceTestHelper.java
@@ -26,8 +26,6 @@
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.NanoAppBinary;
 import android.hardware.location.NanoAppState;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
 
 import java.util.HashMap;
 import java.util.List;
@@ -65,18 +63,6 @@
         registerHubResetClient();
     }
 
-    public void initAndUnloadAllNanoApps() throws InterruptedException, TimeoutException {
-        init();
-
-        // We only need to unload all nanoapps when the device has version < U, so the
-        // tests remain the same on those devices. On newer devices, test mode will
-        // handle this.
-        if (VERSION.SDK_INT < VERSION_CODES.UPSIDE_DOWN_CAKE) {
-            // Unload all nanoapps to ensure test starts at a clean state.
-            unloadAllNanoApps();
-        }
-    }
-
     public void deinit() {
         // unregister to detect any hub reset.
         unregisterHubResetClient();
diff --git a/pal/CMakeLists.txt b/pal/CMakeLists.txt
new file mode 100644
index 0000000..b8d562d
--- /dev/null
+++ b/pal/CMakeLists.txt
@@ -0,0 +1,99 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+include(backend.cmake)
+
+pw_add_facade(chre.pal.audio INTERFACE
+  BACKEND
+    chre.pal.audio_BACKEND
+  HEADERS
+    include/chre/pal/audio.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.pal.system
+    chre.pal.version
+    chre.chre_api
+)
+
+pw_add_facade(chre.pal.ble INTERFACE
+  BACKEND
+    chre.pal.ble_BACKEND
+  HEADERS
+    include/chre/pal/ble.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.pal.system
+    chre.pal.version
+    chre.chre_api
+)
+
+pw_add_facade(chre.pal.gnss INTERFACE
+  BACKEND
+    chre.pal.gnss_BACKEND
+  HEADERS
+    include/chre/pal/gnss.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.pal.system
+    chre.pal.version
+    chre.chre_api
+)
+
+pw_add_facade(chre.pal.sensor INTERFACE
+  BACKEND
+    chre.pal.sensor_BACKEND
+  HEADERS
+    include/chre/pal/sensor.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.pal.system
+    chre.pal.version
+    chre.chre_api
+)
+
+pw_add_library(chre.pal.system INTERFACE
+  HEADERS
+    include/chre/pal/system.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+)
+
+pw_add_library(chre.pal.version INTERFACE
+  HEADERS
+    include/chre/pal/version.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+    chre.pal.version
+)
+
+pw_add_facade(chre.pal.wifi INTERFACE
+  BACKEND
+    chre.pal.wifi_BACKEND
+  HEADERS
+    include/chre/pal/wifi.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.pal.system
+    chre.pal.version
+    chre.chre_api
+)
+
+pw_add_facade(chre.pal.wwan INTERFACE
+  BACKEND
+    chre.pal.wwan_BACKEND
+  HEADERS
+    include/chre/pal/wwan.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.pal.system
+    chre.pal.version
+    chre.chre_api
+)
diff --git a/pal/backend.cmake b/pal/backend.cmake
new file mode 100644
index 0000000..9a4c871
--- /dev/null
+++ b/pal/backend.cmake
@@ -0,0 +1,21 @@
+include_guard(GLOBAL)
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+# Backend for chre.pal.audio.
+pw_add_backend_variable(chre.pal.audio_BACKEND)
+
+# Backend for chre.pal.ble.
+pw_add_backend_variable(chre.pal.ble_BACKEND)
+
+# Backend for chre.pal.gnss.
+pw_add_backend_variable(chre.pal.gnss_BACKEND)
+
+# Backend for chre.pal.sensor.
+pw_add_backend_variable(chre.pal.sensor_BACKEND)
+
+# Backend for chre.pal.wifi.
+pw_add_backend_variable(chre.pal.wifi_BACKEND)
+
+# Backend for chre.pal.wwan.
+pw_add_backend_variable(chre.pal.wwan_BACKEND)
diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt
new file mode 100644
index 0000000..7f2d2a9
--- /dev/null
+++ b/platform/CMakeLists.txt
@@ -0,0 +1,342 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+include(backend.cmake)
+
+add_subdirectory(arm)
+add_subdirectory(freertos)
+add_subdirectory(shared)
+
+pw_add_facade(chre.platform.assert INTERFACE
+  BACKEND
+    chre.platform.assert_BACKEND
+  HEADERS
+    include/chre/platform/assert.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.platform.log
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.atomic INTERFACE
+  BACKEND
+    chre.platform.atomic_BACKEND
+  HEADERS
+    include/chre/platform/atomic.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.condition_variable INTERFACE
+  BACKEND
+    chre.platform.condition_variable_BACKEND
+  HEADERS
+    include/chre/platform/condition_variable.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.platform.mutex
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.context INTERFACE
+  BACKEND
+    chre.platform.context_BACKEND
+  HEADERS
+    include/chre/platform/context.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.fatal_error INTERFACE
+  BACKEND
+    chre.platform.fatal_error_BACKEND
+  HEADERS
+    include/chre/platform/fatal_error.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.platform.log
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.host_link INTERFACE
+  BACKEND
+    chre.platform.host_link_BACKEND
+  HEADERS
+    include/chre/platform/host_link.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.log INTERFACE
+  BACKEND
+    chre.platform.log_BACKEND
+  HEADERS
+    include/chre/platform/log.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.memory INTERFACE
+  BACKEND
+    chre.platform.memory_BACKEND
+  HEADERS
+    include/chre/platform/memory.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.memory_manager INTERFACE
+  BACKEND
+    chre.platform.memory_manager_BACKEND
+  HEADERS
+    include/chre/platform/heap_block_header.h
+    include/chre/platform/memory_manager.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.core
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.mutex INTERFACE
+  BACKEND
+    chre.platform.mutex_BACKEND
+  HEADERS
+    include/chre/platform/mutex.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.notifier INTERFACE
+  BACKEND
+    chre.platform.notifier_BACKEND
+  HEADERS
+    include/chre/platform/notifier.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_audio INTERFACE
+  BACKEND
+    chre.platform.platform_audio_BACKEND
+  HEADERS
+    include/chre/platform/platform_audio.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.chre_api
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_ble INTERFACE
+  BACKEND
+    chre.platform.platform_ble_BACKEND
+  HEADERS
+    include/chre/platform/platform_ble.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_debug_dump_manager INTERFACE
+  BACKEND
+    chre.platform.platform_debug_dump_manager_BACKEND
+  HEADERS
+    include/chre/platform/platform_debug_dump_manager.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.chre_api
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_gnss INTERFACE
+  BACKEND
+    chre.platform.platform_gnss_BACKEND
+  HEADERS
+    include/chre/platform/platform_gnss.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_nanoapp INTERFACE
+  BACKEND
+    chre.platform.platform_nanoapp_BACKEND
+  HEADERS
+    include/chre/platform/platform_nanoapp.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_sensor INTERFACE
+  BACKEND
+    chre.platform.platform_sensor_BACKEND
+  HEADERS
+    include/chre/platform/platform_sensor.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.core
+    chre.platform.fatal_error
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_sensor_manager INTERFACE
+  BACKEND
+    chre.platform.platform_sensor_manager_BACKEND
+  HEADERS
+    include/chre/platform/platform_sensor_manager.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.core
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_sensor_type_helpers INTERFACE
+  BACKEND
+    chre.platform.platform_sensor_type_helpers_BACKEND
+  HEADERS
+    include/chre/platform/platform_sensor_type_helpers.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.core
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_wifi INTERFACE
+  BACKEND
+    chre.platform.platform_wifi_BACKEND
+  HEADERS
+    include/chre/platform/platform_wifi.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.platform_wwan INTERFACE
+  BACKEND
+    chre.platform.platform_wwan_BACKEND
+  HEADERS
+    include/chre/platform/platform_wwan.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.power_control_manager INTERFACE
+  BACKEND
+    chre.platform.power_control_manager_BACKEND
+  HEADERS
+    include/chre/platform/power_control_manager.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.static_nanoapp_init INTERFACE
+  BACKEND
+    chre.platform.static_nanoapp_init_BACKEND
+  HEADERS
+    include/chre/platform/static_nanoapp_init.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.system_time INTERFACE
+  BACKEND
+    chre.platform.system_time_BACKEND
+  HEADERS
+    include/chre/platform/system_time.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.system_timer INTERFACE
+  BACKEND
+    chre.platform.system_timer_BACKEND
+  HEADERS
+    include/chre/platform/system_timer.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.thread_handle INTERFACE
+  BACKEND
+    chre.platform.thread_handle_BACKEND
+  HEADERS
+    include/chre/platform/thread_handle.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.tracing INTERFACE
+  BACKEND
+    chre.platform.tracing_BACKEND
+  HEADERS
+    include/chre/platform/tracing.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.version INTERFACE
+  BACKEND
+    chre.platform.version_BACKEND
+  HEADERS
+    include/chre/platform/version.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
diff --git a/platform/arm/CMakeLists.txt b/platform/arm/CMakeLists.txt
new file mode 100644
index 0000000..a7fda77
--- /dev/null
+++ b/platform/arm/CMakeLists.txt
@@ -0,0 +1,8 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_library(chre.platform.arm.nanoapp_loader STATIC
+  SOURCES
+    nanoapp_loader.cc
+  PRIVATE_DEPS
+    chre.platform.shared.nanoapp_loader.facade
+)
diff --git a/platform/backend.cmake b/platform/backend.cmake
new file mode 100644
index 0000000..91d0712
--- /dev/null
+++ b/platform/backend.cmake
@@ -0,0 +1,87 @@
+include_guard(GLOBAL)
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+# Backend for chre.platform.assert.
+pw_add_backend_variable(chre.platform.assert_BACKEND)
+
+# Backend for chre.platform.atomic.
+pw_add_backend_variable(chre.platform.atomic_BACKEND)
+
+# Backend for chre.platform.condition_variable.
+pw_add_backend_variable(chre.platform.condition_variable_BACKEND)
+
+# Backend for chre.platform.context.
+pw_add_backend_variable(chre.platform.context_BACKEND)
+
+# Backend for chre.platform.fatal_error.
+pw_add_backend_variable(chre.platform.fatal_error_BACKEND)
+
+# Backend for chre.platform.host_link.
+pw_add_backend_variable(chre.platform.host_link_BACKEND)
+
+# Backend for chre.platform.log.
+pw_add_backend_variable(chre.platform.log_BACKEND)
+
+# Backend for chre.platform.memory.
+pw_add_backend_variable(chre.platform.memory_BACKEND)
+
+# Backend for chre.platform.memory_manager.
+pw_add_backend_variable(chre.platform.memory_manager_BACKEND)
+
+# Backend for chre.platform.mutex.
+pw_add_backend_variable(chre.platform.mutex_BACKEND)
+
+# Backend for chre.platform.notifier.
+pw_add_backend_variable(chre.platform.notifier_BACKEND)
+
+# Backend for chre.platform.platform_audio.
+pw_add_backend_variable(chre.platform.platform_audio_BACKEND)
+
+# Backend for chre.platform.platform_ble.
+pw_add_backend_variable(chre.platform.platform_ble_BACKEND)
+
+# Backend for chre.platform.platform_debug_dump_manager.
+pw_add_backend_variable(chre.platform.platform_debug_dump_manager_BACKEND)
+
+# Backend for chre.platform.platform_gnss.
+pw_add_backend_variable(chre.platform.platform_gnss_BACKEND)
+
+# Backend for chre.platform.platform_nanoapp.
+pw_add_backend_variable(chre.platform.platform_nanoapp_BACKEND)
+
+# Backend for chre.platform.platform_sensor.
+pw_add_backend_variable(chre.platform.platform_sensor_BACKEND)
+
+# Backend for chre.platform.platform_sensor_manager.
+pw_add_backend_variable(chre.platform.platform_sensor_manager_BACKEND)
+
+# Backend for chre.platform.platform_sensor_type_helpers.
+pw_add_backend_variable(chre.platform.platform_sensor_type_helpers_BACKEND)
+
+# Backend for chre.platform.platform_wifi.
+pw_add_backend_variable(chre.platform.platform_wifi_BACKEND)
+
+# Backend for chre.platform.platform_wwan.
+pw_add_backend_variable(chre.platform.platform_wwan_BACKEND)
+
+# Backend for chre.platform.power_control_manager.
+pw_add_backend_variable(chre.platform.power_control_manager_BACKEND)
+
+# Backend for chre.platform.static_nanoapp_init.
+pw_add_backend_variable(chre.platform.static_nanoapp_init_BACKEND)
+
+# Backend for chre.platform.system_time.
+pw_add_backend_variable(chre.platform.system_time_BACKEND)
+
+# Backend for chre.platform.system_timer.
+pw_add_backend_variable(chre.platform.system_timer_BACKEND)
+
+# Backend for chre.platform.thread_handle.
+pw_add_backend_variable(chre.platform.thread_handle_BACKEND)
+
+# Backend for chre.platform.tracing.
+pw_add_backend_variable(chre.platform.tracing_BACKEND)
+
+# Backend for chre.platform.version.
+pw_add_backend_variable(chre.platform.version_BACKEND)
diff --git a/platform/exynos/host_link.cc b/platform/exynos/host_link.cc
index 92629f2..5dfa7ce 100644
--- a/platform/exynos/host_link.cc
+++ b/platform/exynos/host_link.cc
@@ -221,4 +221,14 @@
   LOGE("NAN unsupported.");
 }
 
+void HostMessageHandlers::handleBtSocketOpen(
+    uint16_t /* hostClientId */, uint64_t /* socketId */,
+    const char * /* name */, uint64_t /* endpointId */, uint64_t /* hubId */,
+    uint32_t /* aclConnectionHandle */, uint32_t /* localCid */,
+    uint32_t /* remoteCid */, uint32_t /* psm */, uint32_t /* localMtu */,
+    uint32_t /* remoteMtu */, uint32_t /* localMps */, uint32_t /* remoteMps */,
+    uint32_t /* initialRxCredits */, uint32_t /* initialTxCredits */) {
+  LOGE("BT Socket offload not supported");
+}
+
 }  // namespace chre
diff --git a/platform/exynos/platform_cache_management.cc b/platform/exynos/platform_cache_management.cc
index 975fe79..7f3a65c 100644
--- a/platform/exynos/platform_cache_management.cc
+++ b/platform/exynos/platform_cache_management.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "chre/target_platform/platform_cache_management.h"
+#include "chre/platform/shared/platform_cache_management.h"
 
 namespace chre {
 
diff --git a/platform/freertos/CMakeLists.txt b/platform/freertos/CMakeLists.txt
new file mode 100644
index 0000000..ab464ee
--- /dev/null
+++ b/platform/freertos/CMakeLists.txt
@@ -0,0 +1,25 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_library(chre.platform.freertos.platform_nanoapp STATIC
+  HEADERS
+    public_platform_nanoapp/chre/target_platform/platform_nanoapp_base.h
+  PUBLIC_INCLUDES
+    public_platform_nanoapp
+  PUBLIC_DEPS
+    chre.platform.shared.memory
+    chre.platform.shared.nanoapp_support_lib_dso
+  SOURCES
+    platform_nanoapp.cc
+  PRIVATE_DEPS
+    chre.chre_api
+    chre.platform.assert
+    chre.platform.host_link
+    chre.platform.log
+    chre.platform.platform_nanoapp.facade
+    chre.platform.shared.authentication
+    chre.platform.shared.dlfcn
+    chre.platform.shared.nanoapp_dso_util
+    chre.platform.shared.nanoapp_loader
+    chre.util
+    chre.util.system
+)
diff --git a/platform/freertos/include/chre/target_platform/init.h b/platform/freertos/include/chre/target_platform/init.h
new file mode 120000
index 0000000..e90376d
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/init.h
@@ -0,0 +1 @@
+../../../public_init/chre/target_platform/init.h
\ No newline at end of file
diff --git a/platform/freertos/include/chre/target_platform/mutex_base.h b/platform/freertos/include/chre/target_platform/mutex_base.h
new file mode 120000
index 0000000..243ec1b
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/mutex_base.h
@@ -0,0 +1 @@
+../../../public_mutex/chre/target_platform/mutex_base.h
\ No newline at end of file
diff --git a/platform/freertos/include/chre/target_platform/mutex_base_impl.h b/platform/freertos/include/chre/target_platform/mutex_base_impl.h
new file mode 120000
index 0000000..8cb1b7a
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/mutex_base_impl.h
@@ -0,0 +1 @@
+../../../public_mutex/chre/target_platform/mutex_base_impl.h
\ No newline at end of file
diff --git a/platform/freertos/include/chre/target_platform/platform_nanoapp_base.h b/platform/freertos/include/chre/target_platform/platform_nanoapp_base.h
new file mode 120000
index 0000000..6628c23
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/platform_nanoapp_base.h
@@ -0,0 +1 @@
+../../../public_platform_nanoapp/chre/target_platform/platform_nanoapp_base.h
\ No newline at end of file
diff --git a/platform/freertos/include/chre/target_platform/static_nanoapp_init.h b/platform/freertos/include/chre/target_platform/static_nanoapp_init.h
new file mode 120000
index 0000000..5302945
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/static_nanoapp_init.h
@@ -0,0 +1 @@
+../../../public_static_nanoapp_init/chre/target_platform/static_nanoapp_init.h
\ No newline at end of file
diff --git a/platform/freertos/include/chre/target_platform/init.h b/platform/freertos/public_init/chre/target_platform/init.h
similarity index 100%
rename from platform/freertos/include/chre/target_platform/init.h
rename to platform/freertos/public_init/chre/target_platform/init.h
diff --git a/platform/freertos/include/chre/target_platform/mutex_base.h b/platform/freertos/public_mutex/chre/target_platform/mutex_base.h
similarity index 100%
rename from platform/freertos/include/chre/target_platform/mutex_base.h
rename to platform/freertos/public_mutex/chre/target_platform/mutex_base.h
diff --git a/platform/freertos/include/chre/target_platform/mutex_base_impl.h b/platform/freertos/public_mutex/chre/target_platform/mutex_base_impl.h
similarity index 100%
rename from platform/freertos/include/chre/target_platform/mutex_base_impl.h
rename to platform/freertos/public_mutex/chre/target_platform/mutex_base_impl.h
diff --git a/platform/freertos/include/chre/target_platform/platform_nanoapp_base.h b/platform/freertos/public_platform_nanoapp/chre/target_platform/platform_nanoapp_base.h
similarity index 100%
rename from platform/freertos/include/chre/target_platform/platform_nanoapp_base.h
rename to platform/freertos/public_platform_nanoapp/chre/target_platform/platform_nanoapp_base.h
diff --git a/platform/freertos/include/chre/target_platform/static_nanoapp_init.h b/platform/freertos/public_static_nanoapp_init/chre/target_platform/static_nanoapp_init.h
similarity index 100%
rename from platform/freertos/include/chre/target_platform/static_nanoapp_init.h
rename to platform/freertos/public_static_nanoapp_init/chre/target_platform/static_nanoapp_init.h
diff --git a/platform/include/chre/platform/event_loop_hooks.h b/platform/include/chre/platform/event_loop_hooks.h
new file mode 100644
index 0000000..8756c79
--- /dev/null
+++ b/platform/include/chre/platform/event_loop_hooks.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_PLATFORM_EVENT_LOOP_HOOKS_H_
+#define CHRE_PLATFORM_EVENT_LOOP_HOOKS_H_
+
+/**
+ * @file
+ * Include a platform-specific event-loop configuration header if it exists.
+ * This header can optionally override the default macros defined below.
+ *
+ * TODO(b/380327627): Move the conentents of this file to chre/variant/config.h
+ * once CHRE team migrates platforms to use the variant config pattern and
+ * they address b/376532038.
+ */
+#ifdef CHRE_PLATFORM_EVENT_LOOP_CONFIG_HEADER
+#include CHRE_PLATFORM_EVENT_LOOP_CONFIG_HEADER
+#endif  // CHRE_PLATFORM_EVENT_LOOP_CONFIG_HEADER
+
+/**
+ * @param eventLoop Pointer to the current EventLoop instance that is used to
+ *        deliver events to nanoapps.
+ * @param eventType Event type identifier, which implies the type of eventData
+ * @param eventData The data that failed to be posted
+ * @param freeCallback Function to invoke to when the event has been processed
+ *        by all recipients; this must be safe to call immediately, to handle
+ *        the case where CHRE is shutting down
+ * @param senderInstanceId The instance ID of the sender of this event
+ * @param targetInstanceId targetInstanceId The instance ID of the destination
+ *        of this event
+ * @param targetGroupMask Mask used to limit the recipients that are
+ *        registered to receive this event
+ *
+ * @since v1.11
+ * @note FATAL_ERROR is called after this macro is executed
+ */
+#ifndef CHRE_HANDLE_FAILED_SYSTEM_EVENT_ENQUEUE
+#define CHRE_HANDLE_FAILED_SYSTEM_EVENT_ENQUEUE(                     \
+    eventLoop, eventType, eventData, freeCallback, senderInstanceId, \
+    targetInstanceId, targetGroupMask)                               \
+  do {                                                               \
+  } while (0)
+#endif  // !CHRE_HANDLE_FAILED_SYSTEM_EVENT_ENQUEUE
+
+/**
+ * @param eventLoop Pointer to the current EventLoop instance that is used to
+ *        deliver events to nanoapps.
+ * @param eventType Event type identifier, which implies the type of eventData
+ * @param eventData The data that failed to be posted
+ * @param callback Function to invoke from the context of the CHRE thread
+ * @param extraData Additional arbitrary data to provide to the callback
+ *
+ * @since v1.11
+ * @note FATAL_ERROR is called after this macro is executed
+ */
+#ifndef CHRE_HANDLE_EVENT_QUEUE_FULL_DURING_SYSTEM_POST
+#define CHRE_HANDLE_EVENT_QUEUE_FULL_DURING_SYSTEM_POST(  \
+    eventLoop, eventType, eventData, callback, extraData) \
+  do {                                                    \
+  } while (0)
+#endif  // !CHRE_HANDLE_EVENT_QUEUE_FULL_DURING_SYSTEM_POST
+
+/**
+ * @param eventLoop Pointer to the current EventLoop instance that is used to
+ *        deliver events to nanoapps.
+ * @param eventType Event type identifier, which implies the type of eventData
+ * @param eventData The data that failed to be posted
+ * @param freeCallback Function to invoke to when the event has been processed
+ *        by all recipients; this must be safe to call immediately, to handle
+ *        the case where CHRE is shutting down
+ * @param senderInstanceId The instance ID of the sender of this event
+ * @param targetInstanceId targetInstanceId The instance ID of the destination
+ *        of this event
+ * @param targetGroupMask Mask used to limit the recipients that are
+ *        registered to receive this event
+ *
+ * @since v1.11
+ * @note Upon return, the freeCallaback will be invoked if not nullptr.
+ */
+#ifndef CHRE_HANDLE_LOW_PRIORITY_ENQUEUE_FAILURE
+#define CHRE_HANDLE_LOW_PRIORITY_ENQUEUE_FAILURE(                    \
+    eventLoop, eventType, eventData, freeCallback, senderInstanceId, \
+    targetInstanceId, targetGroupMask)                               \
+  do {                                                               \
+  } while (0)
+#endif  // !CHRE_HANDLE_LOW_PRIORITY_ENQUEUE_FAILURE
+
+#endif  // CHRE_PLATFORM_EVENT_LOOP_HOOKS_H_
diff --git a/platform/include/chre/platform/host_link.h b/platform/include/chre/platform/host_link.h
index 0d5b7b4..2bf5464 100644
--- a/platform/include/chre/platform/host_link.h
+++ b/platform/include/chre/platform/host_link.h
@@ -93,4 +93,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/host_link_impl.h")
+#include "chre/target_platform/host_link_impl.h"
+#endif  // __has_include("chre/target_platform/host_link_impl.h")
+
 #endif  // CHRE_PLATFORM_HOST_LINK_H_
diff --git a/platform/include/chre/platform/memory_manager.h b/platform/include/chre/platform/memory_manager.h
index 48c1881..e7906df 100644
--- a/platform/include/chre/platform/memory_manager.h
+++ b/platform/include/chre/platform/memory_manager.h
@@ -21,9 +21,9 @@
 #include <cstdint>
 
 #include "chre/core/nanoapp.h"
+#include "chre/platform/heap_block_header.h"
 #include "chre/util/non_copyable.h"
 #include "chre/util/system/debug_dump.h"
-#include "heap_block_header.h"
 
 // This default value can be overridden in the variant-specific makefile.
 #ifndef CHRE_MAX_ALLOCATION_BYTES
@@ -140,4 +140,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/memory_manager_impl.h")
+#include "chre/target_platform/memory_manager_impl.h"
+#endif  // __has_include("chre/target_platform/memory_manager_impl.h")
+
 #endif  // CHRE_PLATFORM_MEMORY_MANAGER_H_
diff --git a/platform/include/chre/platform/platform_audio.h b/platform/include/chre/platform/platform_audio.h
index 3f20115..353d6d9 100644
--- a/platform/include/chre/platform/platform_audio.h
+++ b/platform/include/chre/platform/platform_audio.h
@@ -129,4 +129,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_audio_impl.h")
+#include "chre/target_platform/platform_audio_impl.h"
+#endif  // __has_include("chre/target_platform/platform_audio_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_AUDIO_H_
diff --git a/platform/include/chre/platform/platform_ble.h b/platform/include/chre/platform/platform_ble.h
index fdb495f..8574b77 100644
--- a/platform/include/chre/platform/platform_ble.h
+++ b/platform/include/chre/platform/platform_ble.h
@@ -120,4 +120,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_ble_impl.h")
+#include "chre/target_platform/platform_ble_impl.h"
+#endif  // __has_include("chre/target_platform/platform_ble_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_BLE_H_
diff --git a/platform/include/chre/platform/platform_debug_dump_manager.h b/platform/include/chre/platform/platform_debug_dump_manager.h
index 89b2dce..3a665db 100644
--- a/platform/include/chre/platform/platform_debug_dump_manager.h
+++ b/platform/include/chre/platform/platform_debug_dump_manager.h
@@ -66,4 +66,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_debug_dump_manager_impl.h")
+#include "chre/target_platform/platform_debug_dump_manager_impl.h"
+#endif  // __has_include("chre/target_platform/platform_debug_dump_manager_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_DEBUG_DUMP_MANAGER_H_
diff --git a/platform/include/chre/platform/platform_gnss.h b/platform/include/chre/platform/platform_gnss.h
index c922f5e..2bc7cfb 100644
--- a/platform/include/chre/platform/platform_gnss.h
+++ b/platform/include/chre/platform/platform_gnss.h
@@ -95,4 +95,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_gnss_impl.h")
+#include "chre/target_platform/platform_gnss_impl.h"
+#endif  // __has_include("chre/target_platform/platform_gnss_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_GNSS_H_
diff --git a/platform/include/chre/platform/platform_nanoapp.h b/platform/include/chre/platform/platform_nanoapp.h
index 817226c..7d9e736 100644
--- a/platform/include/chre/platform/platform_nanoapp.h
+++ b/platform/include/chre/platform/platform_nanoapp.h
@@ -135,4 +135,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_nanoapp_impl.h")
+#include "chre/target_platform/platform_nanoapp_impl.h"
+#endif  // __has_include("chre/target_platform/platform_nanoapp_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_NANOAPP_H_
diff --git a/platform/include/chre/platform/platform_sensor.h b/platform/include/chre/platform/platform_sensor.h
index 277afb5..fa0a75d 100644
--- a/platform/include/chre/platform/platform_sensor.h
+++ b/platform/include/chre/platform/platform_sensor.h
@@ -92,4 +92,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_sensor_impl.h")
+#include "chre/target_platform/platform_sensor_impl.h"
+#endif  // __has_include("chre/target_platform/platform_sensor_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_SENSOR_H_
diff --git a/platform/include/chre/platform/platform_sensor_manager.h b/platform/include/chre/platform/platform_sensor_manager.h
index ded9859..a3c0fb6 100644
--- a/platform/include/chre/platform/platform_sensor_manager.h
+++ b/platform/include/chre/platform/platform_sensor_manager.h
@@ -18,7 +18,6 @@
 #define CHRE_PLATFORM_PLATFORM_SENSOR_MANAGER_H_
 
 #include "chre/core/sensor.h"
-#include "chre/pal/sensor.h"
 #include "chre/target_platform/platform_sensor_manager_base.h"
 #include "chre/util/dynamic_vector.h"
 
@@ -155,4 +154,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_sensor_manager_impl.h")
+#include "chre/target_platform/platform_sensor_manager_impl.h"
+#endif  // __has_include("chre/target_platform/platform_sensor_manager_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_SENSOR_MANAGER_H_
diff --git a/platform/include/chre/platform/platform_sensor_type_helpers.h b/platform/include/chre/platform/platform_sensor_type_helpers.h
index a396897..7afb88b 100644
--- a/platform/include/chre/platform/platform_sensor_type_helpers.h
+++ b/platform/include/chre/platform/platform_sensor_type_helpers.h
@@ -79,4 +79,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_sensor_type_helpers_impl.h")
+#include "chre/target_platform/platform_sensor_type_helpers_impl.h"
+#endif  // __has_include("chre/target_platform/platform_sensor_type_helpers_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_SENSOR_TYPE_HELPERS_H_
diff --git a/platform/include/chre/platform/platform_wifi.h b/platform/include/chre/platform/platform_wifi.h
index fc7a545..1692575 100644
--- a/platform/include/chre/platform/platform_wifi.h
+++ b/platform/include/chre/platform/platform_wifi.h
@@ -129,4 +129,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_wifi_impl.h")
+#include "chre/target_platform/platform_wifi_impl.h"
+#endif  // __has_include("chre/target_platform/platform_wifi_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_WIFI_H_
diff --git a/platform/include/chre/platform/platform_wwan.h b/platform/include/chre/platform/platform_wwan.h
index bed6449..e22ad55 100644
--- a/platform/include/chre/platform/platform_wwan.h
+++ b/platform/include/chre/platform/platform_wwan.h
@@ -64,4 +64,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/platform_wwan_impl.h")
+#include "chre/target_platform/platform_wwan_impl.h"
+#endif  // __has_include("chre/target_platform/platform_wwan_impl.h")
+
 #endif  // CHRE_PLATFORM_PLATFORM_WWAN_H_
diff --git a/platform/include/chre/platform/power_control_manager.h b/platform/include/chre/platform/power_control_manager.h
index 6b3566b..963718c 100644
--- a/platform/include/chre/platform/power_control_manager.h
+++ b/platform/include/chre/platform/power_control_manager.h
@@ -52,4 +52,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/power_control_manager_impl.h")
+#include "chre/target_platform/power_control_manager_impl.h"
+#endif  // __has_include("chre/target_platform/power_control_manager_impl.h")
+
 #endif  // CHRE_PLATFORM_POWER_CONTROL_MANAGER_H
diff --git a/platform/include/chre/platform/system_time.h b/platform/include/chre/platform/system_time.h
index ac58e50..a25bd02 100644
--- a/platform/include/chre/platform/system_time.h
+++ b/platform/include/chre/platform/system_time.h
@@ -66,4 +66,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/system_time_impl.h")
+#include "chre/target_platform/system_time_impl.h"
+#endif  // __has_include("chre/target_platform/system_time_impl.h")
+
 #endif  // CHRE_PLATFORM_TIME_H_
diff --git a/platform/include/chre/platform/system_timer.h b/platform/include/chre/platform/system_timer.h
index 08160d2..7a4e459 100644
--- a/platform/include/chre/platform/system_timer.h
+++ b/platform/include/chre/platform/system_timer.h
@@ -107,4 +107,9 @@
 
 }  // namespace chre
 
+/* The platform can optionally provide an inlined implementation */
+#if __has_include("chre/target_platform/system_timer_impl.h")
+#include "chre/target_platform/system_timer_impl.h"
+#endif  // __has_include("chre/target_platform/system_timer_impl.h")
+
 #endif  // CHRE_PLATFORM_SYSTEM_TIMER_H_
diff --git a/platform/platform.mk b/platform/platform.mk
index 8e16753..8dd0d61 100644
--- a/platform/platform.mk
+++ b/platform/platform.mk
@@ -193,6 +193,11 @@
 # Simulator-specific Compiler Flags ############################################
 
 SIM_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/include
+SIM_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_ble_pal
+SIM_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_debug_dump_manager
+SIM_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_gnss_pal
+SIM_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_wifi_pal
+SIM_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_wwan_pal
 SIM_CFLAGS += -Iplatform/linux/sim/include
 
 # Simulator-specific Source Files ##############################################
@@ -342,6 +347,11 @@
 # The order here is important so that the googletest target prefers shared,
 # linux and then SLPI.
 GOOGLETEST_CFLAGS += -Iplatform/shared/include
+GOOGLETEST_CFLAGS += -Iplatform/shared/public_platform_ble_pal
+GOOGLETEST_CFLAGS += -Iplatform/shared/public_platform_debug_dump_manager
+GOOGLETEST_CFLAGS += -Iplatform/shared/public_platform_gnss_pal
+GOOGLETEST_CFLAGS += -Iplatform/shared/public_platform_wifi_pal
+GOOGLETEST_CFLAGS += -Iplatform/shared/public_platform_wwan_pal
 GOOGLETEST_CFLAGS += -Iplatform/linux/include
 GOOGLETEST_CFLAGS += -Iplatform/slpi/include
 GOOGLETEST_CFLAGS += -Iplatform/shared/pw_trace/include
@@ -513,6 +523,14 @@
 TINYSYS_SRCS += $(CHRE_PREFIX)/platform/shared/platform_gnss.cc
 endif
 
+ifeq ($(CHRE_WIFI_SUPPORT_ENABLED), true)
+TINYSYS_SRCS += platform/shared/platform_wifi.cc
+endif
+
+ifeq ($(CHRE_WWAN_SUPPORT_ENABLED), true)
+TINYSYS_SRCS += platform/shared/platform_wwan.cc
+endif
+
 # Compiler flags
 
 # Variables
@@ -523,6 +541,11 @@
 TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/aligned_alloc_unsupported/include
 TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/include
 TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/nanoapp/include
+TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_ble_pal
+TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_debug_dump_manager
+TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_gnss_pal
+TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_wifi_pal
+TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/public_platform_wwan_pal
 TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/include/chre/platform/shared/libc
 TINYSYS_CFLAGS += -I$(CHRE_PREFIX)/platform/tinysys/include
 
diff --git a/platform/shared/CMakeLists.txt b/platform/shared/CMakeLists.txt
new file mode 100644
index 0000000..f6beab8
--- /dev/null
+++ b/platform/shared/CMakeLists.txt
@@ -0,0 +1,644 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+include(backend.cmake)
+
+pw_add_facade(chre.platform.shared.authentication INTERFACE
+  BACKEND
+    chre.platform.shared.authentication_BACKEND
+  HEADERS
+    include/chre/platform/shared/authentication.h
+  PUBLIC_INCLUDES
+    include
+)
+
+pw_add_facade(chre.platform.shared.bt_snoop_log INTERFACE
+  BACKEND
+    chre.platform.shared.bt_snoop_log_BACKEND
+  HEADERS
+    include/chre/platform/shared/bt_snoop_log.h
+  PUBLIC_INCLUDES
+    include
+)
+
+# Implements chre_api/chre/audio.h's:
+# - bool chreAudioGetSource(uint32_t handle,
+#                           struct chreAudioSource *audioSource)
+# - bool chreAudioConfigureSource(uint32_t handle, bool enable,
+#                                 uint64_t bufferDuration,
+#                                 uint64_t deliveryInterval)
+# - bool chreAudioGetStatus(uint32_t handle,
+#                           struct chreAudioSourceStatus *status)
+#
+pw_add_library(chre.platform.shared.chre_api_audio STATIC
+  SOURCES
+    chre_api_audio.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.platform.platform_audio
+    chre.util
+    chre.util.system
+)
+# Implements chre_api/chre/ble.h's:
+# - uint32_t chreBleGetCapabilities()
+# - uint32_t chreBleGetFilterCapabilities()
+# - bool chreBleFlushAsync(const void *cookie)
+# - bool chreBleStartScanAsync(chreBleScanMode mode,
+#                              uint32_t reportDelayMs,
+#                              const struct chreBleScanFilter *filter)
+# - bool chreBleStartScanAsyncV1_9(enum chreBleScanMode mode,
+#                                  uint32_t reportDelayMs,
+#                                  const struct chreBleScanFilterV1_9 *filter,
+#                                  const void *cookie)
+# - bool chreBleStopScanAsync()
+# - bool chreBleStopScanAsyncV1_9(const void *cookie)
+# - bool chreBleReadRssiAsync(uint16_t connectionHandle,
+#                             const void *cookie)
+# - bool chreBleGetScanStatus(struct chreBleScanStatus *status)
+#
+pw_add_library(chre.platform.shared.chre_api_ble STATIC
+  SOURCES
+    chre_api_ble.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.util
+    chre.util.system
+)
+
+# Implements chre_api/chre/event.h's:
+# - bool chreSendEvent(uint16_t eventType, void *eventData,
+#                      chreEventCompleteFunction *freeCallback,
+#                      uint32_t targetInstanceId)
+# - bool chreSendMessageToHost(void *message, uint32_t messageSize,
+#                              uint32_t messageType,
+#                              chreMessageFreeFunction *freeCallback)
+# - bool chreSendMessageToHostEndpoint(
+#       void *message, size_t messageSize, uint32_t messageType,
+#       uint16_t hostEndpoint, chreMessageFreeFunction *freeCallback)
+# - bool chreSendMessageWithPermissions(
+#       void *message, size_t messageSize, uint32_t messageType,
+#       uint16_t hostEndpoint, uint32_t messagePermissions,
+#       chreMessageFreeFunction *freeCallback)
+# - bool chreSendReliableMessageAsync(
+#       void *message, size_t messageSize, uint32_t messageType,
+#       uint16_t hostEndpoint, uint32_t messagePermissions,
+#       chreMessageFreeFunction *freeCallback, const void *cookie)
+# - bool chreGetNanoappInfoByAppId(uint64_t appId, struct chreNanoappInfo *info)
+# - bool chreGetNanoappInfoByInstanceId(uint32_t instanceId,
+#                                       struct chreNanoappInfo *info)
+# - void chreConfigureNanoappInfoEvents(bool enable)
+# - void chreConfigureHostSleepStateEvents(bool enable)
+# - bool chreIsHostAwake()
+# - void chreConfigureDebugDumpEvent(bool enable)
+# - bool chreConfigureHostEndpointNotifications(uint16_t hostEndpointId,
+#                                               bool enable)
+# - bool chrePublishRpcServices(struct chreNanoappRpcService *services,
+#                               size_t numServices)
+# - bool chreGetHostEndpointInfo(uint16_t hostEndpointId,
+#                                struct chreHostEndpointInfo *info)
+#
+pw_add_library(chre.platform.shared.chre_api_core STATIC
+  SOURCES
+    chre_api_core.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.platform.log
+    chre.util
+    chre.util.system
+)
+
+# Implements chre_api/chre/gnss.h's:
+# - uint32_t chreGnssGetCapabilities()
+# - bool chreGnssLocationSessionStartAsync(
+#       uint32_t minIntervalMs, uint32_t minTimeToNextFixMs,
+#       const void *cookie)
+# - bool chreGnssLocationSessionStopAsync(const void *cookie)
+# - bool chreGnssMeasurementSessionStartAsync(
+#        uint32_t minIntervalMs, const void *cookie)
+# - bool chreGnssMeasurementSessionStopAsync(const void *cookie)
+# - bool chreGnssConfigurePassiveLocationListener(bool enable)
+#
+pw_add_library(chre.platform.shared.chre_api_gnss STATIC
+  SOURCES
+    chre_api_gnss.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.util
+    chre.util.system
+)
+
+# Implements chre_api/chre/re.h's:
+# - uint32_t chreGetCapabilities()
+# - uint32_t chreGetMessageToHostMaxSize()
+# - uint64_t chreGetAppId(void)
+# - uint32_t chreGetInstanceId(void)
+# - uint64_t chreGetTime()
+# - int64_t chreGetEstimatedHostTimeOffset()
+# - uint32_t chreTimerSet(uint64_t duration, const void *cookie, bool oneShot)
+# - bool chreTimerCancel(uint32_t timerId)
+# - void chreAbort(uint32_t abortCode)
+# - void* chreHeapAlloc(uint32_t bytes)
+# - void chreHeapFree(void *ptr)
+# - chreDebugDumpLog(const char *formatStr, ...)
+#
+# Also provides:
+# - void platform_chreDebugDumpVaLog(const char *formatStr, va_list args)
+#
+# Does not provide:
+# - void chreLog(enum chreLogLevel level, const char *formatStr, ...)
+pw_add_library(chre.platform.shared.chre_api_re STATIC
+  HEADERS
+    include/chre/platform/shared/debug_dump.h
+  PUBLIC_INCLUDES
+    include
+  SOURCES
+    chre_api_re.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.platform.fatal_error
+    chre.util
+)
+
+# Implements chre_api/chre/sensor.h's:
+# - bool chreSensorFindDefault(uint8_t sensorType, uint32_t *handle)
+# - bool chreSensorFind(uint8_t sensorType, uint8_t sensorIndex,
+#                       uint32_t *handle)
+# - bool chreGetSensorInfo(uint32_t sensorHandle, struct chreSensorInfo *info)
+# - bool chreGetSensorSamplingStatus(uint32_t sensorHandle,
+#                                    struct chreSensorSamplingStatus *status)
+# - bool chreSensorConfigure(uint32_t sensorHandle,
+#                            enum chreSensorConfigureMode mode,
+#                            uint64_t interval, uint64_t latency)
+# - bool chreSensorConfigureBiasEvents(uint32_t sensorHandle, bool enable)
+# - bool chreSensorGetThreeAxisBias(uint32_t sensorHandle,
+#                                   struct chreSensorThreeAxisData *bias)
+# - bool chreSensorFlushAsync(uint32_t sensorHandle, const void *cookie)
+#
+pw_add_library(chre.platform.shared.chre_api_sensor STATIC
+  SOURCES
+    chre_api_sensor.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.util
+)
+
+# Implements chre_api/chre/user_settings.h's:
+# - int8_t chreUserSettingGetState(uint8_t setting)
+# - void chreUserSettingConfigureEvents(uint8_t setting, bool enable)
+#
+pw_add_library(chre.platform.shared.chre_api_user_settings STATIC
+  SOURCES
+    chre_api_user_settings.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.util
+)
+
+# Implements chre_api/chre/version.h's:
+# - uint32_t chreGetApiVersion(void)
+# - uint32_t chreGetVersion(void)
+# - uint64_t chreGetPlatformId(void)
+#
+pw_add_library(chre.platform.shared.chre_api_version STATIC
+  SOURCES
+    chre_api_version.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.util
+)
+
+# Implements chre_api/chre/wifi.h's:
+# - uint32_t chreWifiGetCapabilities()
+# - bool chreWifiNanGetCapabilities(
+#       struct chreWifiNanCapabilities *capabilities)
+# - bool chreWifiConfigureScanMonitorAsync(bool enable, const void *cookie)
+# - bool chreWifiRequestScanAsync(const struct chreWifiScanParams *params,
+#                                 const void *cookie)
+# - bool chreWifiRequestRangingAsync(
+#       const struct chreWifiRangingParams *params, const void *cookie)
+# - bool chreWifiNanSubscribe(struct chreWifiNanSubscribeConfig *config,
+#                             const void *cookie)
+# - bool chreWifiNanSubscribeCancel(uint32_t subscriptionId)
+# - bool chreWifiNanRequestRangingAsync(
+#       const struct chreWifiNanRangingParams *params, const void *cookie)
+#
+pw_add_library(chre.platform.shared.chre_api_wifi STATIC
+  SOURCES
+    chre_api_wifi.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.util
+    chre.util.system
+)
+
+# Implements chre_api/chre/wwan.h's:
+# - uint32_t chreWwanGetCapabilities()
+# - bool chreWwanGetCellInfoAsync(const void *cookie)
+#
+pw_add_library(chre.platform.shared.chre_api_wwan STATIC
+  SOURCES
+    chre_api_wwan.cc
+  PRIVATE_DEPS
+    chre.chre_api.facade
+    chre.core
+    chre.util
+    chre.util.system
+)
+
+pw_add_library(chre.platform.shared.dlfcn STATIC
+  HEADERS
+    include/chre/platform/shared/libc/dlfcn.h
+  PUBLIC_INCLUDES
+    include/chre/platform/shared/libc
+  SOURCES
+    dlfcn.cc
+  PRIVATE_DEPS
+    chre.platform.assert
+    chre.platform.log
+    chre.platform.shared.nanoapp_loader
+    chre.util
+)
+
+pw_add_library(chre.platform.shared.generated.host_messages_generated INTERFACE
+  HEADERS
+    include/chre/platform/shared/generated/host_messages_generated.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre_third_party.flatbuffers
+)
+
+pw_add_library(chre.platform.shared.host_protocol_chre STATIC
+  HEADERS
+    include/chre/platform/shared/host_protocol_chre.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.core
+    chre.platform.shared.generated.host_messages_generated
+    chre.platform.shared.host_protocol_common
+    chre.util
+    chre.util.flatbuffers
+    chre.chre_api
+    chre_third_party.flatbuffers
+  SOURCES
+    host_protocol_chre.cc
+    host_link.cc
+  PRIVATE_DEPS
+    chre.core
+    chre.platform.log
+    chre.platform.shared.host_protocol_chre
+    chre.platform.shared.nanoapp_load_manager
+)
+
+pw_add_library(chre.platform.shared.host_protocol_common STATIC
+  HEADERS
+    include/chre/platform/shared/host_protocol_common.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util.system
+    chre_third_party.flatbuffers
+  SOURCES
+    host_protocol_common.cc
+  PRIVATE_DEPS
+    chre.platform.shared.generated.host_messages_generated
+)
+
+pw_add_library(chre.platform.shared.loader_util INTERFACE
+  HEADERS
+    include/chre/platform/shared/loader_util.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.variant.config
+)
+
+pw_add_library(chre.platform.shared.log_buffer STATIC
+  HEADERS
+    include/chre/platform/shared/log_buffer.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.core
+    chre.platform.mutex
+    chre.platform.shared.bt_snoop_log
+    chre.platform.shared.generated.host_messages_generated
+  SOURCES
+    log_buffer.cc
+  PRIVATE_DEPS
+    chre.platform.assert
+    chre.util
+)
+
+# This requires the backend to provide an implementation for:
+# - void LogBufferManager::preSecondaryBufferUse() const
+pw_add_facade(chre.platform.shared.log_buffer_manager STATIC
+  BACKEND
+    chre.platform.shared.log_buffer_manager_BACKEND
+  HEADERS
+    include/chre/platform/shared/log_buffer_manager.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+    chre.platform.assert
+    chre.platform.condition_variable
+    chre.platform.mutex
+    chre.platform.shared.bt_snoop_log.facade
+    chre.platform.shared.generated.host_messages_generated
+    chre.platform.shared.log_buffer
+    chre.util
+  SOURCES
+    log_buffer_manager.cc
+  PRIVATE_DEPS
+    chre.core
+    chre.platform.log
+    chre.util
+    pw_log_tokenized.config
+    pw_tokenizer
+)
+
+pw_add_facade(chre.platform.shared.memory INTERFACE
+  BACKEND
+    chre.platform.shared.memory_BACKEND
+  HEADERS
+    include/chre/platform/shared/memory.h
+  PUBLIC_INCLUDES
+    include
+)
+
+# Partial backend for chre.platform.memory_manger which implements:
+# 1) void *MemoryManager::nanoappAlloc(Nanoapp *app, uint32_t bytes)
+# 2) void MemoryManager::nanoappFree(Nanoapp *app, void *ptr)
+# 3) uint32_t MemoryManager::nanoappFreeAll(Nanoapp *app)
+# 4) void MemoryManager::logStateToBuffer(DebugDumpWrapper &debugDump) const
+pw_add_library(chre.platform.shared.memory_manager STATIC
+  SOURCES
+    memory_manager.cc
+  PRIVATE_DEPS
+    chre.platform.assert
+    chre.platform.memory_manager.facade
+    chre.util.system
+)
+
+pw_add_library(chre.platform.shared.nanoapp_dso_util STATIC
+  HEADERS
+    include/chre/platform/shared/nanoapp_dso_util.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.platform.shared.nanoapp_support_lib_dso
+  SOURCES
+    nanoapp/nanoapp_dso_util.cc
+  PRIVATE_DEPS
+    chre.platform.log
+)
+
+# This is itself a facade for:
+# 1) bool NanoappLoader::relocateTable(DynamicHeader *dyn, int tag)
+# 2) bool NanoappLoader::resolveGot()
+# 3) if CHREX_SYMBOL_EXTENSIONS is set, providing
+#    `const ExportedData chre::kVendorExportedData[]` via
+#    `chre/extensions/platform/symbol_list.h`.
+pw_add_facade(chre.platform.shared.nanoapp_loader STATIC
+  BACKEND
+    chre.platform.shared.nanoapp_loader_BACKEND
+  HEADERS
+    include/chre/platform/shared/nanoapp_loader.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.platform.shared.loader_util
+  SOURCES
+    nanoapp_loader.cc
+  PRIVATE_DEPS
+    chre.chre_api
+    chre.platform.assert
+    chre.platform.fatal_error
+    chre.platform.shared.chre_api_re
+    chre.platform.shared.dlfcn
+    chre.platform.shared.memory
+    chre.platform.shared.nanoapp_tokenized_log
+    chre.platform.shared.platform_cache_management
+    chre.variant.config
+)
+
+pw_add_library(chre.platform.shared.nanoapp_load_manager STATIC
+  HEADERS
+    include/chre/platform/shared/nanoapp_load_manager.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.core
+    chre.util
+  SOURCES
+    nanoapp_load_manager.cc
+)
+
+# Note that this does NOT actually implement, which are only required for
+# nanoapp builds:
+# - const struct chreNslNanoappInfo *getChreNslDsoNanoappInfo()
+# - bool chreNslDsoGetApi(uint32_t apiId, void **apiHandle)
+pw_add_library(chre.platform.shared.nanoapp_support_lib_dso INTERFACE
+  HEADERS
+    include/chre/platform/shared/nanoapp_support_lib_dso.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+)
+
+pw_add_library(chre.platform.shared.nanoapp_tokenized_log STATIC
+  HEADERS
+    nanoapp/include/chre/platform/shared/nanoapp/tokenized_log.h
+  PUBLIC_INCLUDES
+    nanoapp/include
+  PUBLIC_DEPS
+    chre.chre_api
+  SOURCES
+    tokenized_log.cc
+  PRIVATE_DEPS
+    chre.core
+    chre.platform.log
+    chre.platform.shared.log_buffer_manager
+    pw_log_tokenized.config
+    pw_tokenizer
+)
+
+pw_add_library(chre.platform.shared.pal_audio_stub STATIC
+  SOURCES
+    pal_audio_stub.cc
+  PRIVATE_DEPS
+    chre.pal.audio.facade
+)
+
+pw_add_library(chre.platform.shared.pal_ble_stub STATIC
+  SOURCES
+    pal_ble_stub.cc
+  PRIVATE_DEPS
+    chre.pal.ble.facade
+)
+
+pw_add_library(chre.platform.shared.pal_gnss_stub STATIC
+  SOURCES
+    pal_gnss_stub.cc
+  PRIVATE_DEPS
+    chre.pal.gnss.facade
+)
+
+pw_add_library(chre.platform.shared.pal_sensor_stub STATIC
+  SOURCES
+    pal_sensor_stub.cc
+  PRIVATE_DEPS
+    chre.pal.sensor.facade
+)
+
+pw_add_facade(chre.platform.shared.pal_system_api STATIC
+  BACKEND
+    chre.platform.shared.pal_system_api_BACKEND
+  HEADERS
+    include/chre/platform/shared/pal_system_api.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.pal.system
+  SOURCES
+    pal_system_api.cc
+  PRIVATE_DEPS
+   chre.platform.log
+   chre.platform.memory
+   chre.platform.system_time
+)
+
+pw_add_library(chre.platform.shared.pal_wifi_stub STATIC
+  SOURCES
+    pal_wifi_stub.cc
+  PRIVATE_DEPS
+    chre.pal.wifi.facade
+)
+
+pw_add_library(chre.platform.shared.pal_wwan_stub STATIC
+  SOURCES
+    pal_wwan_stub.cc
+  PRIVATE_DEPS
+    chre.pal.wwan.facade
+)
+
+pw_add_library(chre.platform.shared.platform_ble_pal STATIC
+  HEADERS
+    public_platform_ble_pal/chre/target_platform/platform_ble_base.h
+  PUBLIC_INCLUDES
+    public_platform_ble_pal
+  PUBLIC_DEPS
+    chre.pal.ble
+    chre.platform.shared.platform_pal
+  SOURCES
+    platform_ble.cc
+  PRIVATE_DEPS
+    chre.chre_api
+    chre.core
+    chre.platform.log
+    chre.platform.platform_ble.facade
+    chre.platform.shared.bt_snoop_log
+    chre.platform.shared.pal_system_api
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.shared.platform_cache_management INTERFACE
+  BACKEND
+    chre.platform.shared.platform_cache_management_BACKEND
+  HEADERS
+    include/chre/platform/shared/platform_cache_management.h
+  PUBLIC_INCLUDES
+    include
+)
+
+# The optional CHPP and ASH integration is not supported.
+pw_add_library(chre.platform.shared.platform_debug_dump_manager STATIC
+  HEADERS
+    public_platform_debug_dump_manager/chre/target_platform/platform_debug_dump_manager_base.h
+  PUBLIC_INCLUDES
+    public_platform_debug_dump_manager
+  PUBLIC_DEPS
+    chre.variant.config
+  SOURCES
+    platform_debug_dump_manager.cc
+  PRIVATE_DEPS
+    chre.core
+    chre.platform.host_link
+    chre.platform.log
+    chre.platform.platform_debug_dump_manager.facade
+    chre.util
+)
+
+pw_add_library(chre.platform.shared.platform_gnss_pal STATIC
+  HEADERS
+    public_platform_gnss_pal/chre/target_platform/platform_gnss_base.h
+  PUBLIC_INCLUDES
+    public_platform_gnss_pal
+  PUBLIC_DEPS
+    chre.pal.gnss
+    chre.platform.shared.platform_pal
+  SOURCES
+    platform_gnss.cc
+  PRIVATE_DEPS
+    chre.core
+    chre.platform.log
+    chre.platform.platform_gnss.facade
+    chre.platform.shared.pal_system_api
+    chre.variant.config
+)
+
+pw_add_facade(chre.platform.shared.platform_pal INTERFACE
+  BACKEND
+    chre.platform.shared.platform_pal_BACKEND
+  HEADERS
+    include/chre/platform/shared/platform_pal.h
+  PUBLIC_INCLUDES
+    include
+)
+
+pw_add_library(chre.platform.shared.platform_wifi_pal STATIC
+  HEADERS
+    public_platform_wifi_pal/chre/target_platform/platform_wifi_base.h
+  PUBLIC_INCLUDES
+    public_platform_wifi_pal
+  PUBLIC_DEPS
+    chre.pal.wifi
+    chre.platform.shared.platform_pal
+  SOURCES
+    platform_wifi.cc
+  PRIVATE_DEPS
+    chre.core
+    chre.platform.log
+    chre.platform.platform_wifi.facade
+    chre.platform.shared.pal_system_api
+    chre.util.system
+    chre.variant.config
+)
+
+pw_add_library(chre.platform.shared.platform_wwan_pal STATIC
+  HEADERS
+    public_platform_wwan_pal/chre/target_platform/platform_wwan_base.h
+  PUBLIC_INCLUDES
+    public_platform_wwan_pal
+  PUBLIC_DEPS
+    chre.pal.wwan
+    chre.platform.shared.platform_pal
+  SOURCES
+    platform_wwan.cc
+  PRIVATE_DEPS
+    chre.core
+    chre.platform.log
+    chre.platform.platform_wwan.facade
+    chre.platform.shared.pal_system_api
+    chre.variant.config
+)
diff --git a/platform/shared/backend.cmake b/platform/shared/backend.cmake
new file mode 100644
index 0000000..5d436df
--- /dev/null
+++ b/platform/shared/backend.cmake
@@ -0,0 +1,27 @@
+include_guard(GLOBAL)
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+# Backend for chre.platform.shared.authentication.
+pw_add_backend_variable(chre.platform.shared.authentication_BACKEND)
+
+# Backend for chre.platform.shared.bt_snoop_log.
+pw_add_backend_variable(chre.platform.shared.bt_snoop_log_BACKEND)
+
+# Backend for chre.platform.shared.log_buffer_manager.
+pw_add_backend_variable(chre.platform.shared.log_buffer_manager_BACKEND)
+
+# Backend for chre.platform.shared.memory.
+pw_add_backend_variable(chre.platform.shared.memory_BACKEND)
+
+# Backend for chre.platform.shared.nanoapp_loader.
+pw_add_backend_variable(chre.platform.shared.nanoapp_loader_BACKEND)
+
+# Backend for chre.platform.shared.platform_cache_management.
+pw_add_backend_variable(chre.platform.shared.platform_cache_management_BACKEND)
+
+# Backend for chre.platform.shared.platform_pal.
+pw_add_backend_variable(chre.platform.shared.platform_pal_BACKEND)
+
+# Backend for chre.platform.shared.pal_system_api.
+pw_add_backend_variable(chre.platform.shared.pal_system_api_BACKEND)
diff --git a/platform/shared/chre_api_audio.cc b/platform/shared/chre_api_audio.cc
index adad654..5cec25f 100644
--- a/platform/shared/chre_api_audio.cc
+++ b/platform/shared/chre_api_audio.cc
@@ -20,10 +20,6 @@
 #include "chre/util/macros.h"
 #include "chre/util/system/napp_permissions.h"
 
-#ifdef CHRE_AUDIO_SUPPORT_ENABLED
-#include "chre/platform/platform_audio.h"
-#endif  // CHRE_AUDIO_SUPPORT_ENABLED
-
 using chre::EventLoopManager;
 using chre::EventLoopManagerSingleton;
 using chre::Nanoapp;
@@ -65,3 +61,11 @@
   return false;
 #endif  // CHRE_AUDIO_SUPPORT_ENABLED
 }
+
+DLL_EXPORT bool chreAudioGetStatus(uint32_t handle,
+                                   struct chreAudioSourceStatus *status) {
+  // TODO(b/174590023): either implement or deprecate this API
+  UNUSED_VAR(handle);
+  UNUSED_VAR(status);
+  return false;
+}
diff --git a/platform/shared/chre_api_ble.cc b/platform/shared/chre_api_ble.cc
index ea40387..5b65ac1 100644
--- a/platform/shared/chre_api_ble.cc
+++ b/platform/shared/chre_api_ble.cc
@@ -22,6 +22,7 @@
 
 using chre::EventLoopManager;
 using chre::EventLoopManagerSingleton;
+using chre::Nanoapp;
 using chre::NanoappPermissions;
 
 DLL_EXPORT uint32_t chreBleGetCapabilities() {
@@ -46,7 +47,7 @@
 
 DLL_EXPORT bool chreBleFlushAsync(const void *cookie) {
 #ifdef CHRE_BLE_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_BLE) &&
          EventLoopManagerSingleton::get()->getBleRequestManager().flushAsync(
              nanoapp, cookie);
@@ -60,7 +61,7 @@
     chreBleScanMode mode, uint32_t reportDelayMs,
     const struct chreBleScanFilterV1_9 *filter, const void *cookie) {
 #ifdef CHRE_BLE_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_BLE) &&
          EventLoopManagerSingleton::get()
              ->getBleRequestManager()
@@ -91,7 +92,7 @@
 
 DLL_EXPORT bool chreBleStopScanAsyncV1_9(const void *cookie) {
 #ifdef CHRE_BLE_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_BLE) &&
          EventLoopManagerSingleton::get()->getBleRequestManager().stopScanAsync(
              nanoapp, cookie);
@@ -108,7 +109,7 @@
 DLL_EXPORT bool chreBleReadRssiAsync(uint16_t connectionHandle,
                                      const void *cookie) {
 #ifdef CHRE_BLE_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_BLE) &&
          EventLoopManagerSingleton::get()->getBleRequestManager().readRssiAsync(
              nanoapp, connectionHandle, cookie);
@@ -121,7 +122,7 @@
 
 DLL_EXPORT bool chreBleGetScanStatus(struct chreBleScanStatus *status) {
 #ifdef CHRE_BLE_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_BLE) &&
          EventLoopManagerSingleton::get()->getBleRequestManager().getScanStatus(
              status);
diff --git a/platform/shared/chre_api_core.cc b/platform/shared/chre_api_core.cc
index 780308b..3b5d7db 100644
--- a/platform/shared/chre_api_core.cc
+++ b/platform/shared/chre_api_core.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "chre_api/chre/event.h"
+
 #include <cstdarg>
 #include <cstdio>
 #include <cstring>
@@ -21,19 +23,15 @@
 #include "chre/core/event_loop_manager.h"
 #include "chre/core/host_comms_manager.h"
 #include "chre/core/host_endpoint_manager.h"
-#include "chre/platform/fatal_error.h"
 #include "chre/platform/log.h"
 #include "chre/util/macros.h"
 #include "chre/util/system/napp_permissions.h"
-#include "chre_api/chre/event.h"
-#include "chre_api/chre/re.h"
 
-using ::chre::EventLoop;
-using ::chre::EventLoopManager;
-using ::chre::EventLoopManagerSingleton;
-using ::chre::handleNanoappAbort;
-using ::chre::HostCommsManager;
-using ::chre::Nanoapp;
+using chre::EventLoop;
+using chre::EventLoopManager;
+using chre::EventLoopManagerSingleton;
+using chre::HostCommsManager;
+using chre::Nanoapp;
 
 namespace {
 
@@ -80,19 +78,6 @@
 
 }  // namespace
 
-DLL_EXPORT void chreAbort(uint32_t /* abortCode */) {
-  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
-
-  // TODO: we should cleanly unload the nanoapp, release all of its resources,
-  // and send an abort notification to the host so as to localize the impact to
-  // the calling nanoapp
-  if (nanoapp == nullptr) {
-    FATAL_ERROR("chreAbort called in unknown context");
-  } else {
-    handleNanoappAbort(*nanoapp);
-  }
-}
-
 DLL_EXPORT bool chreSendEvent(uint16_t eventType, void *eventData,
                               chreEventCompleteFunction *freeCallback,
                               uint32_t targetInstanceId) {
@@ -183,12 +168,12 @@
 }
 
 DLL_EXPORT void chreConfigureNanoappInfoEvents(bool enable) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   nanoapp->configureNanoappInfoEvents(enable);
 }
 
 DLL_EXPORT void chreConfigureHostSleepStateEvents(bool enable) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   nanoapp->configureHostSleepEvents(enable);
 }
 
@@ -200,19 +185,19 @@
 }
 
 DLL_EXPORT void chreConfigureDebugDumpEvent(bool enable) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   nanoapp->configureDebugDumpEvent(enable);
 }
 
 DLL_EXPORT bool chreConfigureHostEndpointNotifications(uint16_t hostEndpointId,
                                                        bool enable) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->configureHostEndpointNotifications(hostEndpointId, enable);
 }
 
 DLL_EXPORT bool chrePublishRpcServices(struct chreNanoappRpcService *services,
                                        size_t numServices) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->publishRpcServices(services, numServices);
 }
 
diff --git a/platform/shared/chre_api_gnss.cc b/platform/shared/chre_api_gnss.cc
index f1f7706..7e46bc2 100644
--- a/platform/shared/chre_api_gnss.cc
+++ b/platform/shared/chre_api_gnss.cc
@@ -24,13 +24,12 @@
 using chre::EventLoopManager;
 using chre::EventLoopManagerSingleton;
 using chre::Milliseconds;
+using chre::Nanoapp;
 using chre::NanoappPermissions;
 
 DLL_EXPORT uint32_t chreGnssGetCapabilities() {
 #ifdef CHRE_GNSS_SUPPORT_ENABLED
-  return chre::EventLoopManagerSingleton::get()
-      ->getGnssManager()
-      .getCapabilities();
+  return EventLoopManagerSingleton::get()->getGnssManager().getCapabilities();
 #else
   return CHRE_GNSS_CAPABILITIES_NONE;
 #endif  // CHRE_GNSS_SUPPORT_ENABLED
@@ -41,9 +40,9 @@
     [[maybe_unused]] uint32_t minTimeToNextFixMs,
     [[maybe_unused]] const void *cookie) {
 #ifdef CHRE_GNSS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_GNSS) &&
-         chre::EventLoopManagerSingleton::get()
+         EventLoopManagerSingleton::get()
              ->getGnssManager()
              .getLocationSession()
              .addRequest(nanoapp, Milliseconds(minIntervalMs),
@@ -56,9 +55,9 @@
 DLL_EXPORT bool chreGnssLocationSessionStopAsync(
     [[maybe_unused]] const void *cookie) {
 #ifdef CHRE_GNSS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_GNSS) &&
-         chre::EventLoopManagerSingleton::get()
+         EventLoopManagerSingleton::get()
              ->getGnssManager()
              .getLocationSession()
              .removeRequest(nanoapp, cookie);
@@ -70,9 +69,9 @@
 DLL_EXPORT bool chreGnssMeasurementSessionStartAsync(
     [[maybe_unused]] uint32_t minIntervalMs, [[maybe_unused]] const void *cookie) {
 #ifdef CHRE_GNSS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_GNSS) &&
-         chre::EventLoopManagerSingleton::get()
+         EventLoopManagerSingleton::get()
              ->getGnssManager()
              .getMeasurementSession()
              .addRequest(nanoapp, Milliseconds(minIntervalMs),
@@ -85,9 +84,9 @@
 DLL_EXPORT bool chreGnssMeasurementSessionStopAsync(
     [[maybe_unused]] const void *cookie) {
 #ifdef CHRE_GNSS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_GNSS) &&
-         chre::EventLoopManagerSingleton::get()
+         EventLoopManagerSingleton::get()
              ->getGnssManager()
              .getMeasurementSession()
              .removeRequest(nanoapp, cookie);
@@ -99,9 +98,9 @@
 DLL_EXPORT bool chreGnssConfigurePassiveLocationListener(
     [[maybe_unused]] bool enable) {
 #ifdef CHRE_GNSS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_GNSS) &&
-         chre::EventLoopManagerSingleton::get()
+         EventLoopManagerSingleton::get()
              ->getGnssManager()
              .configurePassiveLocationListener(nanoapp, enable);
 #else
diff --git a/platform/shared/chre_api_re.cc b/platform/shared/chre_api_re.cc
index 11102ed..3d7c8fd 100644
--- a/platform/shared/chre_api_re.cc
+++ b/platform/shared/chre_api_re.cc
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
+#include "chre_api/chre/re.h"
+
 #include "chre/core/event_loop.h"
 #include "chre/core/event_loop_manager.h"
-#include "chre/platform/assert.h"
-#include "chre/platform/memory.h"
+#include "chre/platform/fatal_error.h"
 #include "chre/platform/shared/debug_dump.h"
 #include "chre/platform/system_time.h"
 #include "chre/util/macros.h"
-#include "chre_api/chre/re.h"
 
 using chre::EventLoopManager;
 using chre::EventLoopManagerSingleton;
+using chre::handleNanoappAbort;
+using chre::Nanoapp;
 
 DLL_EXPORT uint32_t chreGetCapabilities() {
   uint32_t capabilities = CHRE_CAPABILITIES_NONE;
@@ -37,26 +39,27 @@
 }
 
 DLL_EXPORT uint32_t chreGetMessageToHostMaxSize() {
-#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
-
-#ifndef CHRE_LARGE_PAYLOAD_MAX_SIZE
-  static_assert(false,
-                "CHRE_LARGE_PAYLOAD_MAX_SIZE must be defined if "
-                "CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED is enabled");
-#else
+#ifdef CHRE_LARGE_PAYLOAD_MAX_SIZE
   static_assert(CHRE_LARGE_PAYLOAD_MAX_SIZE >= CHRE_MESSAGE_TO_HOST_MAX_SIZE,
                 "CHRE_LARGE_PAYLOAD_MAX_SIZE must be greater than or equal to "
                 "CHRE_MESSAGE_TO_HOST_MAX_SIZE");
 
+#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
   static_assert(CHRE_LARGE_PAYLOAD_MAX_SIZE >= 32000,
                 "CHRE_LARGE_PAYLOAD_MAX_SIZE must be greater than or equal to "
                 "32000 when CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED is enabled");
-  return CHRE_LARGE_PAYLOAD_MAX_SIZE;
-#endif  // CHRE_LARGE_PAYLOAD_MAX_SIZE
+#endif
 
+  return CHRE_LARGE_PAYLOAD_MAX_SIZE;
 #else
+#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
+  static_assert(false,
+                "CHRE_LARGE_PAYLOAD_MAX_SIZE must be defined if "
+                "CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED is enabled");
+#endif
+
   return CHRE_MESSAGE_TO_HOST_MAX_SIZE;
-#endif  // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
+#endif  // CHRE_LARGE_PAYLOAD_MAX_SIZE
 }
 
 DLL_EXPORT uint64_t chreGetTime() {
@@ -68,18 +71,18 @@
 }
 
 DLL_EXPORT uint64_t chreGetAppId(void) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->getAppId();
 }
 
 DLL_EXPORT uint32_t chreGetInstanceId(void) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->getInstanceId();
 }
 
 DLL_EXPORT uint32_t chreTimerSet(uint64_t duration, const void *cookie,
                                  bool oneShot) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return EventLoopManagerSingleton::get()
       ->getEventLoop()
       .getTimerPool()
@@ -87,32 +90,39 @@
 }
 
 DLL_EXPORT bool chreTimerCancel(uint32_t timerId) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return EventLoopManagerSingleton::get()
       ->getEventLoop()
       .getTimerPool()
       .cancelNanoappTimer(nanoapp, timerId);
 }
 
+DLL_EXPORT void chreAbort(uint32_t /* abortCode */) {
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  if (nanoapp == nullptr) {
+    FATAL_ERROR("chreAbort called in unknown context");
+  } else {
+    handleNanoappAbort(*nanoapp);
+  }
+}
+
 DLL_EXPORT void *chreHeapAlloc(uint32_t bytes) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
-  return chre::EventLoopManagerSingleton::get()
-      ->getMemoryManager()
-      .nanoappAlloc(nanoapp, bytes);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  return EventLoopManagerSingleton::get()->getMemoryManager().nanoappAlloc(
+      nanoapp, bytes);
 }
 
 DLL_EXPORT void chreHeapFree(void *ptr) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
-  chre::EventLoopManagerSingleton::get()->getMemoryManager().nanoappFree(
-      nanoapp, ptr);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  EventLoopManagerSingleton::get()->getMemoryManager().nanoappFree(nanoapp,
+                                                                   ptr);
 }
 
 DLL_EXPORT void platform_chreDebugDumpVaLog(const char *formatStr,
                                             va_list args) {
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
-  chre::EventLoopManagerSingleton::get()
-      ->getDebugDumpManager()
-      .appendNanoappLog(*nanoapp, formatStr, args);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  EventLoopManagerSingleton::get()->getDebugDumpManager().appendNanoappLog(
+      *nanoapp, formatStr, args);
 }
 
 DLL_EXPORT void chreDebugDumpLog(const char *formatStr, ...) {
diff --git a/platform/shared/chre_api_sensor.cc b/platform/shared/chre_api_sensor.cc
index 9b7b434..6f5b408 100644
--- a/platform/shared/chre_api_sensor.cc
+++ b/platform/shared/chre_api_sensor.cc
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
+#include "chre_api/chre/sensor.h"
+
 #include "chre/core/event_loop_manager.h"
 #include "chre/core/sensor_request.h"
-#include "chre/core/sensor_type_helpers.h"
 #include "chre/util/macros.h"
 #include "chre/util/time.h"
-#include "chre_api/chre/sensor.h"
 
 using chre::EventLoopManager;
 using chre::EventLoopManagerSingleton;
+using chre::Nanoapp;
 using chre::Nanoseconds;
 using chre::SensorMode;
 using chre::SensorRequest;
@@ -36,7 +37,7 @@
 DLL_EXPORT bool chreSensorFind(uint8_t sensorType, uint8_t sensorIndex,
                                uint32_t *handle) {
 #if CHRE_SENSORS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return EventLoopManagerSingleton::get()
       ->getSensorRequestManager()
       .getSensorHandleForNanoapp(sensorType, sensorIndex, *nanoapp, handle);
@@ -53,7 +54,7 @@
 #ifdef CHRE_SENSORS_SUPPORT_ENABLED
   CHRE_ASSERT(info);
 
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
 
   bool success = false;
   if (info != nullptr) {
@@ -92,7 +93,7 @@
                                     enum chreSensorConfigureMode mode,
                                     uint64_t interval, uint64_t latency) {
 #ifdef CHRE_SENSORS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   SensorMode sensorMode = getSensorModeFromEnum(mode);
   SensorRequest sensorRequest(nanoapp->getInstanceId(), sensorMode,
                               Nanoseconds(interval), Nanoseconds(latency));
@@ -111,7 +112,7 @@
 DLL_EXPORT bool chreSensorConfigureBiasEvents(uint32_t sensorHandle,
                                               bool enable) {
 #ifdef CHRE_SENSORS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return EventLoopManagerSingleton::get()
       ->getSensorRequestManager()
       .configureBiasEvents(nanoapp, sensorHandle, enable);
@@ -138,7 +139,7 @@
 DLL_EXPORT bool chreSensorFlushAsync(uint32_t sensorHandle,
                                      const void *cookie) {
 #ifdef CHRE_SENSORS_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return EventLoopManagerSingleton::get()->getSensorRequestManager().flushAsync(
       nanoapp, sensorHandle, cookie);
 #else   // CHRE_SENSORS_SUPPORT_ENABLED
diff --git a/platform/shared/chre_api_user_settings.cc b/platform/shared/chre_api_user_settings.cc
index 8ebba49..79394e7 100644
--- a/platform/shared/chre_api_user_settings.cc
+++ b/platform/shared/chre_api_user_settings.cc
@@ -21,11 +21,12 @@
 #include "chre/util/macros.h"
 
 using chre::EventLoopManager;
+using chre::EventLoopManagerSingleton;
 using chre::Nanoapp;
 using chre::Setting;
 
 DLL_EXPORT int8_t chreUserSettingGetState(uint8_t setting) {
-  return chre::EventLoopManagerSingleton::get()
+  return EventLoopManagerSingleton::get()
       ->getSettingManager()
       .getSettingStateAsInt8(setting);
 }
diff --git a/platform/shared/chre_api_wifi.cc b/platform/shared/chre_api_wifi.cc
index 02b3785..8617100 100644
--- a/platform/shared/chre_api_wifi.cc
+++ b/platform/shared/chre_api_wifi.cc
@@ -22,11 +22,12 @@
 
 using chre::EventLoopManager;
 using chre::EventLoopManagerSingleton;
+using chre::Nanoapp;
 using chre::NanoappPermissions;
 
 DLL_EXPORT uint32_t chreWifiGetCapabilities() {
 #ifdef CHRE_WIFI_SUPPORT_ENABLED
-  return chre::EventLoopManagerSingleton::get()
+  return EventLoopManagerSingleton::get()
       ->getWifiRequestManager()
       .getCapabilities();
 #else
@@ -37,7 +38,7 @@
 DLL_EXPORT bool chreWifiConfigureScanMonitorAsync(
     [[maybe_unused]] bool enable, [[maybe_unused]] const void *cookie) {
 #ifdef CHRE_WIFI_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_WIFI) &&
          EventLoopManagerSingleton::get()
              ->getWifiRequestManager()
@@ -51,7 +52,7 @@
     [[maybe_unused]] const struct chreWifiScanParams *params,
     [[maybe_unused]] const void *cookie) {
 #ifdef CHRE_WIFI_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_WIFI) &&
          EventLoopManagerSingleton::get()->getWifiRequestManager().requestScan(
              nanoapp, params, cookie);
@@ -64,7 +65,7 @@
     [[maybe_unused]] const struct chreWifiRangingParams *params,
     [[maybe_unused]] const void *cookie) {
 #ifdef CHRE_WIFI_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_WIFI) &&
          EventLoopManagerSingleton::get()
              ->getWifiRequestManager()
@@ -79,7 +80,7 @@
     [[maybe_unused]] struct chreWifiNanSubscribeConfig *config,
     [[maybe_unused]] const void *cookie) {
 #if defined(CHRE_WIFI_SUPPORT_ENABLED) && defined(CHRE_WIFI_NAN_SUPPORT_ENABLED)
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_WIFI) &&
          EventLoopManagerSingleton::get()->getWifiRequestManager().nanSubscribe(
              nanoapp, config, cookie);
@@ -91,7 +92,7 @@
 DLL_EXPORT bool chreWifiNanSubscribeCancel(
     [[maybe_unused]] uint32_t subscriptionId) {
 #if defined(CHRE_WIFI_SUPPORT_ENABLED) && defined(CHRE_WIFI_NAN_SUPPORT_ENABLED)
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_WIFI) &&
          EventLoopManagerSingleton::get()
              ->getWifiRequestManager()
@@ -105,7 +106,7 @@
     [[maybe_unused]] const struct chreWifiNanRangingParams *params,
     [[maybe_unused]] const void *cookie) {
 #if defined(CHRE_WIFI_SUPPORT_ENABLED) && defined(CHRE_WIFI_NAN_SUPPORT_ENABLED)
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_WIFI) &&
          EventLoopManagerSingleton::get()
              ->getWifiRequestManager()
diff --git a/platform/shared/chre_api_wwan.cc b/platform/shared/chre_api_wwan.cc
index aa47722..b7bb634 100644
--- a/platform/shared/chre_api_wwan.cc
+++ b/platform/shared/chre_api_wwan.cc
@@ -22,11 +22,12 @@
 
 using chre::EventLoopManager;
 using chre::EventLoopManagerSingleton;
+using chre::Nanoapp;
 using chre::NanoappPermissions;
 
 DLL_EXPORT uint32_t chreWwanGetCapabilities() {
 #ifdef CHRE_WWAN_SUPPORT_ENABLED
-  return chre::EventLoopManagerSingleton::get()
+  return EventLoopManagerSingleton::get()
       ->getWwanRequestManager()
       .getCapabilities();
 #else
@@ -36,9 +37,9 @@
 
 DLL_EXPORT bool chreWwanGetCellInfoAsync([[maybe_unused]] const void *cookie) {
 #ifdef CHRE_WWAN_SUPPORT_ENABLED
-  chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+  Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
   return nanoapp->permitPermissionUse(NanoappPermissions::CHRE_PERMS_WWAN) &&
-         chre::EventLoopManagerSingleton::get()
+         EventLoopManagerSingleton::get()
              ->getWwanRequestManager()
              .requestCellInfo(nanoapp, cookie);
 #else
diff --git a/platform/shared/dlfcn.cc b/platform/shared/dlfcn.cc
index 630b73c..d561676 100644
--- a/platform/shared/dlfcn.cc
+++ b/platform/shared/dlfcn.cc
@@ -17,6 +17,7 @@
 #include <dlfcn.h>
 
 #include "chre/platform/assert.h"
+#include "chre/platform/log.h"
 #include "chre/platform/shared/nanoapp_loader.h"
 #include "chre/util/unique_ptr.h"
 
diff --git a/platform/shared/host_link.cc b/platform/shared/host_link.cc
index 7a0c797..2eff593 100644
--- a/platform/shared/host_link.cc
+++ b/platform/shared/host_link.cc
@@ -73,8 +73,13 @@
 
     if (getLoadManager().hasPendingLoadTransaction()) {
       FragmentedLoadInfo info = getLoadManager().getTransactionInfo();
+      LOGW("A pending load transaction already exists (clientId=%" PRIu16
+           ", txnId=%" PRIu32 ", nextFragmentId=%" PRIu32 "). Overriding it",
+           info.hostClientId, info.transactionId, info.nextFragmentId);
+      // Send a failure response to host where nextFragmentId is either current
+      // or future to the host.
       sendFragmentResponse(info.hostClientId, info.transactionId,
-                           0 /* fragmentId */, false /* success */);
+                           info.nextFragmentId, /* success= */ false);
       getLoadManager().markFailure();
     }
 
diff --git a/platform/shared/host_protocol_chre.cc b/platform/shared/host_protocol_chre.cc
index ca0c86b..6224135 100644
--- a/platform/shared/host_protocol_chre.cc
+++ b/platform/shared/host_protocol_chre.cc
@@ -18,6 +18,7 @@
 
 #include <inttypes.h>
 #include <string.h>
+#include <cstdint>
 
 #include "chre/core/event_loop_manager.h"
 #include "chre/core/host_endpoint_manager.h"
@@ -200,13 +201,53 @@
         break;
       }
 
+      case fbs::ChreMessage::BtSocketOpen: {
+        const auto *btSocketOpen =
+            static_cast<const fbs::BtSocketOpen *>(container->message());
+        if (btSocketOpen->channelInfo_type() !=
+            fbs::ChannelInfo::LeCocChannelInfo) {
+          LOGW("Unexpected BT Socket Open Channel Info Type %" PRIu8,
+               static_cast<uint8_t>(btSocketOpen->channelInfo_type()));
+        } else {
+          const auto *leCocChannelInfo =
+              static_cast<const fbs::LeCocChannelInfo *>(
+                  btSocketOpen->channelInfo());
+          const char *name = getStringFromByteVector(btSocketOpen->name());
+          HostMessageHandlers::handleBtSocketOpen(
+              hostClientId, static_cast<uint64_t>(btSocketOpen->socketId()),
+              name, static_cast<uint64_t>(btSocketOpen->endpointId()),
+              static_cast<uint64_t>(btSocketOpen->hubId()),
+              static_cast<uint32_t>(btSocketOpen->aclConnectionHandle()),
+              static_cast<uint32_t>(leCocChannelInfo->localCid()),
+              static_cast<uint32_t>(leCocChannelInfo->remoteCid()),
+              static_cast<uint32_t>(leCocChannelInfo->psm()),
+              static_cast<uint32_t>(leCocChannelInfo->localMtu()),
+              static_cast<uint32_t>(leCocChannelInfo->remoteMtu()),
+              static_cast<uint32_t>(leCocChannelInfo->localMps()),
+              static_cast<uint32_t>(leCocChannelInfo->remoteMps()),
+              static_cast<uint32_t>(leCocChannelInfo->initialRxCredits()),
+              static_cast<uint32_t>(leCocChannelInfo->initialTxCredits()));
+          success = true;
+        }
+        break;
+      }
+
+      case fbs::ChreMessage::BtSocketCloseResponse: {
+        const auto *btSocketCloseResponse =
+            static_cast<const fbs::BtSocketCloseResponse *>(
+                container->message());
+        LOGD("Received BT Socket close response for socketId=%" PRIu64,
+             btSocketCloseResponse->socketId());
+        success = true;
+        break;
+      }
+
       default:
         LOGW("Got invalid/unexpected message type %" PRIu8,
              static_cast<uint8_t>(container->message_type()));
         success = false;
     }
   }
-
   return success;
 }
 
@@ -383,6 +424,29 @@
   finalize(builder, fbs::ChreMessage::NanConfigurationRequest, request.Union());
 }
 
+void HostProtocolChre::encodeBtSocketOpenResponse(
+    ChreFlatBufferBuilder &builder, uint16_t hostClientId, uint64_t socketId,
+    bool success, const char *reason) {
+  auto reasonOffset = addStringAsByteVector(builder, reason);
+  auto socketOpenResponse = fbs::CreateBtSocketOpenResponse(
+      builder, socketId,
+      success ? fbs::BtSocketOpenStatus::SUCCESS
+              : fbs::BtSocketOpenStatus::FAILURE,
+      reasonOffset);
+  finalize(builder, fbs::ChreMessage::BtSocketOpenResponse,
+           socketOpenResponse.Union(), hostClientId);
+}
+
+void HostProtocolChre::encodeBtSocketClose(ChreFlatBufferBuilder &builder,
+                                           uint16_t hostClientId,
+                                           uint64_t socketId,
+                                           const char *reason) {
+  auto reasonOffset = addStringAsByteVector(builder, reason);
+  auto socketClose = fbs::CreateBtSocketClose(builder, socketId, reasonOffset);
+  finalize(builder, fbs::ChreMessage::BtSocketClose, socketClose.Union(),
+           hostClientId);
+}
+
 bool HostProtocolChre::getSettingFromFbs(fbs::Setting setting,
                                          Setting *chreSetting) {
   bool success = true;
diff --git a/platform/shared/idl/host_messages.fbs b/platform/shared/idl/host_messages.fbs
index cabc3df..2b86db5 100644
--- a/platform/shared/idl/host_messages.fbs
+++ b/platform/shared/idl/host_messages.fbs
@@ -484,6 +484,281 @@
 table PulseRequest {}
 table PulseResponse {}
 
+// LE L2CAP COC channel information.
+table LeCocChannelInfo {
+  // Local Channel Identifier.
+  localCid:int;
+
+  // Remote Channel Identifier.
+  remoteCid:int;
+
+  // Protocol Service Multiplexer.
+  psm:int;
+
+  // Maximum Transmission Unit (MTU, max Rx SDU size) that the local device
+  // will accept for packets received on this channel.
+  localMtu:int;
+
+  // Maximum Transmission Unit (MTU, max Tx SDU size) that the remote device
+  // will accept for packets sent on this channel.
+  remoteMtu:int;
+
+  // Maximum PDU Payload Size (MPS) that the local device will accept for
+  // packets received on this channel.
+  localMps:int;
+
+  // Maximum PDU Payload Size (MPS) that the remote device will accept for
+  // packets sent on this channel.
+  remoteMps:int;
+
+  // The amount of PDUs that the local device will accept from this channel.
+  initialRxCredits:int;
+
+  // The amount of PDUs that the remote device will accept from this channel.
+  initialTxCredits:int;
+}
+
+// Used to specify the channel information of different protocol.
+union ChannelInfo {
+  LeCocChannelInfo
+}
+
+// Request from the host to the offload domain to open a BT socket.
+table BtSocketOpen {
+  // Unique identifier for this socket connection. This ID in the offload
+  // domain matches the ID used on the host side. It is valid only while the
+  // socket is connected.
+  socketId:long;
+
+  // The name of the socket. Nominally a UTF-8 string, but note that we're not
+  // using the built-in "string" data type from FlatBuffers here, because the
+  // generated C++ uses std::string which is not well-supported in the offload
+  // domain. This applies for vendor and toolchain as well.
+  name:[byte];
+
+  // ACL connection handle for the socket.
+  aclConnectionHandle:int;
+
+  // Channel information of the socket protocol.
+  channelInfo:ChannelInfo;
+
+  // The ID of the Hub to which the end point belongs for hardware offload
+  // data path.
+  hubId:long;
+
+  // The ID of the Hub endpoint for hardware offload data path.
+  endpointId:long;
+}
+
+// Status of BT socket open request.
+enum BtSocketOpenStatus : byte {
+  SUCCESS = 0,
+  FAILURE,
+}
+
+// Callback from the offload domain to the host to acknowledge that a BT socket
+// has been opened successfully or has failed to be opened.
+table BtSocketOpenResponse {
+  // Unique identifier for this socket connection.
+  socketId:long;
+
+  // Status indicating success or failure.
+  status:BtSocketOpenStatus;
+
+  // Reason string of the operation failure for debugging purposes.
+  reason:[byte];
+}
+
+// Request from offload domain to host to close a BT socket.
+table BtSocketClose {
+  // Unique identifier for this socket connection.
+  socketId:long;
+
+  // Reason string for closing the socket for debugging purposes
+  reason:[byte];
+}
+
+// Host callback to acknowledge that a BT socket has been closed.
+table BtSocketCloseResponse {
+  // Unique identifier for this socket connection.
+  socketId:long;
+}
+
+table VendorHubInfo {
+  /// The name of the hub. Nominally a UTF-8 string, but note that we're not
+  /// using the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  name:[byte];
+
+  /// Hub version
+  version:uint;
+
+  /// Additional vendor-defined data
+  extended_info:[ubyte];
+}
+
+union MessageHubDetails {
+  HubInfoResponse,
+  VendorHubInfo,
+}
+
+table MessageHub {
+  /// The hub id. -1 is reserved and 0 is invalid. 0x416e64726f696400 represents
+  /// the ContextHub service.
+  id:long;
+
+  /// Details of the message hub.
+  details:MessageHubDetails;
+}
+
+table RegisterMessageHub {
+  hub:MessageHub;
+}
+
+table UnregisterMessageHub {
+  id:long;
+}
+
+table EndpointId {
+  /// Id of the hub hosting the endpoint
+  hubId:long;
+
+  /// The id of the endpoint scoped to the hub
+  id:long;
+}
+
+/// An enum describing the type of an endpoint.
+enum EndpointType : ubyte {
+  INVALID = 0,
+  /// The endpoint is part of the Android Framework
+  FRAMEWORK = 1,
+  /// The endpoint is an Android app
+  APP,
+  /// The endpoint is a native Android program
+  NATIVE,
+  /// The endpoint is a nanoapp
+  NANOAPP,
+  /// A generic, non-nanoapp endpoint
+  GENERIC,
+}
+
+enum RpcFormat : ubyte {
+  /// Fully custom format
+  CUSTOM = 0,
+  /// Stable AIDL defined interface using Binder marshalling
+  AIDL,
+  /// Pigweed RPC defined interface using Protobuf marshalling
+  PW_RPC,
+}
+
+table Service {
+  format:RpcFormat;
+
+  /// Service descriptor. Nominally a UTF-8 string, but note that we're not
+  /// using the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  descriptor:[byte];
+
+  /// Breaking changes should bump the major version.
+  major_version:uint;
+  /// Monotonically increasing minor version.
+  minor_version:uint;
+}
+
+table EndpointInfo {
+  id:EndpointId;
+  type:EndpointType;
+
+  /// Endpoing name. Nominally a UTF-8 string, but note that we're not using
+  /// the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  name:[byte];
+  version:uint;
+
+  /// Values from CHRE_MESSAGE_PERMISSION_*
+  required_permissions:uint;
+  services:[Service];
+}
+
+table RegisterEndpoint {
+  endpoint:EndpointInfo;
+}
+
+table UnregisterEndpoint {
+  endpoint:EndpointId;
+}
+
+/// HAL->CHRE, indicates the HAL is coming up
+table GetMessageHubsAndEndpointsRequest {}
+table GetMessageHubsAndEndpointsResponse {
+  hubs:[MessageHub];
+  endpoints:[EndpointInfo];
+}
+
+table OpenEndpointSessionRequest {
+  id:ushort;
+  fromEndpoint:EndpointId;
+  toEndpoint:EndpointId;
+
+  /// If present, describes the service definition used over the session
+  serviceDescriptor:[byte];
+}
+
+table EndpointSessionOpened {
+  id:ushort;
+}
+
+/// "Reason"s for stopping an endpoint or session over an endpoint.
+enum Reason : ubyte {
+  /// Unspecified reason.
+  UNSPECIFIED = 0,
+  /// Out of memory. There's not enough memory to perform this operation.
+  OUT_OF_MEMORY,
+  /// Timeout. This operation timed out.
+  TIMEOUT,
+  /// Endpoint rejected this openEndpointSession request.
+  OPEN_ENDPOINT_SESSION_REQUEST_REJECTED,
+  /// Endpoint requested closeEndpointSession.
+  CLOSE_ENDPOINT_SESSION_REQUESTED,
+  /// Invalid endpoint.
+  ENDPOINT_INVALID,
+  /// Endpoint is now stopped.
+  ENDPOINT_GONE,
+  /// Endpoint crashed.
+  ENDPOINT_CRASHED,
+  /// Hub was reset or is resetting.
+  HUB_RESET,
+}
+
+table EndpointSessionClosed {
+  id:ushort;
+  reason:Reason;
+}
+
+table EndpointSessionMessage {
+  /// Id of session this message is being sent within
+  session_id:ushort;
+
+  /// Type of the message, specific to the Session protocol
+  type:uint;
+
+  /// Values from CHRE_MESSAGE_PERMISSION_*. Permissions required to read the
+  /// message.
+  permissions:uint;
+  data:[ubyte];
+
+  /// Bitmask of additional flags applied to the message:
+  /// - 0x1: Message delivery status required within 1s
+  flags:uint;
+  sequence_number:uint;
+}
+
+table EndpointSessionMessageDeliveryStatus {
+  /// Id of session the message was sent within
+  session_id:ushort;
+  status:MessageDeliveryStatus;
+}
+
 /// A union that joins together all possible messages. Note that in FlatBuffers,
 /// unions have an implicit type
 union ChreMessage {
@@ -538,6 +813,27 @@
   NanoappTokenDatabaseInfo,
 
   MessageDeliveryStatus,
+
+  BtSocketOpen,
+  BtSocketOpenResponse,
+  BtSocketClose,
+  BtSocketCloseResponse,
+
+  GetMessageHubsAndEndpointsRequest,
+  GetMessageHubsAndEndpointsResponse,
+
+  RegisterMessageHub,
+  UnregisterMessageHub,
+
+  RegisterEndpoint,
+  UnregisterEndpoint,
+
+  OpenEndpointSessionRequest,
+  EndpointSessionOpened,
+  EndpointSessionClosed,
+
+  EndpointSessionMessage,
+  EndpointSessionMessageDeliveryStatus,
 }
 
 struct HostAddress {
diff --git a/platform/shared/include/chre/platform/shared/generated/host_messages_generated.h b/platform/shared/include/chre/platform/shared/generated/host_messages_generated.h
index 86c6cbc..4101439 100644
--- a/platform/shared/include/chre/platform/shared/generated/host_messages_generated.h
+++ b/platform/shared/include/chre/platform/shared/generated/host_messages_generated.h
@@ -111,6 +111,69 @@
 struct PulseResponse;
 struct PulseResponseBuilder;
 
+struct LeCocChannelInfo;
+struct LeCocChannelInfoBuilder;
+
+struct BtSocketOpen;
+struct BtSocketOpenBuilder;
+
+struct BtSocketOpenResponse;
+struct BtSocketOpenResponseBuilder;
+
+struct BtSocketClose;
+struct BtSocketCloseBuilder;
+
+struct BtSocketCloseResponse;
+struct BtSocketCloseResponseBuilder;
+
+struct VendorHubInfo;
+struct VendorHubInfoBuilder;
+
+struct MessageHub;
+struct MessageHubBuilder;
+
+struct RegisterMessageHub;
+struct RegisterMessageHubBuilder;
+
+struct UnregisterMessageHub;
+struct UnregisterMessageHubBuilder;
+
+struct EndpointId;
+struct EndpointIdBuilder;
+
+struct Service;
+struct ServiceBuilder;
+
+struct EndpointInfo;
+struct EndpointInfoBuilder;
+
+struct RegisterEndpoint;
+struct RegisterEndpointBuilder;
+
+struct UnregisterEndpoint;
+struct UnregisterEndpointBuilder;
+
+struct GetMessageHubsAndEndpointsRequest;
+struct GetMessageHubsAndEndpointsRequestBuilder;
+
+struct GetMessageHubsAndEndpointsResponse;
+struct GetMessageHubsAndEndpointsResponseBuilder;
+
+struct OpenEndpointSessionRequest;
+struct OpenEndpointSessionRequestBuilder;
+
+struct EndpointSessionOpened;
+struct EndpointSessionOpenedBuilder;
+
+struct EndpointSessionClosed;
+struct EndpointSessionClosedBuilder;
+
+struct EndpointSessionMessage;
+struct EndpointSessionMessageBuilder;
+
+struct EndpointSessionMessageDeliveryStatus;
+struct EndpointSessionMessageDeliveryStatusBuilder;
+
 struct HostAddress;
 
 struct MessageContainer;
@@ -292,6 +355,270 @@
   return EnumNamesBtSnoopDirection()[index];
 }
 
+enum class ChannelInfo : uint8_t {
+  NONE = 0,
+  LeCocChannelInfo = 1,
+  MIN = NONE,
+  MAX = LeCocChannelInfo
+};
+
+inline const ChannelInfo (&EnumValuesChannelInfo())[2] {
+  static const ChannelInfo values[] = {
+    ChannelInfo::NONE,
+    ChannelInfo::LeCocChannelInfo
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesChannelInfo() {
+  static const char * const names[3] = {
+    "NONE",
+    "LeCocChannelInfo",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameChannelInfo(ChannelInfo e) {
+  if (flatbuffers::IsOutRange(e, ChannelInfo::NONE, ChannelInfo::LeCocChannelInfo)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesChannelInfo()[index];
+}
+
+template<typename T> struct ChannelInfoTraits {
+  static const ChannelInfo enum_value = ChannelInfo::NONE;
+};
+
+template<> struct ChannelInfoTraits<chre::fbs::LeCocChannelInfo> {
+  static const ChannelInfo enum_value = ChannelInfo::LeCocChannelInfo;
+};
+
+bool VerifyChannelInfo(flatbuffers::Verifier &verifier, const void *obj, ChannelInfo type);
+bool VerifyChannelInfoVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
+
+enum class BtSocketOpenStatus : int8_t {
+  SUCCESS = 0,
+  FAILURE = 1,
+  MIN = SUCCESS,
+  MAX = FAILURE
+};
+
+inline const BtSocketOpenStatus (&EnumValuesBtSocketOpenStatus())[2] {
+  static const BtSocketOpenStatus values[] = {
+    BtSocketOpenStatus::SUCCESS,
+    BtSocketOpenStatus::FAILURE
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesBtSocketOpenStatus() {
+  static const char * const names[3] = {
+    "SUCCESS",
+    "FAILURE",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameBtSocketOpenStatus(BtSocketOpenStatus e) {
+  if (flatbuffers::IsOutRange(e, BtSocketOpenStatus::SUCCESS, BtSocketOpenStatus::FAILURE)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesBtSocketOpenStatus()[index];
+}
+
+enum class MessageHubDetails : uint8_t {
+  NONE = 0,
+  HubInfoResponse = 1,
+  VendorHubInfo = 2,
+  MIN = NONE,
+  MAX = VendorHubInfo
+};
+
+inline const MessageHubDetails (&EnumValuesMessageHubDetails())[3] {
+  static const MessageHubDetails values[] = {
+    MessageHubDetails::NONE,
+    MessageHubDetails::HubInfoResponse,
+    MessageHubDetails::VendorHubInfo
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesMessageHubDetails() {
+  static const char * const names[4] = {
+    "NONE",
+    "HubInfoResponse",
+    "VendorHubInfo",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameMessageHubDetails(MessageHubDetails e) {
+  if (flatbuffers::IsOutRange(e, MessageHubDetails::NONE, MessageHubDetails::VendorHubInfo)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesMessageHubDetails()[index];
+}
+
+template<typename T> struct MessageHubDetailsTraits {
+  static const MessageHubDetails enum_value = MessageHubDetails::NONE;
+};
+
+template<> struct MessageHubDetailsTraits<chre::fbs::HubInfoResponse> {
+  static const MessageHubDetails enum_value = MessageHubDetails::HubInfoResponse;
+};
+
+template<> struct MessageHubDetailsTraits<chre::fbs::VendorHubInfo> {
+  static const MessageHubDetails enum_value = MessageHubDetails::VendorHubInfo;
+};
+
+bool VerifyMessageHubDetails(flatbuffers::Verifier &verifier, const void *obj, MessageHubDetails type);
+bool VerifyMessageHubDetailsVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
+
+/// An enum describing the type of an endpoint.
+enum class EndpointType : uint8_t {
+  INVALID = 0,
+  /// The endpoint is part of the Android Framework
+  FRAMEWORK = 1,
+  /// The endpoint is an Android app
+  APP = 2,
+  /// The endpoint is a native Android program
+  NATIVE = 3,
+  /// The endpoint is a nanoapp
+  NANOAPP = 4,
+  /// A generic, non-nanoapp endpoint
+  GENERIC = 5,
+  MIN = INVALID,
+  MAX = GENERIC
+};
+
+inline const EndpointType (&EnumValuesEndpointType())[6] {
+  static const EndpointType values[] = {
+    EndpointType::INVALID,
+    EndpointType::FRAMEWORK,
+    EndpointType::APP,
+    EndpointType::NATIVE,
+    EndpointType::NANOAPP,
+    EndpointType::GENERIC
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesEndpointType() {
+  static const char * const names[7] = {
+    "INVALID",
+    "FRAMEWORK",
+    "APP",
+    "NATIVE",
+    "NANOAPP",
+    "GENERIC",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameEndpointType(EndpointType e) {
+  if (flatbuffers::IsOutRange(e, EndpointType::INVALID, EndpointType::GENERIC)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesEndpointType()[index];
+}
+
+enum class RpcFormat : uint8_t {
+  /// Fully custom format
+  CUSTOM = 0,
+  /// Stable AIDL defined interface using Binder marshalling
+  AIDL = 1,
+  /// Pigweed RPC defined interface using Protobuf marshalling
+  PW_RPC = 2,
+  MIN = CUSTOM,
+  MAX = PW_RPC
+};
+
+inline const RpcFormat (&EnumValuesRpcFormat())[3] {
+  static const RpcFormat values[] = {
+    RpcFormat::CUSTOM,
+    RpcFormat::AIDL,
+    RpcFormat::PW_RPC
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesRpcFormat() {
+  static const char * const names[4] = {
+    "CUSTOM",
+    "AIDL",
+    "PW_RPC",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameRpcFormat(RpcFormat e) {
+  if (flatbuffers::IsOutRange(e, RpcFormat::CUSTOM, RpcFormat::PW_RPC)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesRpcFormat()[index];
+}
+
+/// "Reason"s for stopping an endpoint or session over an endpoint.
+enum class Reason : uint8_t {
+  /// Unspecified reason.
+  UNSPECIFIED = 0,
+  /// Out of memory. There's not enough memory to perform this operation.
+  OUT_OF_MEMORY = 1,
+  /// Timeout. This operation timed out.
+  TIMEOUT = 2,
+  /// Endpoint rejected this openEndpointSession request.
+  OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3,
+  /// Endpoint requested closeEndpointSession.
+  CLOSE_ENDPOINT_SESSION_REQUESTED = 4,
+  /// Invalid endpoint.
+  ENDPOINT_INVALID = 5,
+  /// Endpoint is now stopped.
+  ENDPOINT_GONE = 6,
+  /// Endpoint crashed.
+  ENDPOINT_CRASHED = 7,
+  /// Hub was reset or is resetting.
+  HUB_RESET = 8,
+  MIN = UNSPECIFIED,
+  MAX = HUB_RESET
+};
+
+inline const Reason (&EnumValuesReason())[9] {
+  static const Reason values[] = {
+    Reason::UNSPECIFIED,
+    Reason::OUT_OF_MEMORY,
+    Reason::TIMEOUT,
+    Reason::OPEN_ENDPOINT_SESSION_REQUEST_REJECTED,
+    Reason::CLOSE_ENDPOINT_SESSION_REQUESTED,
+    Reason::ENDPOINT_INVALID,
+    Reason::ENDPOINT_GONE,
+    Reason::ENDPOINT_CRASHED,
+    Reason::HUB_RESET
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesReason() {
+  static const char * const names[10] = {
+    "UNSPECIFIED",
+    "OUT_OF_MEMORY",
+    "TIMEOUT",
+    "OPEN_ENDPOINT_SESSION_REQUEST_REJECTED",
+    "CLOSE_ENDPOINT_SESSION_REQUESTED",
+    "ENDPOINT_INVALID",
+    "ENDPOINT_GONE",
+    "ENDPOINT_CRASHED",
+    "HUB_RESET",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameReason(Reason e) {
+  if (flatbuffers::IsOutRange(e, Reason::UNSPECIFIED, Reason::HUB_RESET)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesReason()[index];
+}
+
 /// A union that joins together all possible messages. Note that in FlatBuffers,
 /// unions have an implicit type
 enum class ChreMessage : uint8_t {
@@ -328,11 +655,26 @@
   PulseResponse = 30,
   NanoappTokenDatabaseInfo = 31,
   MessageDeliveryStatus = 32,
+  BtSocketOpen = 33,
+  BtSocketOpenResponse = 34,
+  BtSocketClose = 35,
+  BtSocketCloseResponse = 36,
+  GetMessageHubsAndEndpointsRequest = 37,
+  GetMessageHubsAndEndpointsResponse = 38,
+  RegisterMessageHub = 39,
+  UnregisterMessageHub = 40,
+  RegisterEndpoint = 41,
+  UnregisterEndpoint = 42,
+  OpenEndpointSessionRequest = 43,
+  EndpointSessionOpened = 44,
+  EndpointSessionClosed = 45,
+  EndpointSessionMessage = 46,
+  EndpointSessionMessageDeliveryStatus = 47,
   MIN = NONE,
-  MAX = MessageDeliveryStatus
+  MAX = EndpointSessionMessageDeliveryStatus
 };
 
-inline const ChreMessage (&EnumValuesChreMessage())[33] {
+inline const ChreMessage (&EnumValuesChreMessage())[48] {
   static const ChreMessage values[] = {
     ChreMessage::NONE,
     ChreMessage::NanoappMessage,
@@ -366,13 +708,28 @@
     ChreMessage::PulseRequest,
     ChreMessage::PulseResponse,
     ChreMessage::NanoappTokenDatabaseInfo,
-    ChreMessage::MessageDeliveryStatus
+    ChreMessage::MessageDeliveryStatus,
+    ChreMessage::BtSocketOpen,
+    ChreMessage::BtSocketOpenResponse,
+    ChreMessage::BtSocketClose,
+    ChreMessage::BtSocketCloseResponse,
+    ChreMessage::GetMessageHubsAndEndpointsRequest,
+    ChreMessage::GetMessageHubsAndEndpointsResponse,
+    ChreMessage::RegisterMessageHub,
+    ChreMessage::UnregisterMessageHub,
+    ChreMessage::RegisterEndpoint,
+    ChreMessage::UnregisterEndpoint,
+    ChreMessage::OpenEndpointSessionRequest,
+    ChreMessage::EndpointSessionOpened,
+    ChreMessage::EndpointSessionClosed,
+    ChreMessage::EndpointSessionMessage,
+    ChreMessage::EndpointSessionMessageDeliveryStatus
   };
   return values;
 }
 
 inline const char * const *EnumNamesChreMessage() {
-  static const char * const names[34] = {
+  static const char * const names[49] = {
     "NONE",
     "NanoappMessage",
     "HubInfoRequest",
@@ -406,13 +763,28 @@
     "PulseResponse",
     "NanoappTokenDatabaseInfo",
     "MessageDeliveryStatus",
+    "BtSocketOpen",
+    "BtSocketOpenResponse",
+    "BtSocketClose",
+    "BtSocketCloseResponse",
+    "GetMessageHubsAndEndpointsRequest",
+    "GetMessageHubsAndEndpointsResponse",
+    "RegisterMessageHub",
+    "UnregisterMessageHub",
+    "RegisterEndpoint",
+    "UnregisterEndpoint",
+    "OpenEndpointSessionRequest",
+    "EndpointSessionOpened",
+    "EndpointSessionClosed",
+    "EndpointSessionMessage",
+    "EndpointSessionMessageDeliveryStatus",
     nullptr
   };
   return names;
 }
 
 inline const char *EnumNameChreMessage(ChreMessage e) {
-  if (flatbuffers::IsOutRange(e, ChreMessage::NONE, ChreMessage::MessageDeliveryStatus)) return "";
+  if (flatbuffers::IsOutRange(e, ChreMessage::NONE, ChreMessage::EndpointSessionMessageDeliveryStatus)) return "";
   const size_t index = static_cast<size_t>(e);
   return EnumNamesChreMessage()[index];
 }
@@ -549,6 +921,66 @@
   static const ChreMessage enum_value = ChreMessage::MessageDeliveryStatus;
 };
 
+template<> struct ChreMessageTraits<chre::fbs::BtSocketOpen> {
+  static const ChreMessage enum_value = ChreMessage::BtSocketOpen;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::BtSocketOpenResponse> {
+  static const ChreMessage enum_value = ChreMessage::BtSocketOpenResponse;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::BtSocketClose> {
+  static const ChreMessage enum_value = ChreMessage::BtSocketClose;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::BtSocketCloseResponse> {
+  static const ChreMessage enum_value = ChreMessage::BtSocketCloseResponse;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::GetMessageHubsAndEndpointsRequest> {
+  static const ChreMessage enum_value = ChreMessage::GetMessageHubsAndEndpointsRequest;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::GetMessageHubsAndEndpointsResponse> {
+  static const ChreMessage enum_value = ChreMessage::GetMessageHubsAndEndpointsResponse;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::RegisterMessageHub> {
+  static const ChreMessage enum_value = ChreMessage::RegisterMessageHub;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::UnregisterMessageHub> {
+  static const ChreMessage enum_value = ChreMessage::UnregisterMessageHub;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::RegisterEndpoint> {
+  static const ChreMessage enum_value = ChreMessage::RegisterEndpoint;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::UnregisterEndpoint> {
+  static const ChreMessage enum_value = ChreMessage::UnregisterEndpoint;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::OpenEndpointSessionRequest> {
+  static const ChreMessage enum_value = ChreMessage::OpenEndpointSessionRequest;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::EndpointSessionOpened> {
+  static const ChreMessage enum_value = ChreMessage::EndpointSessionOpened;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::EndpointSessionClosed> {
+  static const ChreMessage enum_value = ChreMessage::EndpointSessionClosed;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::EndpointSessionMessage> {
+  static const ChreMessage enum_value = ChreMessage::EndpointSessionMessage;
+};
+
+template<> struct ChreMessageTraits<chre::fbs::EndpointSessionMessageDeliveryStatus> {
+  static const ChreMessage enum_value = ChreMessage::EndpointSessionMessageDeliveryStatus;
+};
+
 bool VerifyChreMessage(flatbuffers::Verifier &verifier, const void *obj, ChreMessage type);
 bool VerifyChreMessageVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
 
@@ -2787,6 +3219,1501 @@
   return builder_.Finish();
 }
 
+struct LeCocChannelInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef LeCocChannelInfoBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_LOCALCID = 4,
+    VT_REMOTECID = 6,
+    VT_PSM = 8,
+    VT_LOCALMTU = 10,
+    VT_REMOTEMTU = 12,
+    VT_LOCALMPS = 14,
+    VT_REMOTEMPS = 16,
+    VT_INITIALRXCREDITS = 18,
+    VT_INITIALTXCREDITS = 20
+  };
+  int32_t localCid() const {
+    return GetField<int32_t>(VT_LOCALCID, 0);
+  }
+  int32_t remoteCid() const {
+    return GetField<int32_t>(VT_REMOTECID, 0);
+  }
+  int32_t psm() const {
+    return GetField<int32_t>(VT_PSM, 0);
+  }
+  int32_t localMtu() const {
+    return GetField<int32_t>(VT_LOCALMTU, 0);
+  }
+  int32_t remoteMtu() const {
+    return GetField<int32_t>(VT_REMOTEMTU, 0);
+  }
+  int32_t localMps() const {
+    return GetField<int32_t>(VT_LOCALMPS, 0);
+  }
+  int32_t remoteMps() const {
+    return GetField<int32_t>(VT_REMOTEMPS, 0);
+  }
+  int32_t initialRxCredits() const {
+    return GetField<int32_t>(VT_INITIALRXCREDITS, 0);
+  }
+  int32_t initialTxCredits() const {
+    return GetField<int32_t>(VT_INITIALTXCREDITS, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int32_t>(verifier, VT_LOCALCID) &&
+           VerifyField<int32_t>(verifier, VT_REMOTECID) &&
+           VerifyField<int32_t>(verifier, VT_PSM) &&
+           VerifyField<int32_t>(verifier, VT_LOCALMTU) &&
+           VerifyField<int32_t>(verifier, VT_REMOTEMTU) &&
+           VerifyField<int32_t>(verifier, VT_LOCALMPS) &&
+           VerifyField<int32_t>(verifier, VT_REMOTEMPS) &&
+           VerifyField<int32_t>(verifier, VT_INITIALRXCREDITS) &&
+           VerifyField<int32_t>(verifier, VT_INITIALTXCREDITS) &&
+           verifier.EndTable();
+  }
+};
+
+struct LeCocChannelInfoBuilder {
+  typedef LeCocChannelInfo Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_localCid(int32_t localCid) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_LOCALCID, localCid, 0);
+  }
+  void add_remoteCid(int32_t remoteCid) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_REMOTECID, remoteCid, 0);
+  }
+  void add_psm(int32_t psm) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_PSM, psm, 0);
+  }
+  void add_localMtu(int32_t localMtu) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_LOCALMTU, localMtu, 0);
+  }
+  void add_remoteMtu(int32_t remoteMtu) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_REMOTEMTU, remoteMtu, 0);
+  }
+  void add_localMps(int32_t localMps) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_LOCALMPS, localMps, 0);
+  }
+  void add_remoteMps(int32_t remoteMps) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_REMOTEMPS, remoteMps, 0);
+  }
+  void add_initialRxCredits(int32_t initialRxCredits) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_INITIALRXCREDITS, initialRxCredits, 0);
+  }
+  void add_initialTxCredits(int32_t initialTxCredits) {
+    fbb_.AddElement<int32_t>(LeCocChannelInfo::VT_INITIALTXCREDITS, initialTxCredits, 0);
+  }
+  explicit LeCocChannelInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  LeCocChannelInfoBuilder &operator=(const LeCocChannelInfoBuilder &);
+  flatbuffers::Offset<LeCocChannelInfo> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<LeCocChannelInfo>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<LeCocChannelInfo> CreateLeCocChannelInfo(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int32_t localCid = 0,
+    int32_t remoteCid = 0,
+    int32_t psm = 0,
+    int32_t localMtu = 0,
+    int32_t remoteMtu = 0,
+    int32_t localMps = 0,
+    int32_t remoteMps = 0,
+    int32_t initialRxCredits = 0,
+    int32_t initialTxCredits = 0) {
+  LeCocChannelInfoBuilder builder_(_fbb);
+  builder_.add_initialTxCredits(initialTxCredits);
+  builder_.add_initialRxCredits(initialRxCredits);
+  builder_.add_remoteMps(remoteMps);
+  builder_.add_localMps(localMps);
+  builder_.add_remoteMtu(remoteMtu);
+  builder_.add_localMtu(localMtu);
+  builder_.add_psm(psm);
+  builder_.add_remoteCid(remoteCid);
+  builder_.add_localCid(localCid);
+  return builder_.Finish();
+}
+
+struct BtSocketOpen FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BtSocketOpenBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SOCKETID = 4,
+    VT_NAME = 6,
+    VT_ACLCONNECTIONHANDLE = 8,
+    VT_CHANNELINFO_TYPE = 10,
+    VT_CHANNELINFO = 12,
+    VT_HUBID = 14,
+    VT_ENDPOINTID = 16
+  };
+  int64_t socketId() const {
+    return GetField<int64_t>(VT_SOCKETID, 0);
+  }
+  const flatbuffers::Vector<int8_t> *name() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  int32_t aclConnectionHandle() const {
+    return GetField<int32_t>(VT_ACLCONNECTIONHANDLE, 0);
+  }
+  chre::fbs::ChannelInfo channelInfo_type() const {
+    return static_cast<chre::fbs::ChannelInfo>(GetField<uint8_t>(VT_CHANNELINFO_TYPE, 0));
+  }
+  const void *channelInfo() const {
+    return GetPointer<const void *>(VT_CHANNELINFO);
+  }
+  template<typename T> const T *channelInfo_as() const;
+  const chre::fbs::LeCocChannelInfo *channelInfo_as_LeCocChannelInfo() const {
+    return channelInfo_type() == chre::fbs::ChannelInfo::LeCocChannelInfo ? static_cast<const chre::fbs::LeCocChannelInfo *>(channelInfo()) : nullptr;
+  }
+  int64_t hubId() const {
+    return GetField<int64_t>(VT_HUBID, 0);
+  }
+  int64_t endpointId() const {
+    return GetField<int64_t>(VT_ENDPOINTID, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_SOCKETID) &&
+           VerifyOffset(verifier, VT_NAME) &&
+           verifier.VerifyVector(name()) &&
+           VerifyField<int32_t>(verifier, VT_ACLCONNECTIONHANDLE) &&
+           VerifyField<uint8_t>(verifier, VT_CHANNELINFO_TYPE) &&
+           VerifyOffset(verifier, VT_CHANNELINFO) &&
+           VerifyChannelInfo(verifier, channelInfo(), channelInfo_type()) &&
+           VerifyField<int64_t>(verifier, VT_HUBID) &&
+           VerifyField<int64_t>(verifier, VT_ENDPOINTID) &&
+           verifier.EndTable();
+  }
+};
+
+template<> inline const chre::fbs::LeCocChannelInfo *BtSocketOpen::channelInfo_as<chre::fbs::LeCocChannelInfo>() const {
+  return channelInfo_as_LeCocChannelInfo();
+}
+
+struct BtSocketOpenBuilder {
+  typedef BtSocketOpen Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_socketId(int64_t socketId) {
+    fbb_.AddElement<int64_t>(BtSocketOpen::VT_SOCKETID, socketId, 0);
+  }
+  void add_name(flatbuffers::Offset<flatbuffers::Vector<int8_t>> name) {
+    fbb_.AddOffset(BtSocketOpen::VT_NAME, name);
+  }
+  void add_aclConnectionHandle(int32_t aclConnectionHandle) {
+    fbb_.AddElement<int32_t>(BtSocketOpen::VT_ACLCONNECTIONHANDLE, aclConnectionHandle, 0);
+  }
+  void add_channelInfo_type(chre::fbs::ChannelInfo channelInfo_type) {
+    fbb_.AddElement<uint8_t>(BtSocketOpen::VT_CHANNELINFO_TYPE, static_cast<uint8_t>(channelInfo_type), 0);
+  }
+  void add_channelInfo(flatbuffers::Offset<void> channelInfo) {
+    fbb_.AddOffset(BtSocketOpen::VT_CHANNELINFO, channelInfo);
+  }
+  void add_hubId(int64_t hubId) {
+    fbb_.AddElement<int64_t>(BtSocketOpen::VT_HUBID, hubId, 0);
+  }
+  void add_endpointId(int64_t endpointId) {
+    fbb_.AddElement<int64_t>(BtSocketOpen::VT_ENDPOINTID, endpointId, 0);
+  }
+  explicit BtSocketOpenBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  BtSocketOpenBuilder &operator=(const BtSocketOpenBuilder &);
+  flatbuffers::Offset<BtSocketOpen> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<BtSocketOpen>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<BtSocketOpen> CreateBtSocketOpen(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> name = 0,
+    int32_t aclConnectionHandle = 0,
+    chre::fbs::ChannelInfo channelInfo_type = chre::fbs::ChannelInfo::NONE,
+    flatbuffers::Offset<void> channelInfo = 0,
+    int64_t hubId = 0,
+    int64_t endpointId = 0) {
+  BtSocketOpenBuilder builder_(_fbb);
+  builder_.add_endpointId(endpointId);
+  builder_.add_hubId(hubId);
+  builder_.add_socketId(socketId);
+  builder_.add_channelInfo(channelInfo);
+  builder_.add_aclConnectionHandle(aclConnectionHandle);
+  builder_.add_name(name);
+  builder_.add_channelInfo_type(channelInfo_type);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<BtSocketOpen> CreateBtSocketOpenDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    const std::vector<int8_t> *name = nullptr,
+    int32_t aclConnectionHandle = 0,
+    chre::fbs::ChannelInfo channelInfo_type = chre::fbs::ChannelInfo::NONE,
+    flatbuffers::Offset<void> channelInfo = 0,
+    int64_t hubId = 0,
+    int64_t endpointId = 0) {
+  auto name__ = name ? _fbb.CreateVector<int8_t>(*name) : 0;
+  return chre::fbs::CreateBtSocketOpen(
+      _fbb,
+      socketId,
+      name__,
+      aclConnectionHandle,
+      channelInfo_type,
+      channelInfo,
+      hubId,
+      endpointId);
+}
+
+struct BtSocketOpenResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BtSocketOpenResponseBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SOCKETID = 4,
+    VT_STATUS = 6,
+    VT_REASON = 8
+  };
+  int64_t socketId() const {
+    return GetField<int64_t>(VT_SOCKETID, 0);
+  }
+  chre::fbs::BtSocketOpenStatus status() const {
+    return static_cast<chre::fbs::BtSocketOpenStatus>(GetField<int8_t>(VT_STATUS, 0));
+  }
+  const flatbuffers::Vector<int8_t> *reason() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_REASON);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_SOCKETID) &&
+           VerifyField<int8_t>(verifier, VT_STATUS) &&
+           VerifyOffset(verifier, VT_REASON) &&
+           verifier.VerifyVector(reason()) &&
+           verifier.EndTable();
+  }
+};
+
+struct BtSocketOpenResponseBuilder {
+  typedef BtSocketOpenResponse Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_socketId(int64_t socketId) {
+    fbb_.AddElement<int64_t>(BtSocketOpenResponse::VT_SOCKETID, socketId, 0);
+  }
+  void add_status(chre::fbs::BtSocketOpenStatus status) {
+    fbb_.AddElement<int8_t>(BtSocketOpenResponse::VT_STATUS, static_cast<int8_t>(status), 0);
+  }
+  void add_reason(flatbuffers::Offset<flatbuffers::Vector<int8_t>> reason) {
+    fbb_.AddOffset(BtSocketOpenResponse::VT_REASON, reason);
+  }
+  explicit BtSocketOpenResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  BtSocketOpenResponseBuilder &operator=(const BtSocketOpenResponseBuilder &);
+  flatbuffers::Offset<BtSocketOpenResponse> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<BtSocketOpenResponse>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<BtSocketOpenResponse> CreateBtSocketOpenResponse(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    chre::fbs::BtSocketOpenStatus status = chre::fbs::BtSocketOpenStatus::SUCCESS,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> reason = 0) {
+  BtSocketOpenResponseBuilder builder_(_fbb);
+  builder_.add_socketId(socketId);
+  builder_.add_reason(reason);
+  builder_.add_status(status);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<BtSocketOpenResponse> CreateBtSocketOpenResponseDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    chre::fbs::BtSocketOpenStatus status = chre::fbs::BtSocketOpenStatus::SUCCESS,
+    const std::vector<int8_t> *reason = nullptr) {
+  auto reason__ = reason ? _fbb.CreateVector<int8_t>(*reason) : 0;
+  return chre::fbs::CreateBtSocketOpenResponse(
+      _fbb,
+      socketId,
+      status,
+      reason__);
+}
+
+struct BtSocketClose FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BtSocketCloseBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SOCKETID = 4,
+    VT_REASON = 6
+  };
+  int64_t socketId() const {
+    return GetField<int64_t>(VT_SOCKETID, 0);
+  }
+  const flatbuffers::Vector<int8_t> *reason() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_REASON);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_SOCKETID) &&
+           VerifyOffset(verifier, VT_REASON) &&
+           verifier.VerifyVector(reason()) &&
+           verifier.EndTable();
+  }
+};
+
+struct BtSocketCloseBuilder {
+  typedef BtSocketClose Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_socketId(int64_t socketId) {
+    fbb_.AddElement<int64_t>(BtSocketClose::VT_SOCKETID, socketId, 0);
+  }
+  void add_reason(flatbuffers::Offset<flatbuffers::Vector<int8_t>> reason) {
+    fbb_.AddOffset(BtSocketClose::VT_REASON, reason);
+  }
+  explicit BtSocketCloseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  BtSocketCloseBuilder &operator=(const BtSocketCloseBuilder &);
+  flatbuffers::Offset<BtSocketClose> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<BtSocketClose>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<BtSocketClose> CreateBtSocketClose(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> reason = 0) {
+  BtSocketCloseBuilder builder_(_fbb);
+  builder_.add_socketId(socketId);
+  builder_.add_reason(reason);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<BtSocketClose> CreateBtSocketCloseDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0,
+    const std::vector<int8_t> *reason = nullptr) {
+  auto reason__ = reason ? _fbb.CreateVector<int8_t>(*reason) : 0;
+  return chre::fbs::CreateBtSocketClose(
+      _fbb,
+      socketId,
+      reason__);
+}
+
+struct BtSocketCloseResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BtSocketCloseResponseBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SOCKETID = 4
+  };
+  int64_t socketId() const {
+    return GetField<int64_t>(VT_SOCKETID, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_SOCKETID) &&
+           verifier.EndTable();
+  }
+};
+
+struct BtSocketCloseResponseBuilder {
+  typedef BtSocketCloseResponse Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_socketId(int64_t socketId) {
+    fbb_.AddElement<int64_t>(BtSocketCloseResponse::VT_SOCKETID, socketId, 0);
+  }
+  explicit BtSocketCloseResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  BtSocketCloseResponseBuilder &operator=(const BtSocketCloseResponseBuilder &);
+  flatbuffers::Offset<BtSocketCloseResponse> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<BtSocketCloseResponse>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<BtSocketCloseResponse> CreateBtSocketCloseResponse(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t socketId = 0) {
+  BtSocketCloseResponseBuilder builder_(_fbb);
+  builder_.add_socketId(socketId);
+  return builder_.Finish();
+}
+
+struct VendorHubInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef VendorHubInfoBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_NAME = 4,
+    VT_VERSION = 6,
+    VT_EXTENDED_INFO = 8
+  };
+  /// The name of the hub. Nominally a UTF-8 string, but note that we're not
+  /// using the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  const flatbuffers::Vector<int8_t> *name() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  /// Hub version
+  uint32_t version() const {
+    return GetField<uint32_t>(VT_VERSION, 0);
+  }
+  /// Additional vendor-defined data
+  const flatbuffers::Vector<uint8_t> *extended_info() const {
+    return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_EXTENDED_INFO);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_NAME) &&
+           verifier.VerifyVector(name()) &&
+           VerifyField<uint32_t>(verifier, VT_VERSION) &&
+           VerifyOffset(verifier, VT_EXTENDED_INFO) &&
+           verifier.VerifyVector(extended_info()) &&
+           verifier.EndTable();
+  }
+};
+
+struct VendorHubInfoBuilder {
+  typedef VendorHubInfo Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_name(flatbuffers::Offset<flatbuffers::Vector<int8_t>> name) {
+    fbb_.AddOffset(VendorHubInfo::VT_NAME, name);
+  }
+  void add_version(uint32_t version) {
+    fbb_.AddElement<uint32_t>(VendorHubInfo::VT_VERSION, version, 0);
+  }
+  void add_extended_info(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> extended_info) {
+    fbb_.AddOffset(VendorHubInfo::VT_EXTENDED_INFO, extended_info);
+  }
+  explicit VendorHubInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  VendorHubInfoBuilder &operator=(const VendorHubInfoBuilder &);
+  flatbuffers::Offset<VendorHubInfo> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<VendorHubInfo>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<VendorHubInfo> CreateVendorHubInfo(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> name = 0,
+    uint32_t version = 0,
+    flatbuffers::Offset<flatbuffers::Vector<uint8_t>> extended_info = 0) {
+  VendorHubInfoBuilder builder_(_fbb);
+  builder_.add_extended_info(extended_info);
+  builder_.add_version(version);
+  builder_.add_name(name);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<VendorHubInfo> CreateVendorHubInfoDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    const std::vector<int8_t> *name = nullptr,
+    uint32_t version = 0,
+    const std::vector<uint8_t> *extended_info = nullptr) {
+  auto name__ = name ? _fbb.CreateVector<int8_t>(*name) : 0;
+  auto extended_info__ = extended_info ? _fbb.CreateVector<uint8_t>(*extended_info) : 0;
+  return chre::fbs::CreateVendorHubInfo(
+      _fbb,
+      name__,
+      version,
+      extended_info__);
+}
+
+struct MessageHub FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef MessageHubBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4,
+    VT_DETAILS_TYPE = 6,
+    VT_DETAILS = 8
+  };
+  /// The hub id. -1 is reserved and 0 is invalid. 0x416e64726f696400 represents
+  /// the ContextHub service.
+  int64_t id() const {
+    return GetField<int64_t>(VT_ID, 0);
+  }
+  chre::fbs::MessageHubDetails details_type() const {
+    return static_cast<chre::fbs::MessageHubDetails>(GetField<uint8_t>(VT_DETAILS_TYPE, 0));
+  }
+  /// Details of the message hub.
+  const void *details() const {
+    return GetPointer<const void *>(VT_DETAILS);
+  }
+  template<typename T> const T *details_as() const;
+  const chre::fbs::HubInfoResponse *details_as_HubInfoResponse() const {
+    return details_type() == chre::fbs::MessageHubDetails::HubInfoResponse ? static_cast<const chre::fbs::HubInfoResponse *>(details()) : nullptr;
+  }
+  const chre::fbs::VendorHubInfo *details_as_VendorHubInfo() const {
+    return details_type() == chre::fbs::MessageHubDetails::VendorHubInfo ? static_cast<const chre::fbs::VendorHubInfo *>(details()) : nullptr;
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_ID) &&
+           VerifyField<uint8_t>(verifier, VT_DETAILS_TYPE) &&
+           VerifyOffset(verifier, VT_DETAILS) &&
+           VerifyMessageHubDetails(verifier, details(), details_type()) &&
+           verifier.EndTable();
+  }
+};
+
+template<> inline const chre::fbs::HubInfoResponse *MessageHub::details_as<chre::fbs::HubInfoResponse>() const {
+  return details_as_HubInfoResponse();
+}
+
+template<> inline const chre::fbs::VendorHubInfo *MessageHub::details_as<chre::fbs::VendorHubInfo>() const {
+  return details_as_VendorHubInfo();
+}
+
+struct MessageHubBuilder {
+  typedef MessageHub Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(int64_t id) {
+    fbb_.AddElement<int64_t>(MessageHub::VT_ID, id, 0);
+  }
+  void add_details_type(chre::fbs::MessageHubDetails details_type) {
+    fbb_.AddElement<uint8_t>(MessageHub::VT_DETAILS_TYPE, static_cast<uint8_t>(details_type), 0);
+  }
+  void add_details(flatbuffers::Offset<void> details) {
+    fbb_.AddOffset(MessageHub::VT_DETAILS, details);
+  }
+  explicit MessageHubBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  MessageHubBuilder &operator=(const MessageHubBuilder &);
+  flatbuffers::Offset<MessageHub> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<MessageHub>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<MessageHub> CreateMessageHub(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t id = 0,
+    chre::fbs::MessageHubDetails details_type = chre::fbs::MessageHubDetails::NONE,
+    flatbuffers::Offset<void> details = 0) {
+  MessageHubBuilder builder_(_fbb);
+  builder_.add_id(id);
+  builder_.add_details(details);
+  builder_.add_details_type(details_type);
+  return builder_.Finish();
+}
+
+struct RegisterMessageHub FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef RegisterMessageHubBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_HUB = 4
+  };
+  const chre::fbs::MessageHub *hub() const {
+    return GetPointer<const chre::fbs::MessageHub *>(VT_HUB);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_HUB) &&
+           verifier.VerifyTable(hub()) &&
+           verifier.EndTable();
+  }
+};
+
+struct RegisterMessageHubBuilder {
+  typedef RegisterMessageHub Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_hub(flatbuffers::Offset<chre::fbs::MessageHub> hub) {
+    fbb_.AddOffset(RegisterMessageHub::VT_HUB, hub);
+  }
+  explicit RegisterMessageHubBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  RegisterMessageHubBuilder &operator=(const RegisterMessageHubBuilder &);
+  flatbuffers::Offset<RegisterMessageHub> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<RegisterMessageHub>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<RegisterMessageHub> CreateRegisterMessageHub(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::MessageHub> hub = 0) {
+  RegisterMessageHubBuilder builder_(_fbb);
+  builder_.add_hub(hub);
+  return builder_.Finish();
+}
+
+struct UnregisterMessageHub FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef UnregisterMessageHubBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4
+  };
+  int64_t id() const {
+    return GetField<int64_t>(VT_ID, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_ID) &&
+           verifier.EndTable();
+  }
+};
+
+struct UnregisterMessageHubBuilder {
+  typedef UnregisterMessageHub Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(int64_t id) {
+    fbb_.AddElement<int64_t>(UnregisterMessageHub::VT_ID, id, 0);
+  }
+  explicit UnregisterMessageHubBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  UnregisterMessageHubBuilder &operator=(const UnregisterMessageHubBuilder &);
+  flatbuffers::Offset<UnregisterMessageHub> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<UnregisterMessageHub>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<UnregisterMessageHub> CreateUnregisterMessageHub(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t id = 0) {
+  UnregisterMessageHubBuilder builder_(_fbb);
+  builder_.add_id(id);
+  return builder_.Finish();
+}
+
+struct EndpointId FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointIdBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_HUBID = 4,
+    VT_ID = 6
+  };
+  /// Id of the hub hosting the endpoint
+  int64_t hubId() const {
+    return GetField<int64_t>(VT_HUBID, 0);
+  }
+  /// The id of the endpoint scoped to the hub
+  int64_t id() const {
+    return GetField<int64_t>(VT_ID, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int64_t>(verifier, VT_HUBID) &&
+           VerifyField<int64_t>(verifier, VT_ID) &&
+           verifier.EndTable();
+  }
+};
+
+struct EndpointIdBuilder {
+  typedef EndpointId Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_hubId(int64_t hubId) {
+    fbb_.AddElement<int64_t>(EndpointId::VT_HUBID, hubId, 0);
+  }
+  void add_id(int64_t id) {
+    fbb_.AddElement<int64_t>(EndpointId::VT_ID, id, 0);
+  }
+  explicit EndpointIdBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointIdBuilder &operator=(const EndpointIdBuilder &);
+  flatbuffers::Offset<EndpointId> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointId>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointId> CreateEndpointId(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int64_t hubId = 0,
+    int64_t id = 0) {
+  EndpointIdBuilder builder_(_fbb);
+  builder_.add_id(id);
+  builder_.add_hubId(hubId);
+  return builder_.Finish();
+}
+
+struct Service FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef ServiceBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_FORMAT = 4,
+    VT_DESCRIPTOR = 6,
+    VT_MAJOR_VERSION = 8,
+    VT_MINOR_VERSION = 10
+  };
+  chre::fbs::RpcFormat format() const {
+    return static_cast<chre::fbs::RpcFormat>(GetField<uint8_t>(VT_FORMAT, 0));
+  }
+  /// Service descriptor. Nominally a UTF-8 string, but note that we're not
+  /// using the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  const flatbuffers::Vector<int8_t> *descriptor() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_DESCRIPTOR);
+  }
+  /// Breaking changes should bump the major version.
+  uint32_t major_version() const {
+    return GetField<uint32_t>(VT_MAJOR_VERSION, 0);
+  }
+  /// Monotonically increasing minor version.
+  uint32_t minor_version() const {
+    return GetField<uint32_t>(VT_MINOR_VERSION, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint8_t>(verifier, VT_FORMAT) &&
+           VerifyOffset(verifier, VT_DESCRIPTOR) &&
+           verifier.VerifyVector(descriptor()) &&
+           VerifyField<uint32_t>(verifier, VT_MAJOR_VERSION) &&
+           VerifyField<uint32_t>(verifier, VT_MINOR_VERSION) &&
+           verifier.EndTable();
+  }
+};
+
+struct ServiceBuilder {
+  typedef Service Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_format(chre::fbs::RpcFormat format) {
+    fbb_.AddElement<uint8_t>(Service::VT_FORMAT, static_cast<uint8_t>(format), 0);
+  }
+  void add_descriptor(flatbuffers::Offset<flatbuffers::Vector<int8_t>> descriptor) {
+    fbb_.AddOffset(Service::VT_DESCRIPTOR, descriptor);
+  }
+  void add_major_version(uint32_t major_version) {
+    fbb_.AddElement<uint32_t>(Service::VT_MAJOR_VERSION, major_version, 0);
+  }
+  void add_minor_version(uint32_t minor_version) {
+    fbb_.AddElement<uint32_t>(Service::VT_MINOR_VERSION, minor_version, 0);
+  }
+  explicit ServiceBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  ServiceBuilder &operator=(const ServiceBuilder &);
+  flatbuffers::Offset<Service> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<Service>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<Service> CreateService(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    chre::fbs::RpcFormat format = chre::fbs::RpcFormat::CUSTOM,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> descriptor = 0,
+    uint32_t major_version = 0,
+    uint32_t minor_version = 0) {
+  ServiceBuilder builder_(_fbb);
+  builder_.add_minor_version(minor_version);
+  builder_.add_major_version(major_version);
+  builder_.add_descriptor(descriptor);
+  builder_.add_format(format);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<Service> CreateServiceDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    chre::fbs::RpcFormat format = chre::fbs::RpcFormat::CUSTOM,
+    const std::vector<int8_t> *descriptor = nullptr,
+    uint32_t major_version = 0,
+    uint32_t minor_version = 0) {
+  auto descriptor__ = descriptor ? _fbb.CreateVector<int8_t>(*descriptor) : 0;
+  return chre::fbs::CreateService(
+      _fbb,
+      format,
+      descriptor__,
+      major_version,
+      minor_version);
+}
+
+struct EndpointInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointInfoBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4,
+    VT_TYPE = 6,
+    VT_NAME = 8,
+    VT_VERSION = 10,
+    VT_REQUIRED_PERMISSIONS = 12,
+    VT_SERVICES = 14
+  };
+  const chre::fbs::EndpointId *id() const {
+    return GetPointer<const chre::fbs::EndpointId *>(VT_ID);
+  }
+  chre::fbs::EndpointType type() const {
+    return static_cast<chre::fbs::EndpointType>(GetField<uint8_t>(VT_TYPE, 0));
+  }
+  /// Endpoing name. Nominally a UTF-8 string, but note that we're not using
+  /// the built-in "string" data type from FlatBuffers here, because the
+  /// generated C++ uses std::string which is not well-supported in CHRE.
+  const flatbuffers::Vector<int8_t> *name() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_NAME);
+  }
+  uint32_t version() const {
+    return GetField<uint32_t>(VT_VERSION, 0);
+  }
+  /// Values from CHRE_MESSAGE_PERMISSION_*
+  uint32_t required_permissions() const {
+    return GetField<uint32_t>(VT_REQUIRED_PERMISSIONS, 0);
+  }
+  const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>> *services() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>> *>(VT_SERVICES);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_ID) &&
+           verifier.VerifyTable(id()) &&
+           VerifyField<uint8_t>(verifier, VT_TYPE) &&
+           VerifyOffset(verifier, VT_NAME) &&
+           verifier.VerifyVector(name()) &&
+           VerifyField<uint32_t>(verifier, VT_VERSION) &&
+           VerifyField<uint32_t>(verifier, VT_REQUIRED_PERMISSIONS) &&
+           VerifyOffset(verifier, VT_SERVICES) &&
+           verifier.VerifyVector(services()) &&
+           verifier.VerifyVectorOfTables(services()) &&
+           verifier.EndTable();
+  }
+};
+
+struct EndpointInfoBuilder {
+  typedef EndpointInfo Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(flatbuffers::Offset<chre::fbs::EndpointId> id) {
+    fbb_.AddOffset(EndpointInfo::VT_ID, id);
+  }
+  void add_type(chre::fbs::EndpointType type) {
+    fbb_.AddElement<uint8_t>(EndpointInfo::VT_TYPE, static_cast<uint8_t>(type), 0);
+  }
+  void add_name(flatbuffers::Offset<flatbuffers::Vector<int8_t>> name) {
+    fbb_.AddOffset(EndpointInfo::VT_NAME, name);
+  }
+  void add_version(uint32_t version) {
+    fbb_.AddElement<uint32_t>(EndpointInfo::VT_VERSION, version, 0);
+  }
+  void add_required_permissions(uint32_t required_permissions) {
+    fbb_.AddElement<uint32_t>(EndpointInfo::VT_REQUIRED_PERMISSIONS, required_permissions, 0);
+  }
+  void add_services(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>>> services) {
+    fbb_.AddOffset(EndpointInfo::VT_SERVICES, services);
+  }
+  explicit EndpointInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointInfoBuilder &operator=(const EndpointInfoBuilder &);
+  flatbuffers::Offset<EndpointInfo> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointInfo>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointInfo> CreateEndpointInfo(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::EndpointId> id = 0,
+    chre::fbs::EndpointType type = chre::fbs::EndpointType::INVALID,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> name = 0,
+    uint32_t version = 0,
+    uint32_t required_permissions = 0,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::Service>>> services = 0) {
+  EndpointInfoBuilder builder_(_fbb);
+  builder_.add_services(services);
+  builder_.add_required_permissions(required_permissions);
+  builder_.add_version(version);
+  builder_.add_name(name);
+  builder_.add_id(id);
+  builder_.add_type(type);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<EndpointInfo> CreateEndpointInfoDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::EndpointId> id = 0,
+    chre::fbs::EndpointType type = chre::fbs::EndpointType::INVALID,
+    const std::vector<int8_t> *name = nullptr,
+    uint32_t version = 0,
+    uint32_t required_permissions = 0,
+    const std::vector<flatbuffers::Offset<chre::fbs::Service>> *services = nullptr) {
+  auto name__ = name ? _fbb.CreateVector<int8_t>(*name) : 0;
+  auto services__ = services ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::Service>>(*services) : 0;
+  return chre::fbs::CreateEndpointInfo(
+      _fbb,
+      id,
+      type,
+      name__,
+      version,
+      required_permissions,
+      services__);
+}
+
+struct RegisterEndpoint FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef RegisterEndpointBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ENDPOINT = 4
+  };
+  const chre::fbs::EndpointInfo *endpoint() const {
+    return GetPointer<const chre::fbs::EndpointInfo *>(VT_ENDPOINT);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_ENDPOINT) &&
+           verifier.VerifyTable(endpoint()) &&
+           verifier.EndTable();
+  }
+};
+
+struct RegisterEndpointBuilder {
+  typedef RegisterEndpoint Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_endpoint(flatbuffers::Offset<chre::fbs::EndpointInfo> endpoint) {
+    fbb_.AddOffset(RegisterEndpoint::VT_ENDPOINT, endpoint);
+  }
+  explicit RegisterEndpointBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  RegisterEndpointBuilder &operator=(const RegisterEndpointBuilder &);
+  flatbuffers::Offset<RegisterEndpoint> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<RegisterEndpoint>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<RegisterEndpoint> CreateRegisterEndpoint(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::EndpointInfo> endpoint = 0) {
+  RegisterEndpointBuilder builder_(_fbb);
+  builder_.add_endpoint(endpoint);
+  return builder_.Finish();
+}
+
+struct UnregisterEndpoint FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef UnregisterEndpointBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ENDPOINT = 4
+  };
+  const chre::fbs::EndpointId *endpoint() const {
+    return GetPointer<const chre::fbs::EndpointId *>(VT_ENDPOINT);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_ENDPOINT) &&
+           verifier.VerifyTable(endpoint()) &&
+           verifier.EndTable();
+  }
+};
+
+struct UnregisterEndpointBuilder {
+  typedef UnregisterEndpoint Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_endpoint(flatbuffers::Offset<chre::fbs::EndpointId> endpoint) {
+    fbb_.AddOffset(UnregisterEndpoint::VT_ENDPOINT, endpoint);
+  }
+  explicit UnregisterEndpointBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  UnregisterEndpointBuilder &operator=(const UnregisterEndpointBuilder &);
+  flatbuffers::Offset<UnregisterEndpoint> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<UnregisterEndpoint>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<UnregisterEndpoint> CreateUnregisterEndpoint(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<chre::fbs::EndpointId> endpoint = 0) {
+  UnregisterEndpointBuilder builder_(_fbb);
+  builder_.add_endpoint(endpoint);
+  return builder_.Finish();
+}
+
+/// HAL->CHRE, indicates the HAL is coming up
+struct GetMessageHubsAndEndpointsRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef GetMessageHubsAndEndpointsRequestBuilder Builder;
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           verifier.EndTable();
+  }
+};
+
+struct GetMessageHubsAndEndpointsRequestBuilder {
+  typedef GetMessageHubsAndEndpointsRequest Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  explicit GetMessageHubsAndEndpointsRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  GetMessageHubsAndEndpointsRequestBuilder &operator=(const GetMessageHubsAndEndpointsRequestBuilder &);
+  flatbuffers::Offset<GetMessageHubsAndEndpointsRequest> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<GetMessageHubsAndEndpointsRequest>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsRequest> CreateGetMessageHubsAndEndpointsRequest(
+    flatbuffers::FlatBufferBuilder &_fbb) {
+  GetMessageHubsAndEndpointsRequestBuilder builder_(_fbb);
+  return builder_.Finish();
+}
+
+struct GetMessageHubsAndEndpointsResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef GetMessageHubsAndEndpointsResponseBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_HUBS = 4,
+    VT_ENDPOINTS = 6
+  };
+  const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>> *hubs() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>> *>(VT_HUBS);
+  }
+  const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>> *endpoints() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>> *>(VT_ENDPOINTS);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_HUBS) &&
+           verifier.VerifyVector(hubs()) &&
+           verifier.VerifyVectorOfTables(hubs()) &&
+           VerifyOffset(verifier, VT_ENDPOINTS) &&
+           verifier.VerifyVector(endpoints()) &&
+           verifier.VerifyVectorOfTables(endpoints()) &&
+           verifier.EndTable();
+  }
+};
+
+struct GetMessageHubsAndEndpointsResponseBuilder {
+  typedef GetMessageHubsAndEndpointsResponse Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_hubs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>>> hubs) {
+    fbb_.AddOffset(GetMessageHubsAndEndpointsResponse::VT_HUBS, hubs);
+  }
+  void add_endpoints(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>>> endpoints) {
+    fbb_.AddOffset(GetMessageHubsAndEndpointsResponse::VT_ENDPOINTS, endpoints);
+  }
+  explicit GetMessageHubsAndEndpointsResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  GetMessageHubsAndEndpointsResponseBuilder &operator=(const GetMessageHubsAndEndpointsResponseBuilder &);
+  flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<GetMessageHubsAndEndpointsResponse>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> CreateGetMessageHubsAndEndpointsResponse(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::MessageHub>>> hubs = 0,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<chre::fbs::EndpointInfo>>> endpoints = 0) {
+  GetMessageHubsAndEndpointsResponseBuilder builder_(_fbb);
+  builder_.add_endpoints(endpoints);
+  builder_.add_hubs(hubs);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<GetMessageHubsAndEndpointsResponse> CreateGetMessageHubsAndEndpointsResponseDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    const std::vector<flatbuffers::Offset<chre::fbs::MessageHub>> *hubs = nullptr,
+    const std::vector<flatbuffers::Offset<chre::fbs::EndpointInfo>> *endpoints = nullptr) {
+  auto hubs__ = hubs ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::MessageHub>>(*hubs) : 0;
+  auto endpoints__ = endpoints ? _fbb.CreateVector<flatbuffers::Offset<chre::fbs::EndpointInfo>>(*endpoints) : 0;
+  return chre::fbs::CreateGetMessageHubsAndEndpointsResponse(
+      _fbb,
+      hubs__,
+      endpoints__);
+}
+
+struct OpenEndpointSessionRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef OpenEndpointSessionRequestBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4,
+    VT_FROMENDPOINT = 6,
+    VT_TOENDPOINT = 8,
+    VT_SERVICEDESCRIPTOR = 10
+  };
+  uint16_t id() const {
+    return GetField<uint16_t>(VT_ID, 0);
+  }
+  const chre::fbs::EndpointId *fromEndpoint() const {
+    return GetPointer<const chre::fbs::EndpointId *>(VT_FROMENDPOINT);
+  }
+  const chre::fbs::EndpointId *toEndpoint() const {
+    return GetPointer<const chre::fbs::EndpointId *>(VT_TOENDPOINT);
+  }
+  /// If present, describes the service definition used over the session
+  const flatbuffers::Vector<int8_t> *serviceDescriptor() const {
+    return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_SERVICEDESCRIPTOR);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_ID) &&
+           VerifyOffset(verifier, VT_FROMENDPOINT) &&
+           verifier.VerifyTable(fromEndpoint()) &&
+           VerifyOffset(verifier, VT_TOENDPOINT) &&
+           verifier.VerifyTable(toEndpoint()) &&
+           VerifyOffset(verifier, VT_SERVICEDESCRIPTOR) &&
+           verifier.VerifyVector(serviceDescriptor()) &&
+           verifier.EndTable();
+  }
+};
+
+struct OpenEndpointSessionRequestBuilder {
+  typedef OpenEndpointSessionRequest Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(uint16_t id) {
+    fbb_.AddElement<uint16_t>(OpenEndpointSessionRequest::VT_ID, id, 0);
+  }
+  void add_fromEndpoint(flatbuffers::Offset<chre::fbs::EndpointId> fromEndpoint) {
+    fbb_.AddOffset(OpenEndpointSessionRequest::VT_FROMENDPOINT, fromEndpoint);
+  }
+  void add_toEndpoint(flatbuffers::Offset<chre::fbs::EndpointId> toEndpoint) {
+    fbb_.AddOffset(OpenEndpointSessionRequest::VT_TOENDPOINT, toEndpoint);
+  }
+  void add_serviceDescriptor(flatbuffers::Offset<flatbuffers::Vector<int8_t>> serviceDescriptor) {
+    fbb_.AddOffset(OpenEndpointSessionRequest::VT_SERVICEDESCRIPTOR, serviceDescriptor);
+  }
+  explicit OpenEndpointSessionRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  OpenEndpointSessionRequestBuilder &operator=(const OpenEndpointSessionRequestBuilder &);
+  flatbuffers::Offset<OpenEndpointSessionRequest> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<OpenEndpointSessionRequest>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<OpenEndpointSessionRequest> CreateOpenEndpointSessionRequest(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t id = 0,
+    flatbuffers::Offset<chre::fbs::EndpointId> fromEndpoint = 0,
+    flatbuffers::Offset<chre::fbs::EndpointId> toEndpoint = 0,
+    flatbuffers::Offset<flatbuffers::Vector<int8_t>> serviceDescriptor = 0) {
+  OpenEndpointSessionRequestBuilder builder_(_fbb);
+  builder_.add_serviceDescriptor(serviceDescriptor);
+  builder_.add_toEndpoint(toEndpoint);
+  builder_.add_fromEndpoint(fromEndpoint);
+  builder_.add_id(id);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<OpenEndpointSessionRequest> CreateOpenEndpointSessionRequestDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t id = 0,
+    flatbuffers::Offset<chre::fbs::EndpointId> fromEndpoint = 0,
+    flatbuffers::Offset<chre::fbs::EndpointId> toEndpoint = 0,
+    const std::vector<int8_t> *serviceDescriptor = nullptr) {
+  auto serviceDescriptor__ = serviceDescriptor ? _fbb.CreateVector<int8_t>(*serviceDescriptor) : 0;
+  return chre::fbs::CreateOpenEndpointSessionRequest(
+      _fbb,
+      id,
+      fromEndpoint,
+      toEndpoint,
+      serviceDescriptor__);
+}
+
+struct EndpointSessionOpened FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointSessionOpenedBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4
+  };
+  uint16_t id() const {
+    return GetField<uint16_t>(VT_ID, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_ID) &&
+           verifier.EndTable();
+  }
+};
+
+struct EndpointSessionOpenedBuilder {
+  typedef EndpointSessionOpened Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(uint16_t id) {
+    fbb_.AddElement<uint16_t>(EndpointSessionOpened::VT_ID, id, 0);
+  }
+  explicit EndpointSessionOpenedBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointSessionOpenedBuilder &operator=(const EndpointSessionOpenedBuilder &);
+  flatbuffers::Offset<EndpointSessionOpened> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointSessionOpened>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointSessionOpened> CreateEndpointSessionOpened(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t id = 0) {
+  EndpointSessionOpenedBuilder builder_(_fbb);
+  builder_.add_id(id);
+  return builder_.Finish();
+}
+
+struct EndpointSessionClosed FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointSessionClosedBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_ID = 4,
+    VT_REASON = 6
+  };
+  uint16_t id() const {
+    return GetField<uint16_t>(VT_ID, 0);
+  }
+  chre::fbs::Reason reason() const {
+    return static_cast<chre::fbs::Reason>(GetField<uint8_t>(VT_REASON, 0));
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_ID) &&
+           VerifyField<uint8_t>(verifier, VT_REASON) &&
+           verifier.EndTable();
+  }
+};
+
+struct EndpointSessionClosedBuilder {
+  typedef EndpointSessionClosed Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_id(uint16_t id) {
+    fbb_.AddElement<uint16_t>(EndpointSessionClosed::VT_ID, id, 0);
+  }
+  void add_reason(chre::fbs::Reason reason) {
+    fbb_.AddElement<uint8_t>(EndpointSessionClosed::VT_REASON, static_cast<uint8_t>(reason), 0);
+  }
+  explicit EndpointSessionClosedBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointSessionClosedBuilder &operator=(const EndpointSessionClosedBuilder &);
+  flatbuffers::Offset<EndpointSessionClosed> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointSessionClosed>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointSessionClosed> CreateEndpointSessionClosed(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t id = 0,
+    chre::fbs::Reason reason = chre::fbs::Reason::UNSPECIFIED) {
+  EndpointSessionClosedBuilder builder_(_fbb);
+  builder_.add_id(id);
+  builder_.add_reason(reason);
+  return builder_.Finish();
+}
+
+struct EndpointSessionMessage FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointSessionMessageBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SESSION_ID = 4,
+    VT_TYPE = 6,
+    VT_PERMISSIONS = 8,
+    VT_DATA = 10,
+    VT_FLAGS = 12,
+    VT_SEQUENCE_NUMBER = 14
+  };
+  /// Id of session this message is being sent within
+  uint16_t session_id() const {
+    return GetField<uint16_t>(VT_SESSION_ID, 0);
+  }
+  /// Type of the message, specific to the Session protocol
+  uint32_t type() const {
+    return GetField<uint32_t>(VT_TYPE, 0);
+  }
+  /// Values from CHRE_MESSAGE_PERMISSION_*. Permissions required to read the
+  /// message.
+  uint32_t permissions() const {
+    return GetField<uint32_t>(VT_PERMISSIONS, 0);
+  }
+  const flatbuffers::Vector<uint8_t> *data() const {
+    return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_DATA);
+  }
+  /// Bitmask of additional flags applied to the message:
+  /// - 0x1: Message delivery status required within 1s
+  uint32_t flags() const {
+    return GetField<uint32_t>(VT_FLAGS, 0);
+  }
+  uint32_t sequence_number() const {
+    return GetField<uint32_t>(VT_SEQUENCE_NUMBER, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_SESSION_ID) &&
+           VerifyField<uint32_t>(verifier, VT_TYPE) &&
+           VerifyField<uint32_t>(verifier, VT_PERMISSIONS) &&
+           VerifyOffset(verifier, VT_DATA) &&
+           verifier.VerifyVector(data()) &&
+           VerifyField<uint32_t>(verifier, VT_FLAGS) &&
+           VerifyField<uint32_t>(verifier, VT_SEQUENCE_NUMBER) &&
+           verifier.EndTable();
+  }
+};
+
+struct EndpointSessionMessageBuilder {
+  typedef EndpointSessionMessage Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_session_id(uint16_t session_id) {
+    fbb_.AddElement<uint16_t>(EndpointSessionMessage::VT_SESSION_ID, session_id, 0);
+  }
+  void add_type(uint32_t type) {
+    fbb_.AddElement<uint32_t>(EndpointSessionMessage::VT_TYPE, type, 0);
+  }
+  void add_permissions(uint32_t permissions) {
+    fbb_.AddElement<uint32_t>(EndpointSessionMessage::VT_PERMISSIONS, permissions, 0);
+  }
+  void add_data(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data) {
+    fbb_.AddOffset(EndpointSessionMessage::VT_DATA, data);
+  }
+  void add_flags(uint32_t flags) {
+    fbb_.AddElement<uint32_t>(EndpointSessionMessage::VT_FLAGS, flags, 0);
+  }
+  void add_sequence_number(uint32_t sequence_number) {
+    fbb_.AddElement<uint32_t>(EndpointSessionMessage::VT_SEQUENCE_NUMBER, sequence_number, 0);
+  }
+  explicit EndpointSessionMessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointSessionMessageBuilder &operator=(const EndpointSessionMessageBuilder &);
+  flatbuffers::Offset<EndpointSessionMessage> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointSessionMessage>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointSessionMessage> CreateEndpointSessionMessage(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t session_id = 0,
+    uint32_t type = 0,
+    uint32_t permissions = 0,
+    flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data = 0,
+    uint32_t flags = 0,
+    uint32_t sequence_number = 0) {
+  EndpointSessionMessageBuilder builder_(_fbb);
+  builder_.add_sequence_number(sequence_number);
+  builder_.add_flags(flags);
+  builder_.add_data(data);
+  builder_.add_permissions(permissions);
+  builder_.add_type(type);
+  builder_.add_session_id(session_id);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<EndpointSessionMessage> CreateEndpointSessionMessageDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t session_id = 0,
+    uint32_t type = 0,
+    uint32_t permissions = 0,
+    const std::vector<uint8_t> *data = nullptr,
+    uint32_t flags = 0,
+    uint32_t sequence_number = 0) {
+  auto data__ = data ? _fbb.CreateVector<uint8_t>(*data) : 0;
+  return chre::fbs::CreateEndpointSessionMessage(
+      _fbb,
+      session_id,
+      type,
+      permissions,
+      data__,
+      flags,
+      sequence_number);
+}
+
+struct EndpointSessionMessageDeliveryStatus FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef EndpointSessionMessageDeliveryStatusBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_SESSION_ID = 4,
+    VT_STATUS = 6
+  };
+  /// Id of session the message was sent within
+  uint16_t session_id() const {
+    return GetField<uint16_t>(VT_SESSION_ID, 0);
+  }
+  const chre::fbs::MessageDeliveryStatus *status() const {
+    return GetPointer<const chre::fbs::MessageDeliveryStatus *>(VT_STATUS);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint16_t>(verifier, VT_SESSION_ID) &&
+           VerifyOffset(verifier, VT_STATUS) &&
+           verifier.VerifyTable(status()) &&
+           verifier.EndTable();
+  }
+};
+
+struct EndpointSessionMessageDeliveryStatusBuilder {
+  typedef EndpointSessionMessageDeliveryStatus Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_session_id(uint16_t session_id) {
+    fbb_.AddElement<uint16_t>(EndpointSessionMessageDeliveryStatus::VT_SESSION_ID, session_id, 0);
+  }
+  void add_status(flatbuffers::Offset<chre::fbs::MessageDeliveryStatus> status) {
+    fbb_.AddOffset(EndpointSessionMessageDeliveryStatus::VT_STATUS, status);
+  }
+  explicit EndpointSessionMessageDeliveryStatusBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  EndpointSessionMessageDeliveryStatusBuilder &operator=(const EndpointSessionMessageDeliveryStatusBuilder &);
+  flatbuffers::Offset<EndpointSessionMessageDeliveryStatus> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<EndpointSessionMessageDeliveryStatus>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<EndpointSessionMessageDeliveryStatus> CreateEndpointSessionMessageDeliveryStatus(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint16_t session_id = 0,
+    flatbuffers::Offset<chre::fbs::MessageDeliveryStatus> status = 0) {
+  EndpointSessionMessageDeliveryStatusBuilder builder_(_fbb);
+  builder_.add_status(status);
+  builder_.add_session_id(session_id);
+  return builder_.Finish();
+}
+
 /// The top-level container that encapsulates all possible messages. Note that
 /// per FlatBuffers requirements, we can't use a union as the top-level
 /// structure (root type), so we must wrap it in a table.
@@ -2900,6 +4827,51 @@
   const chre::fbs::MessageDeliveryStatus *message_as_MessageDeliveryStatus() const {
     return message_type() == chre::fbs::ChreMessage::MessageDeliveryStatus ? static_cast<const chre::fbs::MessageDeliveryStatus *>(message()) : nullptr;
   }
+  const chre::fbs::BtSocketOpen *message_as_BtSocketOpen() const {
+    return message_type() == chre::fbs::ChreMessage::BtSocketOpen ? static_cast<const chre::fbs::BtSocketOpen *>(message()) : nullptr;
+  }
+  const chre::fbs::BtSocketOpenResponse *message_as_BtSocketOpenResponse() const {
+    return message_type() == chre::fbs::ChreMessage::BtSocketOpenResponse ? static_cast<const chre::fbs::BtSocketOpenResponse *>(message()) : nullptr;
+  }
+  const chre::fbs::BtSocketClose *message_as_BtSocketClose() const {
+    return message_type() == chre::fbs::ChreMessage::BtSocketClose ? static_cast<const chre::fbs::BtSocketClose *>(message()) : nullptr;
+  }
+  const chre::fbs::BtSocketCloseResponse *message_as_BtSocketCloseResponse() const {
+    return message_type() == chre::fbs::ChreMessage::BtSocketCloseResponse ? static_cast<const chre::fbs::BtSocketCloseResponse *>(message()) : nullptr;
+  }
+  const chre::fbs::GetMessageHubsAndEndpointsRequest *message_as_GetMessageHubsAndEndpointsRequest() const {
+    return message_type() == chre::fbs::ChreMessage::GetMessageHubsAndEndpointsRequest ? static_cast<const chre::fbs::GetMessageHubsAndEndpointsRequest *>(message()) : nullptr;
+  }
+  const chre::fbs::GetMessageHubsAndEndpointsResponse *message_as_GetMessageHubsAndEndpointsResponse() const {
+    return message_type() == chre::fbs::ChreMessage::GetMessageHubsAndEndpointsResponse ? static_cast<const chre::fbs::GetMessageHubsAndEndpointsResponse *>(message()) : nullptr;
+  }
+  const chre::fbs::RegisterMessageHub *message_as_RegisterMessageHub() const {
+    return message_type() == chre::fbs::ChreMessage::RegisterMessageHub ? static_cast<const chre::fbs::RegisterMessageHub *>(message()) : nullptr;
+  }
+  const chre::fbs::UnregisterMessageHub *message_as_UnregisterMessageHub() const {
+    return message_type() == chre::fbs::ChreMessage::UnregisterMessageHub ? static_cast<const chre::fbs::UnregisterMessageHub *>(message()) : nullptr;
+  }
+  const chre::fbs::RegisterEndpoint *message_as_RegisterEndpoint() const {
+    return message_type() == chre::fbs::ChreMessage::RegisterEndpoint ? static_cast<const chre::fbs::RegisterEndpoint *>(message()) : nullptr;
+  }
+  const chre::fbs::UnregisterEndpoint *message_as_UnregisterEndpoint() const {
+    return message_type() == chre::fbs::ChreMessage::UnregisterEndpoint ? static_cast<const chre::fbs::UnregisterEndpoint *>(message()) : nullptr;
+  }
+  const chre::fbs::OpenEndpointSessionRequest *message_as_OpenEndpointSessionRequest() const {
+    return message_type() == chre::fbs::ChreMessage::OpenEndpointSessionRequest ? static_cast<const chre::fbs::OpenEndpointSessionRequest *>(message()) : nullptr;
+  }
+  const chre::fbs::EndpointSessionOpened *message_as_EndpointSessionOpened() const {
+    return message_type() == chre::fbs::ChreMessage::EndpointSessionOpened ? static_cast<const chre::fbs::EndpointSessionOpened *>(message()) : nullptr;
+  }
+  const chre::fbs::EndpointSessionClosed *message_as_EndpointSessionClosed() const {
+    return message_type() == chre::fbs::ChreMessage::EndpointSessionClosed ? static_cast<const chre::fbs::EndpointSessionClosed *>(message()) : nullptr;
+  }
+  const chre::fbs::EndpointSessionMessage *message_as_EndpointSessionMessage() const {
+    return message_type() == chre::fbs::ChreMessage::EndpointSessionMessage ? static_cast<const chre::fbs::EndpointSessionMessage *>(message()) : nullptr;
+  }
+  const chre::fbs::EndpointSessionMessageDeliveryStatus *message_as_EndpointSessionMessageDeliveryStatus() const {
+    return message_type() == chre::fbs::ChreMessage::EndpointSessionMessageDeliveryStatus ? static_cast<const chre::fbs::EndpointSessionMessageDeliveryStatus *>(message()) : nullptr;
+  }
   /// The originating or destination client ID on the host side, used to direct
   /// responses only to the client that sent the request. Although initially
   /// populated by the requesting client, this is enforced to be the correct
@@ -3047,6 +5019,66 @@
   return message_as_MessageDeliveryStatus();
 }
 
+template<> inline const chre::fbs::BtSocketOpen *MessageContainer::message_as<chre::fbs::BtSocketOpen>() const {
+  return message_as_BtSocketOpen();
+}
+
+template<> inline const chre::fbs::BtSocketOpenResponse *MessageContainer::message_as<chre::fbs::BtSocketOpenResponse>() const {
+  return message_as_BtSocketOpenResponse();
+}
+
+template<> inline const chre::fbs::BtSocketClose *MessageContainer::message_as<chre::fbs::BtSocketClose>() const {
+  return message_as_BtSocketClose();
+}
+
+template<> inline const chre::fbs::BtSocketCloseResponse *MessageContainer::message_as<chre::fbs::BtSocketCloseResponse>() const {
+  return message_as_BtSocketCloseResponse();
+}
+
+template<> inline const chre::fbs::GetMessageHubsAndEndpointsRequest *MessageContainer::message_as<chre::fbs::GetMessageHubsAndEndpointsRequest>() const {
+  return message_as_GetMessageHubsAndEndpointsRequest();
+}
+
+template<> inline const chre::fbs::GetMessageHubsAndEndpointsResponse *MessageContainer::message_as<chre::fbs::GetMessageHubsAndEndpointsResponse>() const {
+  return message_as_GetMessageHubsAndEndpointsResponse();
+}
+
+template<> inline const chre::fbs::RegisterMessageHub *MessageContainer::message_as<chre::fbs::RegisterMessageHub>() const {
+  return message_as_RegisterMessageHub();
+}
+
+template<> inline const chre::fbs::UnregisterMessageHub *MessageContainer::message_as<chre::fbs::UnregisterMessageHub>() const {
+  return message_as_UnregisterMessageHub();
+}
+
+template<> inline const chre::fbs::RegisterEndpoint *MessageContainer::message_as<chre::fbs::RegisterEndpoint>() const {
+  return message_as_RegisterEndpoint();
+}
+
+template<> inline const chre::fbs::UnregisterEndpoint *MessageContainer::message_as<chre::fbs::UnregisterEndpoint>() const {
+  return message_as_UnregisterEndpoint();
+}
+
+template<> inline const chre::fbs::OpenEndpointSessionRequest *MessageContainer::message_as<chre::fbs::OpenEndpointSessionRequest>() const {
+  return message_as_OpenEndpointSessionRequest();
+}
+
+template<> inline const chre::fbs::EndpointSessionOpened *MessageContainer::message_as<chre::fbs::EndpointSessionOpened>() const {
+  return message_as_EndpointSessionOpened();
+}
+
+template<> inline const chre::fbs::EndpointSessionClosed *MessageContainer::message_as<chre::fbs::EndpointSessionClosed>() const {
+  return message_as_EndpointSessionClosed();
+}
+
+template<> inline const chre::fbs::EndpointSessionMessage *MessageContainer::message_as<chre::fbs::EndpointSessionMessage>() const {
+  return message_as_EndpointSessionMessage();
+}
+
+template<> inline const chre::fbs::EndpointSessionMessageDeliveryStatus *MessageContainer::message_as<chre::fbs::EndpointSessionMessageDeliveryStatus>() const {
+  return message_as_EndpointSessionMessageDeliveryStatus();
+}
+
 struct MessageContainerBuilder {
   typedef MessageContainer Table;
   flatbuffers::FlatBufferBuilder &fbb_;
@@ -3086,6 +5118,60 @@
   return builder_.Finish();
 }
 
+inline bool VerifyChannelInfo(flatbuffers::Verifier &verifier, const void *obj, ChannelInfo type) {
+  switch (type) {
+    case ChannelInfo::NONE: {
+      return true;
+    }
+    case ChannelInfo::LeCocChannelInfo: {
+      auto ptr = reinterpret_cast<const chre::fbs::LeCocChannelInfo *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    default: return true;
+  }
+}
+
+inline bool VerifyChannelInfoVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
+  if (!values || !types) return !values && !types;
+  if (values->size() != types->size()) return false;
+  for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
+    if (!VerifyChannelInfo(
+        verifier,  values->Get(i), types->GetEnum<ChannelInfo>(i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+inline bool VerifyMessageHubDetails(flatbuffers::Verifier &verifier, const void *obj, MessageHubDetails type) {
+  switch (type) {
+    case MessageHubDetails::NONE: {
+      return true;
+    }
+    case MessageHubDetails::HubInfoResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::HubInfoResponse *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case MessageHubDetails::VendorHubInfo: {
+      auto ptr = reinterpret_cast<const chre::fbs::VendorHubInfo *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    default: return true;
+  }
+}
+
+inline bool VerifyMessageHubDetailsVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
+  if (!values || !types) return !values && !types;
+  if (values->size() != types->size()) return false;
+  for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
+    if (!VerifyMessageHubDetails(
+        verifier,  values->Get(i), types->GetEnum<MessageHubDetails>(i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
 inline bool VerifyChreMessage(flatbuffers::Verifier &verifier, const void *obj, ChreMessage type) {
   switch (type) {
     case ChreMessage::NONE: {
@@ -3219,6 +5305,66 @@
       auto ptr = reinterpret_cast<const chre::fbs::MessageDeliveryStatus *>(obj);
       return verifier.VerifyTable(ptr);
     }
+    case ChreMessage::BtSocketOpen: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketOpen *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::BtSocketOpenResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketOpenResponse *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::BtSocketClose: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketClose *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::BtSocketCloseResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::BtSocketCloseResponse *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsRequest: {
+      auto ptr = reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsRequest *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::GetMessageHubsAndEndpointsResponse: {
+      auto ptr = reinterpret_cast<const chre::fbs::GetMessageHubsAndEndpointsResponse *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::RegisterMessageHub: {
+      auto ptr = reinterpret_cast<const chre::fbs::RegisterMessageHub *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::UnregisterMessageHub: {
+      auto ptr = reinterpret_cast<const chre::fbs::UnregisterMessageHub *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::RegisterEndpoint: {
+      auto ptr = reinterpret_cast<const chre::fbs::RegisterEndpoint *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::UnregisterEndpoint: {
+      auto ptr = reinterpret_cast<const chre::fbs::UnregisterEndpoint *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::OpenEndpointSessionRequest: {
+      auto ptr = reinterpret_cast<const chre::fbs::OpenEndpointSessionRequest *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::EndpointSessionOpened: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionOpened *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::EndpointSessionClosed: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionClosed *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::EndpointSessionMessage: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionMessage *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case ChreMessage::EndpointSessionMessageDeliveryStatus: {
+      auto ptr = reinterpret_cast<const chre::fbs::EndpointSessionMessageDeliveryStatus *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
     default: return true;
   }
 }
diff --git a/platform/shared/include/chre/platform/shared/host_protocol_chre.h b/platform/shared/include/chre/platform/shared/host_protocol_chre.h
index 62421c0..8857fad 100644
--- a/platform/shared/include/chre/platform/shared/host_protocol_chre.h
+++ b/platform/shared/include/chre/platform/shared/host_protocol_chre.h
@@ -18,6 +18,7 @@
 #define CHRE_PLATFORM_SHARED_HOST_PROTOCOL_CHRE_H_
 
 #include <stdint.h>
+#include <cstdint>
 
 #include "chre/core/event_loop_common.h"
 #include "chre/core/nanoapp.h"
@@ -100,6 +101,15 @@
 
   static void handleNanConfigurationUpdate(bool enabled);
 
+  static void handleBtSocketOpen(uint16_t hostClientId, uint64_t socketId,
+                                 const char *name, uint64_t endpointId,
+                                 uint64_t hubId, uint32_t aclConnectionHandle,
+                                 uint32_t localCid, uint32_t remoteCid,
+                                 uint32_t psm, uint32_t localMtu,
+                                 uint32_t remoteMtu, uint32_t localMps,
+                                 uint32_t remoteMps, uint32_t initialRxCredits,
+                                 uint32_t initialTxCredits);
+
  private:
   static void sendFragmentResponse(uint16_t hostClientId,
                                    uint32_t transactionId, uint32_t fragmentId,
@@ -153,10 +163,11 @@
   static bool decodeMessageFromHost(const void *message, size_t messageLen);
 
   /**
-   * Refer to the context hub HAL definition for a details of these parameters.
+   * Refer to the context hub HAL definition for a details of these
+   * parameters.
    *
-   * @param builder A newly constructed ChreFlatBufferBuilder that will be used
-   * to encode the message
+   * @param builder A newly constructed ChreFlatBufferBuilder that will be
+   * used to encode the message
    */
   static void encodeHubInfoResponse(
       ChreFlatBufferBuilder &builder, const char *name, const char *vendor,
@@ -324,6 +335,32 @@
    */
   static void encodeNanConfigurationRequest(ChreFlatBufferBuilder &builder,
                                             bool enable);
+
+  /**
+   * Encodes a BT socket open response.
+   *
+   * @param builder An instance of the CHRE Flatbuffer builder.
+   * @param hostClientId Host client identifier.
+   * @param success Whether the socket open request was successful.
+   * @param reason Failure reason if success is false.
+   * @param socketId BT socket identifier.
+   */
+  static void encodeBtSocketOpenResponse(ChreFlatBufferBuilder &builder,
+                                         uint16_t hostClientId,
+                                         uint64_t socketId, bool success,
+                                         const char *reason);
+
+  /**
+   * Encodes a BT socket close request.
+   *
+   * @param builder An instance of the CHRE Flatbuffer builder.
+   * @param hostClientId Host client identifier.
+   * @param reason Reason socket is being closed.
+   * @param socketId BT socket identifier.
+   */
+  static void encodeBtSocketClose(ChreFlatBufferBuilder &builder,
+                                  uint16_t hostClientId, uint64_t socketId,
+                                  const char *reason);
 };
 
 }  // namespace chre
diff --git a/platform/shared/include/chre/target_platform/platform_cache_management.h b/platform/shared/include/chre/platform/shared/platform_cache_management.h
similarity index 100%
rename from platform/shared/include/chre/target_platform/platform_cache_management.h
rename to platform/shared/include/chre/platform/shared/platform_cache_management.h
diff --git a/platform/shared/log_buffer_manager.cc b/platform/shared/log_buffer_manager.cc
index e408d0c..d090068 100644
--- a/platform/shared/log_buffer_manager.cc
+++ b/platform/shared/log_buffer_manager.cc
@@ -22,6 +22,13 @@
 #include "chre/platform/shared/generated/host_messages_generated.h"
 #include "chre/util/lock_guard.h"
 
+#ifdef CHRE_TOKENIZED_LOGGING_ENABLED
+#include "chre/platform/log.h"
+#include "pw_log_tokenized/config.h"
+#include "pw_tokenizer/encode_args.h"
+#include "pw_tokenizer/tokenize.h"
+#endif  // CHRE_TOKENIZED_LOGGING_ENABLED
+
 void chrePlatformLogToBuffer(chreLogLevel chreLogLevel, const char *format,
                              ...) {
   va_list args;
@@ -44,6 +51,23 @@
   chre::LogBufferManagerSingleton::get()->logBtSnoop(direction, buffer, size);
 }
 
+#ifdef CHRE_TOKENIZED_LOGGING_ENABLED
+// The callback function that must be defined to handle an encoded
+// tokenizer message.
+void EncodeTokenizedMessage(uint32_t level, pw_tokenizer_Token token,
+                            pw_tokenizer_ArgTypes types, ...) {
+  va_list args;
+  va_start(args, types);
+  pw::tokenizer::EncodedMessage<pw::log_tokenized::kEncodingBufferSizeBytes>
+      encodedMessage(token, types, args);
+  va_end(args);
+
+  chrePlatformEncodedLogToBuffer(static_cast<chreLogLevel>(level),
+                                 encodedMessage.data_as_uint8(),
+                                 encodedMessage.size());
+}
+#endif  // CHRE_TOKENIZED_LOGGING_ENABLED
+
 namespace chre {
 
 using LogType = fbs::LogType;
diff --git a/platform/shared/nanoapp_load_manager.cc b/platform/shared/nanoapp_load_manager.cc
index 67de637..f786823 100644
--- a/platform/shared/nanoapp_load_manager.cc
+++ b/platform/shared/nanoapp_load_manager.cc
@@ -23,12 +23,6 @@
                                         uint32_t appVersion, uint32_t appFlags,
                                         size_t totalBinaryLen,
                                         uint32_t targetApiVersion) {
-  if (hasPendingLoadTransaction()) {
-    LOGW(
-        "Pending load transaction already exists. Overriding previous"
-        " transaction.");
-  }
-
   mCurrentLoadInfo.hostClientId = hostClientId;
   mCurrentLoadInfo.transactionId = transactionId;
   mCurrentLoadInfo.nextFragmentId = 1;
diff --git a/platform/shared/nanoapp_loader.cc b/platform/shared/nanoapp_loader.cc
index 9fce9ac..5eea156 100644
--- a/platform/shared/nanoapp_loader.cc
+++ b/platform/shared/nanoapp_loader.cc
@@ -27,7 +27,7 @@
 #include "chre/platform/shared/debug_dump.h"
 #include "chre/platform/shared/memory.h"
 #include "chre/platform/shared/nanoapp/tokenized_log.h"
-#include "chre/target_platform/platform_cache_management.h"
+#include "chre/platform/shared/platform_cache_management.h"
 #include "chre/util/dynamic_vector.h"
 #include "chre/util/macros.h"
 
@@ -133,7 +133,7 @@
   return cos(rad);
 }
 
-float sqrtOverride(float val) {
+double sqrtOverride(double val) {
   return sqrt(val);
 }
 
@@ -178,6 +178,7 @@
     ADD_EXPORTED_C_SYMBOL(fmaxf),
     ADD_EXPORTED_C_SYMBOL(fminf),
     ADD_EXPORTED_C_SYMBOL(fmodf),
+    ADD_EXPORTED_C_SYMBOL(ldexpf),
     ADD_EXPORTED_C_SYMBOL(log10f),
     ADD_EXPORTED_C_SYMBOL(log1pf),
     ADD_EXPORTED_C_SYMBOL(log2f),
diff --git a/platform/shared/pal_ble_stub.cc b/platform/shared/pal_ble_stub.cc
index f34fdca..77f22f7 100644
--- a/platform/shared/pal_ble_stub.cc
+++ b/platform/shared/pal_ble_stub.cc
@@ -16,10 +16,7 @@
 
 #include "chre/pal/ble.h"
 
-#include "chre/util/macros.h"
-
-const struct chrePalBleApi *chrePalBleGetApi(uint32_t requestedApiVersion) {
-  UNUSED_VAR(requestedApiVersion);
+const struct chrePalBleApi *chrePalBleGetApi(uint32_t /*requestedApiVersion*/) {
   // This stub implementation of the CHRE PAL returns nullptr to indicate that
   // it is not supplied by this platform.
   return nullptr;
diff --git a/platform/shared/pal_gnss_stub.cc b/platform/shared/pal_gnss_stub.cc
index 6b46b99..d279a15 100644
--- a/platform/shared/pal_gnss_stub.cc
+++ b/platform/shared/pal_gnss_stub.cc
@@ -16,7 +16,8 @@
 
 #include "chre/pal/gnss.h"
 
-const struct chrePalGnssApi *chrePalGnssGetApi(uint32_t requestedApiVersion) {
+const struct chrePalGnssApi *chrePalGnssGetApi(
+    uint32_t /*requestedApiVersion*/) {
   // This stub implementation of the CHRE PAL returns nullptr to indicate that
   // it is not supplied by this platform.
   return nullptr;
diff --git a/platform/shared/pal_sensor_stub.cc b/platform/shared/pal_sensor_stub.cc
index 235d023..10d27e7 100644
--- a/platform/shared/pal_sensor_stub.cc
+++ b/platform/shared/pal_sensor_stub.cc
@@ -16,11 +16,8 @@
 
 #include "chre/pal/sensor.h"
 
-#include "chre/util/macros.h"
-
 const struct chrePalSensorApi *chrePalSensorGetApi(
-    uint32_t requestedApiVersion) {
-  UNUSED_VAR(requestedApiVersion);
+    uint32_t /*requestedApiVersion*/) {
 
   // This stub implementation of the CHRE PAL returns nullptr to indicate that
   // it is not supplied by this platform.
diff --git a/platform/shared/pal_wifi_stub.cc b/platform/shared/pal_wifi_stub.cc
index 19d76aa..ef3059a 100644
--- a/platform/shared/pal_wifi_stub.cc
+++ b/platform/shared/pal_wifi_stub.cc
@@ -16,7 +16,8 @@
 
 #include "chre/pal/wifi.h"
 
-const struct chrePalWifiApi *chrePalWifiGetApi(uint32_t requestedApiVersion) {
+const struct chrePalWifiApi *chrePalWifiGetApi(
+    uint32_t /*requestedApiVersion*/) {
   // This stub implementation of the CHRE PAL returns nullptr to indicate that
   // it is not supplied by this platform.
   return nullptr;
diff --git a/platform/shared/pal_wwan_stub.cc b/platform/shared/pal_wwan_stub.cc
index 4233534..7908d12 100644
--- a/platform/shared/pal_wwan_stub.cc
+++ b/platform/shared/pal_wwan_stub.cc
@@ -16,7 +16,8 @@
 
 #include "chre/pal/wwan.h"
 
-const struct chrePalWwanApi *chrePalWwanGetApi(uint32_t requestedApiVersion) {
+const struct chrePalWwanApi *chrePalWwanGetApi(
+    uint32_t /*requestedApiVersion*/) {
   // This stub implementation of the CHRE PAL returns nullptr to indicate that
   // it is not supplied by this platform.
   return nullptr;
diff --git a/platform/shared/platform_ble.cc b/platform/shared/platform_ble.cc
index 54e5f1e..bf1f1cb 100644
--- a/platform/shared/platform_ble.cc
+++ b/platform/shared/platform_ble.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_BLE_SUPPORT_ENABLED
+
 #include "chre/platform/platform_ble.h"
 
 #include <cinttypes>
@@ -166,3 +168,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_BLE_SUPPORT_ENABLED
diff --git a/platform/shared/platform_gnss.cc b/platform/shared/platform_gnss.cc
index 2c712bf..f667a63 100644
--- a/platform/shared/platform_gnss.cc
+++ b/platform/shared/platform_gnss.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_GNSS_SUPPORT_ENABLED
+
 #include "chre/platform/platform_gnss.h"
 
 #include <cinttypes>
@@ -158,3 +160,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_GNSS_SUPPORT_ENABLED
diff --git a/platform/shared/platform_wifi.cc b/platform/shared/platform_wifi.cc
index 84c9a3c..17681fb 100644
--- a/platform/shared/platform_wifi.cc
+++ b/platform/shared/platform_wifi.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_WIFI_SUPPORT_ENABLED
+
 #include "chre/platform/platform_wifi.h"
 
 #include <cinttypes>
@@ -257,3 +259,5 @@
 }
 
 }  // namespace chre
+
+#endif   // CHRE_WIFI_SUPPORT_ENABLED
diff --git a/platform/shared/platform_wwan.cc b/platform/shared/platform_wwan.cc
index 2e1119a..67f1ddc 100644
--- a/platform/shared/platform_wwan.cc
+++ b/platform/shared/platform_wwan.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#ifdef CHRE_WWAN_SUPPORT_ENABLED
+
 #include "chre/platform/platform_wwan.h"
 
 #include <cinttypes>
@@ -92,3 +94,5 @@
 }
 
 }  // namespace chre
+
+#endif  // CHRE_WWAN_SUPPORT_ENABLED
diff --git a/platform/shared/include/chre/target_platform/platform_ble_base.h b/platform/shared/public_platform_ble_pal/chre/target_platform/platform_ble_base.h
similarity index 100%
rename from platform/shared/include/chre/target_platform/platform_ble_base.h
rename to platform/shared/public_platform_ble_pal/chre/target_platform/platform_ble_base.h
diff --git a/platform/shared/include/chre/target_platform/platform_debug_dump_manager_base.h b/platform/shared/public_platform_debug_dump_manager/chre/target_platform/platform_debug_dump_manager_base.h
similarity index 100%
rename from platform/shared/include/chre/target_platform/platform_debug_dump_manager_base.h
rename to platform/shared/public_platform_debug_dump_manager/chre/target_platform/platform_debug_dump_manager_base.h
diff --git a/platform/shared/include/chre/target_platform/platform_gnss_base.h b/platform/shared/public_platform_gnss_pal/chre/target_platform/platform_gnss_base.h
similarity index 100%
rename from platform/shared/include/chre/target_platform/platform_gnss_base.h
rename to platform/shared/public_platform_gnss_pal/chre/target_platform/platform_gnss_base.h
diff --git a/platform/shared/include/chre/target_platform/platform_wifi_base.h b/platform/shared/public_platform_wifi_pal/chre/target_platform/platform_wifi_base.h
similarity index 100%
rename from platform/shared/include/chre/target_platform/platform_wifi_base.h
rename to platform/shared/public_platform_wifi_pal/chre/target_platform/platform_wifi_base.h
diff --git a/platform/shared/include/chre/target_platform/platform_wwan_base.h b/platform/shared/public_platform_wwan_pal/chre/target_platform/platform_wwan_base.h
similarity index 100%
rename from platform/shared/include/chre/target_platform/platform_wwan_base.h
rename to platform/shared/public_platform_wwan_pal/chre/target_platform/platform_wwan_base.h
diff --git a/platform/slpi/host_link.cc b/platform/slpi/host_link.cc
index 3e07a07..a32f9e6 100644
--- a/platform/slpi/host_link.cc
+++ b/platform/slpi/host_link.cc
@@ -36,10 +36,10 @@
 #include "chre/platform/slpi/power_control_util.h"
 #include "chre/platform/system_time.h"
 #include "chre/platform/system_timer.h"
-#include "chre/util/fixed_size_blocking_queue.h"
 #include "chre/util/flatbuffers/helpers.h"
 #include "chre/util/macros.h"
 #include "chre/util/nested_data_ptr.h"
+#include "chre/util/system/fixed_size_blocking_queue.h"
 #include "chre/util/unique_ptr.h"
 #include "chre_api/chre/version.h"
 
@@ -928,4 +928,14 @@
 #endif  // CHRE_WIFI_NAN_SUPPORT_ENABLED
 }
 
+void HostMessageHandlers::handleBtSocketOpen(
+    uint16_t /* hostClientId */, uint64_t /* socketId */,
+    const char * /* name */, uint64_t /* endpointId */, uint64_t /* hubId */,
+    uint32_t /* aclConnectionHandle */, uint32_t /* localCid */,
+    uint32_t /* remoteCid */, uint32_t /* psm */, uint32_t /* localMtu */,
+    uint32_t /* remoteMtu */, uint32_t /* localMps */, uint32_t /* remoteMps */,
+    uint32_t /* initialRxCredits */, uint32_t /* initialTxCredits */) {
+  LOGE("BT Socket offload not supported");
+}
+
 }  // namespace chre
diff --git a/platform/tinysys/chre_init.cc b/platform/tinysys/chre_init.cc
index 90989de..83abb87 100644
--- a/platform/tinysys/chre_init.cc
+++ b/platform/tinysys/chre_init.cc
@@ -15,8 +15,12 @@
  */
 
 #include "chre/target_platform/chre_init.h"
+#include "chre/platform/shared/dram_vote_client.h"
 
 BaseType_t chreTinysysInit() {
+  chre::DramVoteClientSingleton::init();
+  chre::freertos::initLogger();
+
   return chre::freertos::init();
 }
 
diff --git a/platform/tinysys/host_link.cc b/platform/tinysys/host_link.cc
index a393da9..f6f4712 100644
--- a/platform/tinysys/host_link.cc
+++ b/platform/tinysys/host_link.cc
@@ -84,24 +84,32 @@
 // SCP get ack data from AP (SCP to AP)
 SRAM_REGION_BSS int gChreIpiAckFromHost[2];
 
+#ifdef SCP_CHRE_USE_DMA
+// The min total size of a message to trigger DMA for sending/receiving.
+constexpr uint32_t kMinMessageSizeForDma = 0x1000;  // 4k
+#endif
+
+// The buffer used to receive messages from AP.
+// The size should be consistent with the max sending size on the host side.
+constexpr uint32_t kChreIpiRecvBufferSize = 0x8000;  // 32k
+DRAM_REGION_VARIABLE uint8_t gChreRecvBuffer[kChreIpiRecvBufferSize]
+    __attribute__((aligned(CACHE_LINE_SIZE)));
+
+#ifdef SCP_CHRE_USE_DMA
+// Rounds up the value to be aligned with CACHE_LINE_SIZE.
+static inline uint32_t alignToCacheLine(uint32_t value) {
+  // alignment must be a power of 2.
+  static_assert(CACHE_LINE_SIZE > 0 &&
+                (CACHE_LINE_SIZE & (CACHE_LINE_SIZE - 1)) == 0);
+  return (value + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
+}
+#endif
+
 void *gChreSubregionRecvAddr;
 size_t gChreSubregionRecvSize;
 void *gChreSubregionSendAddr;
 size_t gChreSubregionSendSize;
 
-// TODO(b/277235389): move it to HostLinkBase, and revisit buffer size
-// payload buffers
-#define CHRE_IPI_RECV_BUFFER_SIZE (CHRE_MESSAGE_TO_HOST_MAX_SIZE + 128)
-DRAM_REGION_VARIABLE uint32_t
-    gChreRecvBuffer[CHRE_IPI_RECV_BUFFER_SIZE / sizeof(uint32_t)]
-    __attribute__((aligned(CACHE_LINE_SIZE)));
-
-#ifdef SCP_CHRE_USE_DMA
-static inline uint32_t align(uint32_t target, uint32_t size) {
-  return (target + size - 1) & ~(size - 1);
-}
-#endif
-
 #define SCP_CHRE_MAGIC 0x67728269
 struct ScpChreIpiMsg {
   uint32_t magic;
@@ -132,6 +140,8 @@
   NanConfigurationRequest,
   PulseRequest,
   PulseResponse,
+  NanoappTokenDatabaseInfo,
+  MessageDeliveryStatus,
 };
 
 struct PendingMessage {
@@ -160,7 +170,7 @@
 };
 
 constexpr size_t kOutboundQueueSize = 100;
-DRAM_REGION_VARIABLE FixedSizeBlockingQueue<PendingMessage, kOutboundQueueSize>
+SRAM_REGION_VARIABLE FixedSizeBlockingQueue<PendingMessage, kOutboundQueueSize>
     gOutboundQueue;
 
 typedef void(MessageBuilderFunction)(ChreFlatBufferBuilder &builder,
@@ -243,25 +253,9 @@
     case PendingMessageType::HubInfoResponse:
       result = generateHubInfoResponse(pendingMsg.data.hostClientId);
       break;
-
-    case PendingMessageType::NanoappListResponse:
-    case PendingMessageType::LoadNanoappResponse:
-    case PendingMessageType::UnloadNanoappResponse:
-    case PendingMessageType::DebugDumpData:
-    case PendingMessageType::DebugDumpResponse:
-    case PendingMessageType::TimeSyncRequest:
-    case PendingMessageType::LowPowerMicAccessRequest:
-    case PendingMessageType::LowPowerMicAccessRelease:
-    case PendingMessageType::EncodedLogMessage:
-    case PendingMessageType::SelfTestResponse:
-    case PendingMessageType::MetricLog:
-    case PendingMessageType::NanConfigurationRequest:
-    case PendingMessageType::PulseResponse:
+    default:
       result = generateMessageFromBuilder(pendingMsg.data.builder);
       break;
-
-    default:
-      CHRE_ASSERT_LOG(false, "Unexpected pending message type");
   }
   return result;
 }
@@ -479,9 +473,9 @@
   struct ScpChreIpiMsg msg = *(struct ScpChreIpiMsg *)data;
 
   // check the magic number and payload size need to be copy(if need) */
-  LOGV("%s: msg.magic=0x%x, msg.size=%u", __func__, msg.magic, msg.size);
+  LOGD("%s: Received a message from AP. Size=%u", __func__, msg.size);
   if (msg.magic != SCP_CHRE_MAGIC) {
-    LOGE("Invalid magic number, skip message");
+    LOGE("Invalid magic number: 0x%x, skip message", msg.magic);
     gChreIpiAckToHost[0] = IPI_NO_MEMORY;
     gChreIpiAckToHost[1] = 0;
     return;
@@ -492,25 +486,38 @@
       ap_to_scp(reinterpret_cast<uint32_t>(gChreSubregionRecvAddr));
 
 #ifdef SCP_CHRE_USE_DMA
-  // Using SCP DMA HW to copy the data from share memory to SCP side, ex:
-  // gChreRecvBuffer gChreRecvBuffer could be a global variables or a SCP heap
-  // memory at SRAM/DRAM
-  scp_dma_transaction_dram(reinterpret_cast<uint32_t>(&gChreRecvBuffer[0]),
-                           srcAddr, msg.size, DMA_MEM_ID, NO_RESERVED);
+  if (msg.size < kMinMessageSizeForDma) {
+    dvfs_enable_DRAM_resource(CHRE_MEM_ID);
+    memcpy(static_cast<void *>(gChreRecvBuffer),
+           reinterpret_cast<void *>(srcAddr), msg.size);
+    dvfs_disable_DRAM_resource(CHRE_MEM_ID);
+  } else {
+    auto dstAddr = reinterpret_cast<uint32_t>(gChreRecvBuffer);
 
-  // Invalid cache to update the newest data before using
-  mrv_dcache_invalid_multi_addr(reinterpret_cast<uint32_t>(&gChreRecvBuffer[0]),
-                                align(msg.size, CACHE_LINE_SIZE));
-#else
+    // destination address for receiving data is in a cacheable memory, it
+    // should be invalidated/flushed before transferring from share buffer to
+    // SCP
+    scp_dcache_flush(dstAddr, alignToCacheLine(msg.size));
+
+    // Using SCP DMA HW to copy the data from share memory to SCP side.
+    // The dstAddr could be a global variables or a SCP heap memory at SRAM/DRAM
+    DMA_RESULT result = scp_dma_transaction_dram(dstAddr, srcAddr, msg.size,
+                                                 DMA_MEM_ID, NO_RESERVED);
+
+    if (result != DMA_RESULT_DONE) {
+      LOGE("Failed to receive a message from AP using DMA");
+    }
+  }
+#else  // SCP_CHRE_USE_DMA
+
   dvfs_enable_DRAM_resource(CHRE_MEM_ID);
   memcpy(static_cast<void *>(gChreRecvBuffer),
          reinterpret_cast<void *>(srcAddr), msg.size);
   dvfs_disable_DRAM_resource(CHRE_MEM_ID);
-#endif
+
+#endif  // SCP_CHRE_USE_DMA
 
   // process the message
-  LOGV("chre_rcvbuf: 0x%x 0x%x 0x%x 0x%x", gChreRecvBuffer[0],
-       gChreRecvBuffer[1], gChreRecvBuffer[2], gChreRecvBuffer[3]);
   receive(static_cast<HostLinkBase *>(prdata), gChreRecvBuffer, msg.size);
 
   // After finishing the job, akc the message to host
@@ -519,11 +526,15 @@
 }
 
 DRAM_REGION_FUNCTION void HostLinkBase::initializeIpi(void) {
-  LOGV("%s", __func__);
   bool success = false;
   int ret;
   constexpr size_t kBackgroundTaskStackSize = 1024;
+
+#ifdef PRI_CHRE_BACKGROUND
+  constexpr UBaseType_t kBackgroundTaskPriority = PRI_CHRE_BACKGROUND;
+#else
   constexpr UBaseType_t kBackgroundTaskPriority = 2;
+#endif
 
   // prepared share memory information and register the callback functions
   if (!(ret = scp_get_reserve_mem_by_id(SCP_CHRE_FROM_MEM_ID,
@@ -585,20 +596,49 @@
   msg.magic = SCP_CHRE_MAGIC;
   msg.size = dataLen;
 
-  // Mapping the physical address of share memory for SCP
-  void *dstAddr = reinterpret_cast<void *>(
-      ap_to_scp(reinterpret_cast<uint32_t>(gChreSubregionSendAddr)));
+  uint32_t dstAddr =
+      ap_to_scp(reinterpret_cast<uint32_t>(gChreSubregionSendAddr));
 
 #ifdef SCP_CHRE_USE_DMA
-  // TODO(b/288415339): use DMA for larger payload
-  // No need cache operation, because src_dst handled by SCP CPU and dstAddr is
-  // non-cacheable
+  if (dataLen < kMinMessageSizeForDma) {
+    dvfs_enable_DRAM_resource(CHRE_MEM_ID);
+    memcpy(reinterpret_cast<void *>(dstAddr), data, dataLen);
+    dvfs_disable_DRAM_resource(CHRE_MEM_ID);
+  } else {
+    auto srcAddr = reinterpret_cast<uint32_t>(data);
+    auto msgSize = reinterpret_cast<uint32_t>(msg.size);
+
+    // Separate the message into 2 parts, copySize and dmaSize, and use memcpy
+    // and dma to transfer them respectively. This is needed due to the
+    // alignment requirement of the dma transfer.
+    uint32_t dmaStartSrcAddr = alignToCacheLine(srcAddr);
+    uint32_t copySize = dmaStartSrcAddr - srcAddr;
+    uint32_t dmaSize = msgSize - copySize;
+
+    if (copySize > 0) {
+      dvfs_enable_DRAM_resource(CHRE_MEM_ID);
+      memcpy(reinterpret_cast<void *>(dstAddr), data, copySize);
+      dvfs_disable_DRAM_resource(CHRE_MEM_ID);
+    }
+
+    // source address for sending data is in a cacheable memory, it should
+    // be invalidated/flushed before transferring from SCP to shared buffer
+    scp_dcache_flush(dmaStartSrcAddr, alignToCacheLine(dmaSize));
+
+    // Using SCP DMA HW to copy the data from SCP to shared memory.
+    // The dstAddr could be a global variables or a SCP heap memory at SRAM/DRAM
+    DMA_RESULT result = scp_dma_transaction_dram(
+        dstAddr + copySize, dmaStartSrcAddr, dmaSize, DMA_MEM_ID, NO_RESERVED);
+
+    if (result != DMA_RESULT_DONE) {
+      LOGE("Failed to receive a message from AP using DMA");
+    }
+  }
 #else
   dvfs_enable_DRAM_resource(CHRE_MEM_ID);
-  memcpy(dstAddr, data, dataLen);
+  memcpy(reinterpret_cast<void *>(dstAddr), data, dataLen);
   dvfs_disable_DRAM_resource(CHRE_MEM_ID);
 #endif
-
   // NB: len param for ipi_send is in number of 32-bit words
   int ret = ipi_send_compl(
       IPI_OUT_C_SCP_HOST_CHRE, &msg, sizeof(msg) / sizeof(uint32_t),
@@ -607,18 +647,17 @@
     LOGE("chre ipi send fail(%d)", ret);
   } else {
     /* check ack data for make sure IPI wasn't busy */
-    LOGV("chre ipi send, check ack data: 0x%x", gChreIpiAckFromHost[0]);
     if (gChreIpiAckFromHost[0] == IPI_ACTION_DONE) {
       LOGV("chre ipi send done, you can send another IPI");
     } else if (gChreIpiAckFromHost[0] == IPI_PIN_BUSY) {
       /* you may have to re-send the IPI, or drop this one */
-      LOGV(
+      LOGW(
           "chre ipi send busy, user thread has not wait the IPI until job "
           "finished");
     } else if (gChreIpiAckFromHost[0] == IPI_NO_MEMORY) {
-      LOGV("chre ipi send with wrong size(%zu)", dataLen);
+      LOGW("chre ipi send with wrong size(%zu)", dataLen);
     } else {
-      LOGV("chre ipi send unknown case");
+      LOGW("chre ipi send unknown case: 0x%x", gChreIpiAckFromHost[0]);
     }
   }
 
@@ -680,9 +719,21 @@
   return success;
 }
 
-bool HostLink::sendMessageDeliveryStatus(uint32_t /* messageSequenceNumber */,
-                                         uint8_t /* errorCode */) {
-  return false;
+DRAM_REGION_FUNCTION bool HostLink::sendMessageDeliveryStatus(
+    uint32_t messageSequenceNumber, uint8_t errorCode) {
+  struct DeliveryStatusData {
+    uint32_t messageSequenceNumber;
+    uint8_t errorCode;
+  } args{messageSequenceNumber, errorCode};
+
+  auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) {
+    auto args = static_cast<const DeliveryStatusData *>(cookie);
+    HostProtocolChre::encodeMessageDeliveryStatus(
+        builder, args->messageSequenceNumber, args->errorCode);
+  };
+
+  return buildAndEnqueueMessage(PendingMessageType::MessageDeliveryStatus,
+                                /* initialBufferSize= */ 64, msgBuilder, &args);
 }
 
 // TODO(b/285219398): HostMessageHandlers member function implementations are
@@ -704,7 +755,9 @@
 }
 
 DRAM_REGION_FUNCTION void HostMessageHandlers::handleMessageDeliveryStatus(
-    uint32_t /* messageSequenceNumber */, uint8_t /* errorCode */) {}
+    uint32_t messageSequenceNumber, uint8_t errorCode) {
+  getHostCommsManager().completeTransaction(messageSequenceNumber, errorCode);
+}
 
 DRAM_REGION_FUNCTION void HostMessageHandlers::handleHubInfoRequest(
     uint16_t hostClientId) {
@@ -817,19 +870,25 @@
 DRAM_REGION_FUNCTION void HostLinkBase::sendNanoappTokenDatabaseInfo(
     uint64_t appId, uint32_t tokenDatabaseOffset, size_t tokenDatabaseSize) {
   constexpr size_t kInitialBufferSize = 56;
-  ChreFlatBufferBuilder builder(kInitialBufferSize);
-  uint16_t instanceId;
-  EventLoopManagerSingleton::get()->getEventLoop().findNanoappInstanceIdByAppId(
-      appId, &instanceId);
-  HostProtocolChre::encodeNanoappTokenDatabaseInfo(
-      builder, instanceId, appId, tokenDatabaseOffset, tokenDatabaseSize);
+  struct DatabaseInfoArgs {
+    uint64_t appId;
+    uint32_t tokenDatabaseOffset;
+    size_t tokenDatabaseSize;
+  } args{appId, tokenDatabaseOffset, tokenDatabaseSize};
 
-  if (!getHostCommsManager().send(builder.GetBufferPointer(),
-                                  builder.GetSize())) {
-    LOGE("Failed to send nanoapp token database info for AppID: 0x%016" PRIx64
-         " InstanceID: %" PRIu16,
-         appId, instanceId);
-  }
+  auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) {
+    DatabaseInfoArgs *args = static_cast<DatabaseInfoArgs *>(cookie);
+    uint16_t instanceId;
+    EventLoopManagerSingleton::get()
+        ->getEventLoop()
+        .findNanoappInstanceIdByAppId(args->appId, &instanceId);
+    HostProtocolChre::encodeNanoappTokenDatabaseInfo(
+        builder, instanceId, args->appId, args->tokenDatabaseOffset,
+        args->tokenDatabaseSize);
+  };
+
+  buildAndEnqueueMessage(PendingMessageType::NanoappTokenDatabaseInfo,
+                         kInitialBufferSize, msgBuilder, &args);
 }
 
 DRAM_REGION_FUNCTION void HostLink::flushMessagesSentByNanoapp(
@@ -876,6 +935,16 @@
   LOGE("%s is unsupported", __func__);
 }
 
+DRAM_REGION_FUNCTION void HostMessageHandlers::handleBtSocketOpen(
+    uint16_t /* hostClientId */, uint64_t /* socketId */,
+    const char * /* name */, uint64_t /* endpointId */, uint64_t /* hubId */,
+    uint32_t /* aclConnectionHandle */, uint32_t /* localCid */,
+    uint32_t /* remoteCid */, uint32_t /* psm */, uint32_t /* localMtu */,
+    uint32_t /* remoteMtu */, uint32_t /* localMps */, uint32_t /* remoteMps */,
+    uint32_t /* initialRxCredits */, uint32_t /* initialTxCredits */) {
+  LOGE("BT Socket offload not supported");
+}
+
 DRAM_REGION_FUNCTION void sendAudioRequest() {
   auto msgBuilder = [](ChreFlatBufferBuilder &builder, void * /*cookie*/) {
     HostProtocolChre::encodeLowPowerMicAccessRequest(builder);
diff --git a/platform/tinysys/include/chre/target_platform/system_timer_base.h b/platform/tinysys/include/chre/target_platform/system_timer_base.h
index 2713085..5ecdb05 100644
--- a/platform/tinysys/include/chre/target_platform/system_timer_base.h
+++ b/platform/tinysys/include/chre/target_platform/system_timer_base.h
@@ -25,6 +25,7 @@
 extern "C" {
 #endif
 
+#include "FreeRTOS.h"
 #include "sensorhub/rt_timer.h"
 #include "task.h"
 
@@ -40,7 +41,11 @@
   static constexpr uint32_t kStackDepthWords = 0x200;  // 2K stack size
 
   /** Priority of the callback runner task */
+#ifdef PRI_CHRE_SYS_TIMER
+  static constexpr UBaseType_t kTaskPriority = PRI_CHRE_SYS_TIMER;
+#else
   static constexpr UBaseType_t kTaskPriority = tskIDLE_PRIORITY + 4;
+#endif
 
   /** Name of the callback runner task */
   static constexpr char kTaskName[] = "ChreTimerCbRunner";
@@ -74,4 +79,4 @@
   struct rt_timer rtSystemTimer;
 };
 }  // namespace chre
-#endif  // CHRE_PLATFORM_TINYSYS_SYSTEM_TIMER_BASE_H_
\ No newline at end of file
+#endif  // CHRE_PLATFORM_TINYSYS_SYSTEM_TIMER_BASE_H_
diff --git a/platform/tinysys/memory.cc b/platform/tinysys/memory.cc
index 4bfd5d0..b67c836 100644
--- a/platform/tinysys/memory.cc
+++ b/platform/tinysys/memory.cc
@@ -30,7 +30,6 @@
 #include "encoding.h"
 #include "mt_heap.h"
 #include "resource_req.h"
-#include "sensorhub/heap.h"
 
 #ifdef __cplusplus
 }  // extern "C"
@@ -80,7 +79,7 @@
 }
 
 void *memoryAlloc(size_t size) {
-  void *address = heap_alloc(size);
+  void *address = pvPortMalloc(size);
   if (address == nullptr && size > 0) {
     // Try dram if allocation from sram fails.
     // DramVoteClient tracks the duration of the allocations falling back to
@@ -103,7 +102,7 @@
     vPortDramFree(pointer);
     DramVoteClientSingleton::get()->decrementDramVoteCount();
   } else {
-    heap_free(pointer);
+    vPortFree(pointer);
   }
 }
 }  // namespace chre
diff --git a/platform/tinysys/platform_cache_management.cc b/platform/tinysys/platform_cache_management.cc
index da2ba11..3e12ed1 100644
--- a/platform/tinysys/platform_cache_management.cc
+++ b/platform/tinysys/platform_cache_management.cc
@@ -16,8 +16,8 @@
 
 #include "FreeRTOS.h"
 
-#include "chre/target_platform/platform_cache_management.h"
 #include "chre/platform/shared/nanoapp_loader.h"
+#include "chre/platform/shared/platform_cache_management.h"
 
 #include "dma_api.h"
 
diff --git a/test/simulation/chre_message_hub_test.cc b/test/simulation/chre_message_hub_test.cc
new file mode 100644
index 0000000..7b3a0d3
--- /dev/null
+++ b/test/simulation/chre_message_hub_test.cc
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+#include <cstring>
+#include <optional>
+
+#include "chre/core/event_loop_manager.h"
+#include "chre/util/dynamic_vector.h"
+#include "chre/util/system/message_common.h"
+#include "chre/util/system/message_router.h"
+#include "chre/util/system/napp_permissions.h"
+#include "chre_api/chre/event.h"
+
+#include "pw_allocator/allocator.h"
+#include "pw_allocator/libc_allocator.h"
+#include "pw_allocator/unique_ptr.h"
+#include "pw_function/function.h"
+
+#include "gtest/gtest.h"
+#include "inc/test_util.h"
+#include "test_base.h"
+#include "test_util.h"
+
+namespace chre::message {
+namespace {
+
+constexpr size_t kNumEndpoints = 3;
+constexpr size_t kMessageSize = 5;
+constexpr MessageHubId kOtherMessageHubId = 0xDEADBEEFBEEFDEAD;
+
+EndpointInfo kEndpointInfos[kNumEndpoints] = {
+    EndpointInfo(/* id= */ 1, /* name= */ "endpoint1", /* version= */ 1,
+                 EndpointType::NANOAPP, CHRE_MESSAGE_PERMISSION_NONE),
+    EndpointInfo(/* id= */ 2, /* name= */ "endpoint2", /* version= */ 10,
+                 EndpointType::HOST_NATIVE, CHRE_MESSAGE_PERMISSION_BLE),
+    EndpointInfo(/* id= */ 3, /* name= */ "endpoint3", /* version= */ 100,
+                 EndpointType::GENERIC, CHRE_MESSAGE_PERMISSION_AUDIO)};
+
+//! Base class for MessageHubCallbacks used in tests
+class MessageHubCallbackBase : public MessageRouter::MessageHubCallback {
+ public:
+  void forEachEndpoint(
+      const pw::Function<bool(const EndpointInfo &)> &function) override {
+    for (const EndpointInfo &endpointInfo : kEndpointInfos) {
+      if (function(endpointInfo)) {
+        return;
+      }
+    }
+  }
+
+  std::optional<EndpointInfo> getEndpointInfo(EndpointId endpointId) override {
+    for (const EndpointInfo &endpointInfo : kEndpointInfos) {
+      if (endpointInfo.id == endpointId) {
+        return endpointInfo;
+      }
+    }
+    return std::nullopt;
+  }
+};
+
+//! MessageHubCallback that stores the data passed to onMessageReceived and
+//! onSessionClosed
+class MessageHubCallbackStoreData : public MessageHubCallbackBase {
+ public:
+  MessageHubCallbackStoreData(Message *message, Session *session)
+      : mMessage(message), mSession(session) {}
+
+  bool onMessageReceived(pw::UniquePtr<std::byte[]> &&data, size_t length,
+                         uint32_t messageType, uint32_t messagePermissions,
+                         const Session &session,
+                         bool sentBySessionInitiator) override {
+    if (mMessage != nullptr) {
+      mMessage->sender =
+          sentBySessionInitiator ? session.initiator : session.peer;
+      mMessage->recipient =
+          sentBySessionInitiator ? session.peer : session.initiator;
+      mMessage->sessionId = session.sessionId;
+      mMessage->data = std::move(data);
+      mMessage->length = length;
+      mMessage->messageType = messageType;
+      mMessage->messagePermissions = messagePermissions;
+    }
+    return true;
+  }
+
+  void onSessionClosed(const Session &session) override {
+    if (mSession != nullptr) {
+      *mSession = session;
+    }
+  }
+
+ private:
+  Message *mMessage;
+  Session *mSession;
+};
+
+class ChreMessageHubTest : public TestBase {};
+
+TEST_F(ChreMessageHubTest, MessageRouterNanoappsAreEndpointsToChreMessageHub) {
+  class App : public TestNanoapp {
+   public:
+    App() : TestNanoapp(TestNanoappInfo{.name = "TEST1", .id = 0x1234}) {}
+  };
+
+  uint64_t appId = loadNanoapp(MakeUnique<App>());
+
+  std::optional<EndpointInfo> endpointInfoForApp =
+      MessageRouterSingleton::get()->getEndpointInfo(
+          EventLoopManagerSingleton::get()
+              ->getChreMessageHubManager()
+              .kChreMessageHubId,
+          appId);
+  ASSERT_TRUE(endpointInfoForApp.has_value());
+
+  Nanoapp *nanoapp = getNanoappByAppId(appId);
+  ASSERT_NE(nanoapp, nullptr);
+
+  EXPECT_EQ(endpointInfoForApp->id, nanoapp->getAppId());
+  EXPECT_STREQ(endpointInfoForApp->name, nanoapp->getAppName());
+  EXPECT_EQ(endpointInfoForApp->version, nanoapp->getAppVersion());
+  EXPECT_EQ(endpointInfoForApp->type, EndpointType::NANOAPP);
+  EXPECT_EQ(endpointInfoForApp->requiredPermissions,
+            nanoapp->getAppPermissions());
+}
+
+TEST_F(ChreMessageHubTest,
+       MessageRouterMultipleNanoappsAreEndpointsToChreMessageHub) {
+  class App : public TestNanoapp {
+   public:
+    App() : TestNanoapp(TestNanoappInfo{.name = "TEST1", .id = 0x1234}) {}
+  };
+
+  class App2 : public TestNanoapp {
+   public:
+    App2() : TestNanoapp(TestNanoappInfo{.name = "TEST2", .id = 0x2}) {}
+  };
+
+  uint64_t appId = loadNanoapp(MakeUnique<App>());
+  uint64_t appId2 = loadNanoapp(MakeUnique<App2>());
+  constexpr size_t kNumNanoapps = 2;
+  Nanoapp *nanoapps[kNumNanoapps] = {getNanoappByAppId(appId),
+                                     getNanoappByAppId(appId2)};
+  ASSERT_NE(nanoapps[0], nullptr);
+  ASSERT_NE(nanoapps[1], nullptr);
+
+  DynamicVector<EndpointInfo> endpointInfos;
+  EXPECT_TRUE(MessageRouterSingleton::get()->forEachEndpointOfHub(
+      EventLoopManagerSingleton::get()
+           ->getChreMessageHubManager()
+           .kChreMessageHubId,
+      [&endpointInfos](const EndpointInfo &endpointInfo) {
+        endpointInfos.push_back(endpointInfo);
+        return false;
+      }));
+  EXPECT_EQ(endpointInfos.size(), 2);
+
+  // Endpoint information should be nanoapp information
+  for (size_t i = 0; i < kNumNanoapps; ++i) {
+    EXPECT_EQ(endpointInfos[i].id, nanoapps[i]->getAppId());
+    EXPECT_STREQ(endpointInfos[i].name, nanoapps[i]->getAppName());
+    EXPECT_EQ(endpointInfos[i].version, nanoapps[i]->getAppVersion());
+    EXPECT_EQ(endpointInfos[i].type, EndpointType::NANOAPP);
+    EXPECT_EQ(endpointInfos[i].requiredPermissions,
+              nanoapps[i]->getAppPermissions());
+  }
+}
+
+//! Nanoapp used to test sending messages from a generic endpoint to a nanoapp
+class MessageTestApp : public TestNanoapp {
+ public:
+  MessageTestApp(std::mutex &mutex, std::condition_variable &condVar,
+                 bool &messageReceivedAndValidated, bool &sessionClosed,
+                 const TestNanoappInfo &info)
+      : TestNanoapp(info),
+        mMutex(mutex),
+        mCondVar(condVar),
+        mMessageReceivedAndValidated(messageReceivedAndValidated),
+        mSessionClosed(sessionClosed) {}
+
+  void handleEvent(uint32_t, uint16_t eventType,
+                   const void *eventData) override {
+    switch (eventType) {
+      case CHRE_EVENT_MESSAGE_FROM_ENDPOINT: {
+        {
+          std::unique_lock<std::mutex> lock(mMutex);
+          auto *message =
+              static_cast<const struct chreMessageFromEndpointData *>(
+                  eventData);
+          EXPECT_EQ(message->messageType, 1);
+          EXPECT_EQ(message->messagePermissions, 0);
+          EXPECT_EQ(message->messageSize, kMessageSize);
+
+          auto *messageData = static_cast<const std::byte *>(message->message);
+          for (size_t i = 0; i < kMessageSize; ++i) {
+            EXPECT_EQ(messageData[i], static_cast<std::byte>(i + 1));
+          }
+          mMessageReceivedAndValidated = true;
+        }
+        mCondVar.notify_one();
+        break;
+      }
+      case CHRE_EVENT_ENDPOINT_SESSION_CLOSED: {
+        {
+          std::unique_lock<std::mutex> lock(mMutex);
+          auto *session =
+              static_cast<const struct chreEndpointSessionClosedData *>(
+                  eventData);
+          EXPECT_EQ(session->hubId, kOtherMessageHubId);
+          EXPECT_EQ(session->endpointId, kEndpointInfos[0].id);
+          mSessionClosed = true;
+        }
+        mCondVar.notify_one();
+        break;
+      }
+      default: {
+        break;
+      }
+    }
+  }
+
+  std::mutex &mMutex;
+  std::condition_variable &mCondVar;
+  bool &mMessageReceivedAndValidated;
+  bool &mSessionClosed;
+};
+
+TEST_F(ChreMessageHubTest, MessageRouterSendMessageToNanoapp) {
+  constexpr uint64_t kNanoappId = 0x1234;
+  std::mutex mutex;
+  std::condition_variable condVar;
+  bool messageReceivedAndValidated = false;
+  bool sessionClosed = false;
+
+  pw::allocator::LibCAllocator allocator = pw::allocator::GetLibCAllocator();
+  pw::UniquePtr<std::byte[]> messageData =
+      allocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < kMessageSize; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+
+  // Load the nanoapp
+  uint64_t appId = loadNanoapp(MakeUnique<MessageTestApp>(
+      mutex, condVar, messageReceivedAndValidated, sessionClosed,
+      TestNanoappInfo{.name = "TEST1", .id = kNanoappId}));
+
+  // Create the other hub
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub =
+      MessageRouterSingleton::get()->registerMessageHub(
+          "OTHER_TEST_HUB", kOtherMessageHubId, callback);
+  ASSERT_TRUE(messageHub.has_value());
+
+  // Open the session from the other hub:1 to the nanoapp
+  SessionId sessionId =
+      messageHub->openSession(kEndpointInfos[0].id,
+                              EventLoopManagerSingleton::get()
+                                   ->getChreMessageHubManager()
+                                   .kChreMessageHubId,
+                              kNanoappId);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Send the message to the nanoapp
+  std::unique_lock<std::mutex> lock(mutex);
+  ASSERT_TRUE(messageHub->sendMessage(std::move(messageData), kMessageSize,
+                                      /* messageType= */ 1,
+                                      /* messagePermissions= */ 0, sessionId));
+  condVar.wait(lock);
+  EXPECT_TRUE(messageReceivedAndValidated);
+
+  // Close the session
+  EXPECT_TRUE(messageHub->closeSession(sessionId));
+  condVar.wait(lock);
+  EXPECT_TRUE(sessionClosed);
+}
+
+class MessagePermissionTestApp : public MessageTestApp {
+ public:
+  MessagePermissionTestApp(std::mutex &mutex, std::condition_variable &condVar,
+                           bool &messageReceivedAndValidated,
+                           bool &sessionClosed, const TestNanoappInfo &info)
+      : MessageTestApp(mutex, condVar, messageReceivedAndValidated,
+                       sessionClosed, info) {}
+};
+
+TEST_F(ChreMessageHubTest, MessageRouterSendMessageToNanoappPermissionFailure) {
+  CREATE_CHRE_TEST_EVENT(TRIGGER_COND_VAR, 0);
+
+  constexpr uint64_t kNanoappId = 0x1234;
+  std::mutex mutex;
+  std::condition_variable condVar;
+  bool messageReceivedAndValidated = false;
+  bool sessionClosed = false;
+
+  pw::allocator::LibCAllocator allocator = pw::allocator::GetLibCAllocator();
+  pw::UniquePtr<std::byte[]> messageData =
+      allocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < kMessageSize; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+
+  // Load the nanoapp
+  uint64_t appId = loadNanoapp(MakeUnique<MessagePermissionTestApp>(
+      mutex, condVar, messageReceivedAndValidated, sessionClosed,
+      TestNanoappInfo{
+          .name = "TEST1", .id = kNanoappId, .perms = CHRE_PERMS_BLE}));
+
+  // Create the other hub
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub =
+      MessageRouterSingleton::get()->registerMessageHub(
+          "OTHER_TEST_HUB", kOtherMessageHubId, callback);
+  ASSERT_TRUE(messageHub.has_value());
+
+  // Open the session from the other hub:1 to the nanoapp
+  SessionId sessionId =
+      messageHub->openSession(kEndpointInfos[0].id,
+                              EventLoopManagerSingleton::get()
+                                   ->getChreMessageHubManager()
+                                   .kChreMessageHubId,
+                              kNanoappId);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Send the message to the nanoapp
+  std::unique_lock<std::mutex> lock(mutex);
+  ASSERT_TRUE(messageHub->sendMessage(
+      std::move(messageData), kMessageSize,
+      /* messageType= */ 1,
+      /* messagePermissions= */ CHRE_PERMS_AUDIO | CHRE_PERMS_GNSS, sessionId));
+
+  // Send the trigger cond var event, which will be handled after the
+  // CHRE message from endpoint event (if it is sent erroneously). If the
+  // message event is not sent, this event will unlock the condition variable.
+  // If the message event is sent, the condition variable will be unlocked
+  // after the message event is processed, setting the
+  // messageReceivedAndValidated variable to true, which will fail the test.
+  sendEventToNanoapp(appId, TRIGGER_COND_VAR);
+  condVar.wait(lock);
+  EXPECT_FALSE(messageReceivedAndValidated);
+  EXPECT_TRUE(sessionClosed);
+}
+
+}  // namespace
+}  // namespace chre::message
diff --git a/test/simulation/inc/test_base.h b/test/simulation/inc/test_base.h
index 6638cd6..2b5dd0a 100644
--- a/test/simulation/inc/test_base.h
+++ b/test/simulation/inc/test_base.h
@@ -25,6 +25,7 @@
 #include "chre/core/nanoapp.h"
 #include "chre/platform/system_time.h"
 #include "chre/platform/system_timer.h"
+#include "chre/util/system/message_router.h"
 #include "chre/util/time.h"
 #include "test_event_queue.h"
 
@@ -126,6 +127,7 @@
   MemberInitLogger mInitLogger;
   std::thread mChreThread;
   SystemTimer mSystemTimer;
+  message::MessageRouter::MessageHub mChreMessageHub;
 };
 
 }  // namespace chre
diff --git a/test/simulation/inc/test_event_queue.h b/test/simulation/inc/test_event_queue.h
index 03c1364..0b22f7a 100644
--- a/test/simulation/inc/test_event_queue.h
+++ b/test/simulation/inc/test_event_queue.h
@@ -22,10 +22,10 @@
 #include <cinttypes>
 
 #include "chre/platform/memory.h"
-#include "chre/util/fixed_size_blocking_queue.h"
 #include "chre/util/memory.h"
 #include "chre/util/non_copyable.h"
 #include "chre/util/singleton.h"
+#include "chre/util/system/fixed_size_blocking_queue.h"
 #include "test_event.h"
 
 namespace chre {
diff --git a/test/simulation/test_base.cc b/test/simulation/test_base.cc
index 5c0329e..985295f 100644
--- a/test/simulation/test_base.cc
+++ b/test/simulation/test_base.cc
@@ -17,18 +17,33 @@
 #include "test_base.h"
 
 #include <gtest/gtest.h>
+#include <pw_containers/vector.h>
 
 #include "chre/core/event_loop_manager.h"
 #include "chre/core/init.h"
 #include "chre/platform/linux/platform_log.h"
 #include "chre/platform/linux/task_util/task_manager.h"
+#include "chre/util/system/message_router.h"
 #include "chre/util/time.h"
 #include "chre_api/chre/version.h"
 #include "inc/test_util.h"
 #include "test_util.h"
 
+using ::chre::message::MessageRouter;
+using ::chre::message::MessageRouterSingleton;
+using ::chre::message::Session;
+
 namespace chre {
 
+namespace {
+
+constexpr size_t kMaxMessageHubs = 2;
+constexpr size_t kMaxSessions = 25;
+pw::Vector<MessageRouter::MessageHubRecord, kMaxMessageHubs> gMessageHubs;
+pw::Vector<Session, kMaxSessions> gSessions;
+
+}  // anonymous namespace
+
 /**
  * This base class initializes and runs the event loop.
  *
@@ -44,14 +59,10 @@
  * this test.
  */
 void TestBase::SetUp() {
-  // TODO(b/346903946): remove these extra prints once init failure is resolved
-  printf("SetUp(): log\n");
+  MessageRouterSingleton::init(gMessageHubs, gSessions);
   chre::PlatformLogSingleton::init();
-  printf("SetUp(): TaskManager\n");
   TaskManagerSingleton::init();
-  printf("SetUp(): TestEventQueue\n");
   TestEventQueueSingleton::init();
-  printf("SetUp(): CHRE\n");
   chre::init();
   EventLoopManagerSingleton::get()->lateInit();
 
@@ -67,7 +78,6 @@
   ASSERT_TRUE(mSystemTimer.init());
   ASSERT_TRUE(mSystemTimer.set(callback, nullptr /*data*/,
                                Nanoseconds(getTimeoutNs())));
-  printf("SetUp() complete\n");
 }
 
 void TestBase::TearDown() {
@@ -83,6 +93,7 @@
   deleteNanoappInfos();
   unregisterAllTestNanoapps();
   chre::PlatformLogSingleton::deinit();
+  MessageRouterSingleton::deinit();
 }
 
 TEST_F(TestBase, CanLoadAndStartSingleNanoapp) {
diff --git a/tools/common.sh b/tools/common.sh
new file mode 100644
index 0000000..65d68cb
--- /dev/null
+++ b/tools/common.sh
@@ -0,0 +1,105 @@
+trap 'onExit $?' EXIT
+
+onExit() {
+  if [ "$1" != "0" ]; then
+    echo -e "\e[31m"
+    echo " ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ "
+    echo "▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌"
+    echo "▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌"
+    echo "▐░▌          ▐░▌       ▐░▌▐░▌       ▐░▌▐░▌       ▐░▌▐░▌       ▐░▌"
+    echo "▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌▐░█▄▄▄▄▄▄▄█░▌"
+    echo "▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌"
+    echo "▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀█░█▀▀ ▐░█▀▀▀▀█░█▀▀ ▐░▌       ▐░▌▐░█▀▀▀▀█░█▀▀ "
+    echo "▐░▌          ▐░▌     ▐░▌  ▐░▌     ▐░▌  ▐░▌       ▐░▌▐░▌     ▐░▌  "
+    echo "▐░█▄▄▄▄▄▄▄▄▄ ▐░▌      ▐░▌ ▐░▌      ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░▌      ▐░▌ "
+    echo "▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌"
+    echo " ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀ "
+    echo "                                                                 "
+    echo -e "\e[0m"
+  fi
+}
+
+onSuccess() {
+  echo -e "\e[92m\n"
+  echo ""
+  echo "   ██████  ██   ██ ██ "
+  echo "  ██    ██ ██  ██  ██ "
+  echo "  ██    ██ █████   ██ "
+  echo "  ██    ██ ██  ██     "
+  echo "   ██████  ██   ██ ██ "
+  echo ""
+  echo -e "\e[0m"
+}
+
+onWarning() {
+  echo -e "\e[33m\n"
+  echo " █████   ███   █████   █████████   ███████████   ██████   █████ █████ ██████   █████   █████████   "
+  echo " ░░███   ░███  ░░███   ███░░░░░███ ░░███░░░░░███ ░░██████ ░░███ ░░███ ░░██████ ░░███   ███░░░░░███ "
+  echo "  ░███   ░███   ░███  ░███    ░███  ░███    ░███  ░███░███ ░███  ░███  ░███░███ ░███  ███     ░░░  "
+  echo "  ░███   ░███   ░███  ░███████████  ░██████████   ░███░░███░███  ░███  ░███░░███░███ ░███          "
+  echo "  ░░███  █████  ███   ░███░░░░░███  ░███░░░░░███  ░███ ░░██████  ░███  ░███ ░░██████ ░███    █████ "
+  echo "   ░░░█████░█████░    ░███    ░███  ░███    ░███  ░███  ░░█████  ░███  ░███  ░░█████ ░░███  ░░███  "
+  echo "     ░░███ ░░███      █████   █████ █████   █████ █████  ░░█████ █████ █████  ░░█████ ░░█████████  "
+  echo "      ░░░   ░░░      ░░░░░   ░░░░░ ░░░░░   ░░░░░ ░░░░░    ░░░░░ ░░░░░ ░░░░░    ░░░░░   ░░░░░░░░░   "
+  echo -e "\e[0m"
+}
+
+doRemount() {
+  $ADB root
+  sleep 3
+  set +e
+  REMOUNT_OUTPUT="$($ADB remount -R 2>&1)"
+  REMOUNT_OUTPUT="${REMOUNT_OUTPUT,,}"
+  REMOUNT_RESULT=$?
+  echo "$REMOUNT_OUTPUT"
+  if [[ $REMOUNT_RESULT != 0 || "$REMOUNT_OUTPUT" == *"remount failed"* ]]; then
+    exit 1
+  fi
+  set -e
+  if [[ "$REMOUNT_OUTPUT" == *"rebooting"* ]]
+  then
+    echo -e "\e[93m"
+    echo "██████╗ ███████╗██████╗  ██████╗  ██████╗ ████████╗██╗███╗   ██╗ ██████╗ "
+    echo "██╔══██╗██╔════╝██╔══██╗██╔═══██╗██╔═══██╗╚══██╔══╝██║████╗  ██║██╔════╝ "
+    echo "██████╔╝█████╗  ██████╔╝██║   ██║██║   ██║   ██║   ██║██╔██╗ ██║██║  ███╗"
+    echo "██╔══██╗██╔══╝  ██╔══██╗██║   ██║██║   ██║   ██║   ██║██║╚██╗██║██║   ██║"
+    echo "██║  ██║███████╗██████╔╝╚██████╔╝╚██████╔╝   ██║   ██║██║ ╚████║╚██████╔╝"
+    echo "╚═╝  ╚═╝╚══════╝╚═════╝  ╚═════╝  ╚═════╝    ╚═╝   ╚═╝╚═╝  ╚═══╝ ╚═════╝ "
+    echo "                                                                         "
+    echo -e "\e[0m"
+    sleep 5
+    $ADB wait-for-device root && $ADB wait-for-device remount
+  fi
+}
+
+connectedProduct() {
+  echo "$($ADB shell getprop ro.build.product)"
+}
+
+checkConnectedProduct() {
+  checkDeviceRev
+
+  # Make sure connected device matches $TARGET_PRODUCT
+  CONNECTED_PRODUCT="$(connectedProduct)"
+  if [ "$TARGET_PRODUCT" != "$CONNECTED_PRODUCT" ]
+  then
+    echo "ERROR: Connected device ($CONNECTED_PRODUCT) does not match TARGET_PRODUCT. Please lunch $CONNECTED_PRODUCT first"
+    exit 1
+  fi
+}
+
+checkDeviceRev() {
+  set +e
+  GETPROP_OUTPUT="$($ADB shell getprop ro.boot.hardware.revision 2>&1)"
+  GETPROP_RESULT=$?
+  if [ $GETPROP_RESULT -ne 0 ]
+  then
+    echo "Cannot check device status via adb, please check device connection"
+    exit 1
+  elif [[ "$GETPROP_OUTPUT" =~ ^([DP]VT|MP).* ]]
+  then
+    echo "Device must be EVT or earlier for CHRE development"
+    exit 1
+  fi
+  set -e
+}
diff --git a/tools/find_improper_target_platform_files.py b/tools/find_improper_target_platform_files.py
new file mode 100644
index 0000000..6b6bbd1
--- /dev/null
+++ b/tools/find_improper_target_platform_files.py
@@ -0,0 +1,101 @@
+"""Finds header files in target_platform directories that should not be
+
+Some platform implementations have improperly used target_platform when they
+should instead use platform/<platform_name>. This script helps identify those
+files.
+
+Example usage:
+
+  python find_improper_target_platform_files.py -p ../platform -c ../platform/include
+"""
+import argparse
+import os
+import sys
+
+def find_target_platform_files(directories):
+  """
+  Lists all files recursively in the given directories and filters to those with
+  "target_platform" in their path.
+
+  Args:
+    directories: A list of directories to search.
+
+  Returns:
+    A list of file paths that contain "target_platform" in their path.
+  """
+  target_files = []
+  for directory in directories:
+    for root, _, files in os.walk(directory):
+      for file in files:
+        file_path = os.path.join(root, file)
+        if "target_platform" in file_path:
+          target_files.append(file_path)
+  return target_files
+
+def find_unincluded_files(target_files, include_directories):
+  """
+  Flags target_platform files that are not included in any C/C++ #include
+  statements within files in the include_directories.
+
+  Args:
+    target_files: A list of file paths to check.
+    include_directories: A list of directories containing files with
+                         #include statements.
+
+  Returns:
+    A list of target_platform file paths that are not included.
+  """
+  unincluded_files = []
+  included_files = set()
+
+  for directory in include_directories:
+    for root, _, files in os.walk(directory):
+      for file in files:
+        file_path = os.path.join(root, file)
+        try:
+          with open(file_path, "r") as f:
+            for line in f:
+              if line.startswith("#include") and "target_platform" in line:
+                # Extract the included file name, accounting for variations in
+                # include syntax (e.g., quotes vs. angle brackets)
+                included_file = line.split()[1].strip('"<>')
+                included_files.add(included_file)
+        except UnicodeDecodeError:
+          # Skip non-text files
+          continue
+
+  print(f"target_platform files referenced in {include_directories}:")
+  for file in sorted(included_files):
+    print(f"  {file}")
+
+
+  for target_file in target_files:
+    target_file_name = "target_platform/" + os.path.basename(target_file)
+    found = False
+    for included_file in included_files:
+      if included_file.endswith(target_file_name):
+        found = True
+        break
+    if not found:
+      unincluded_files.append(target_file)
+
+  return unincluded_files
+
+if __name__ == "__main__":
+  parser = argparse.ArgumentParser(description="Find files in target_platform directories which are not included from common code.")
+  parser.add_argument("-p", "--platform", nargs="+", required=True, help="Directories containing target_platform subdirectories")
+  parser.add_argument("-c", "--common", nargs="+", required=True, help="Directories containing facades to platform code - all target_platform headers must appear in an #include statement in a file here")
+  args = parser.parse_args()
+
+  target_directories = args.platform
+  include_directories = args.common
+
+  target_files = find_target_platform_files(target_directories)
+  unincluded_files = find_unincluded_files(target_files, include_directories)
+
+  if len(unincluded_files):
+    print("\nThe following target_platform files do not appear in the list above:")
+    for file in sorted(unincluded_files):
+      print(f"  {file}")
+  else:
+    print("All target_platform files are included.")
diff --git a/tools/tinysys_nanoapp_signer.py b/tools/tinysys_nanoapp_signer.py
new file mode 100755
index 0000000..9d57f6e
--- /dev/null
+++ b/tools/tinysys_nanoapp_signer.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python
+
+#
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""A script to sign nanoapps for testing purpose on tinysys platforms."""
+
+import argparse
+import ctypes
+import hashlib
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.hazmat.primitives.asymmetric import utils
+
+
+class HeaderInfo(ctypes.LittleEndianStructure):
+  _fields_ = [
+      ("magic_number", ctypes.c_uint32),
+      ("header_version", ctypes.c_uint32),
+      ("rollback_info", ctypes.c_uint32),
+      ("binary_length", ctypes.c_uint32),
+      ("flags", ctypes.c_uint64 * 2),
+      ("binary_sha256", ctypes.c_uint8 * 32),
+      ("reserved_chip_id", ctypes.c_uint8 * 32),
+      ("reserved_auth_config", ctypes.c_uint8 * 256),
+      ("reserved_image_config", ctypes.c_uint8 * 256),
+  ]
+
+
+def read_private_key(key_file, password):
+  with open(key_file, "rb") as file:
+    key_bytes = file.read()
+    try:
+      return serialization.load_der_private_key(key_bytes, password=password)
+    except ValueError as e:
+      pass  # Not a DER private key
+
+    try:
+      return serialization.load_pem_private_key(key_bytes, password=password)
+    except ValueError:
+      pass  # Not a PEM private key
+    raise ValueError("Unable to parse the key file as DER or PEM")
+
+
+def main():
+
+  parser = argparse.ArgumentParser(
+      description="Sign a binary to be authenticated on tinysys platforms"
+  )
+  parser.add_argument(
+      "private_key_file",
+      help="The private key (DER or PEM format) used to sign the binary",
+  )
+  parser.add_argument(
+      "-p",
+      "--password",
+      type=str,
+      help="Optional password encrypting the private key",
+  )
+  parser.add_argument(
+      "nanoapp", help="The name of the nanoapp binary file to be signed"
+  )
+  parser.add_argument(
+      "output_path", help="The path where the signed binary should be stored"
+  )
+  args = parser.parse_args()
+
+  # Load the binary file.
+  binary_data = None
+  with open(args.nanoapp, "rb") as binary_file:
+    binary_data = binary_file.read()
+
+  # Load ECDSA private key.
+  password = args.password.encode() if args.password else None
+  private_key = read_private_key(args.private_key_file, password)
+
+  # Generate a zero-filled header.
+  header = bytearray(0x1000)
+
+  # Fill the public key.
+  public_key_numbers = private_key.public_key().public_numbers()
+  header[0x200:0x220] = public_key_numbers.x.to_bytes(32, "big")
+  header[0x220:0x240] = public_key_numbers.y.to_bytes(32, "big")
+
+  # Fill header_info.
+  sha256_hasher = hashlib.sha256()
+  sha256_hasher.update(binary_data)
+  header_info = HeaderInfo(
+      magic_number=0x45524843,
+      header_version=1,
+      binary_length=len(binary_data),
+      binary_sha256=(ctypes.c_uint8 * 32)(*sha256_hasher.digest()),
+  )
+  header_info_bytes = bytes(header_info)
+  header[0x400 : 0x400 + len(header_info_bytes)] = header_info_bytes
+
+  # Generate the signature.
+  signature = private_key.sign(header[0x200:], ec.ECDSA(hashes.SHA256()))
+  r, s = utils.decode_dss_signature(signature)
+  r_bytes = r.to_bytes(32, "big")
+  s_bytes = s.to_bytes(32, "big")
+  header[:32] = r_bytes
+  header[32:64] = s_bytes
+
+  with open(f"{args.output_path}/{args.nanoapp}", "wb") as output:
+    output.write(header)
+    output.write(binary_data)
+
+
+if __name__ == "__main__":
+  main()
diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt
new file mode 100644
index 0000000..3d4554f
--- /dev/null
+++ b/util/CMakeLists.txt
@@ -0,0 +1,201 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_library(chre.util STATIC
+  HEADERS
+    include/chre/util/always_false.h
+    include/chre/util/array_queue.h
+    include/chre/util/array_queue_impl.h
+    include/chre/util/blocking_segmented_queue.h
+    include/chre/util/buffer.h
+    include/chre/util/buffer_base.h
+    include/chre/util/conditional_lock_guard.h
+    include/chre/util/conditional_lock_guard_impl.h
+    include/chre/util/container_support.h
+    include/chre/util/copyable_fixed_size_vector.h
+    include/chre/util/duplicate_message_detector.h
+    include/chre/util/dynamic_vector.h
+    include/chre/util/dynamic_vector_base.h
+    include/chre/util/dynamic_vector_impl.h
+    include/chre/util/enum.h
+    include/chre/util/fixed_size_vector.h
+    include/chre/util/fixed_size_vector_impl.h
+    include/chre/util/fragmentation_manager.h
+    include/chre/util/fragmentation_manager_impl.h
+    include/chre/util/hash.h
+    include/chre/util/heap.h
+    include/chre/util/heap_impl.h
+    include/chre/util/host/assert.h
+    include/chre/util/intrusive_list.h
+    include/chre/util/intrusive_list_base.h
+    include/chre/util/intrusive_list_impl.h
+    include/chre/util/lock_guard.h
+    include/chre/util/lock_guard_impl.h
+    include/chre/util/log_common.h
+    include/chre/util/macros.h
+    include/chre/util/memory.h
+    include/chre/util/memory_impl.h
+    include/chre/util/memory_pool.h
+    include/chre/util/memory_pool_impl.h
+    include/chre/util/nested_data_ptr.h
+    include/chre/util/non_copyable.h
+    include/chre/util/optional.h
+    include/chre/util/optional_impl.h
+    include/chre/util/priority_queue.h
+    include/chre/util/priority_queue_impl.h
+    include/chre/util/raw_storage.h
+    include/chre/util/scope_timer.h
+    include/chre/util/scope_timer_impl.h
+    include/chre/util/segmented_queue.h
+    include/chre/util/segmented_queue_impl.h
+    include/chre/util/singleton.h
+    include/chre/util/singleton_impl.h
+    include/chre/util/throttle.h
+    include/chre/util/time.h
+    include/chre/util/time_impl.h
+    include/chre/util/toolchain.h
+    include/chre/util/unique_ptr.h
+    include/chre/util/unique_ptr_impl.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+    chre.platform.assert
+    chre.platform.memory
+    chre.platform.system_time
+    chre.util.nanoapp
+    chre.variant.config
+
+    # TODO(b/376249597) Move the synchronized expandable memory pool, fixed
+    # size blocking queue, and synchronized memory pool to chre.util.system.
+    chre.platform.condition_variable
+    chre.platform.mutex
+  SOURCES
+    buffer_base.cc
+    duplicate_message_detector.cc
+    dynamic_vector_base.cc
+    hash.cc
+    intrusive_list_base.cc
+)
+
+pw_add_library(chre.util.flatbuffers INTERFACE
+  HEADERS
+    include/chre/util/flatbuffers/helpers.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.util
+    chre.variant.config
+    chre_third_party.flatbuffers
+)
+
+pw_add_library(chre.util.host INTERFACE
+  HEADERS
+    include/chre/util/host/assert.h
+  PUBLIC_INCLUDES
+    include
+)
+
+
+pw_add_library(chre.util.nanoapp STATIC
+  HEADERS
+    include/chre/util/nanoapp/app_id.h
+    include/chre/util/nanoapp/assert.h
+    include/chre/util/nanoapp/audio.h
+    include/chre/util/nanoapp/ble.h
+    include/chre/util/nanoapp/callbacks.h
+    include/chre/util/nanoapp/debug.h
+    include/chre/util/nanoapp/log.h
+    include/chre/util/nanoapp/math.h
+    include/chre/util/nanoapp/string.h
+    include/chre/util/nanoapp/tagged_log.h
+    include/chre/util/nanoapp/wifi.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+    chre.platform.assert
+    chre.platform.log
+    chre.util
+    chre.variant.config
+    pw_tokenizer
+  SOURCES
+    nanoapp/audio.cc
+    nanoapp/ble.cc
+    nanoapp/callbacks.cc
+    nanoapp/debug.cc
+    nanoapp/string.cc
+    nanoapp/wifi.cc
+)
+
+pw_add_library(chre.util.pigweed STATIC
+  HEADERS
+    include/chre/util/pigweed/chre_channel_output.h
+    include/chre/util/pigweed/permission.h
+    include/chre/util/pigweed/rpc_client.h
+    include/chre/util/pigweed/rpc_common.h
+    include/chre/util/pigweed/rpc_helper.h
+    include/chre/util/pigweed/rpc_server.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+    chre.util
+    chre.util.nanoapp
+    chre.variant.config
+    pw_rpc.client
+    pw_rpc.common
+    pw_rpc.server
+    pw_span
+  SOURCES
+    pigweed/chre_channel_output.cc
+    pigweed/rpc_client.cc
+    pigweed/rpc_helper.cc
+    pigweed/rpc_server.cc
+  PRIVATE_DEPS
+    pw_status
+)
+
+pw_add_library(chre.util.system STATIC
+  HEADERS
+    include/chre/util/system/atomic_spsc_queue.h
+    include/chre/util/system/ble_util.h
+    include/chre/util/system/debug_dump.h
+    include/chre/util/system/event_callbacks.h
+    include/chre/util/system/fixed_size_blocking_queue.h
+    include/chre/util/system/fixed_size_blocking_queue_impl.h
+    include/chre/util/system/message_common.h
+    include/chre/util/system/message_router.h
+    include/chre/util/system/napp_header_utils.h
+    include/chre/util/system/napp_permissions.h
+    include/chre/util/system/ref_base.h
+    include/chre/util/system/shared_ptr.h
+    include/chre/util/system/shared_ptr_impl.h
+    include/chre/util/system/synchronized_expandable_memory_pool.h
+    include/chre/util/system/synchronized_expandable_memory_pool_impl.h
+    include/chre/util/system/synchronized_memory_pool.h
+    include/chre/util/system/synchronized_memory_pool_impl.h
+    include/chre/util/system/stats_container.h
+    include/chre/util/system/transaction_manager.h
+    include/chre/util/system/transaction_manager_impl.h
+    include/chre/util/system/wifi_util.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    chre.chre_api
+    chre.core
+    chre.platform.assert
+    chre.platform.atomic
+    chre.platform.memory
+    chre.platform.mutex
+    chre.platform.system_time
+    chre.util
+    chre.variant.config
+    pw_allocator.deallocator
+  SOURCES
+    system/ble_util.cc
+    system/event_callbacks.cc
+    system/debug_dump.cc
+    system/message_router.cc
+  PRIVATE_DEPS
+    chre.platform.log
+)
diff --git a/util/include/chre/util/blocking_segmented_queue.h b/util/include/chre/util/blocking_segmented_queue.h
index 92d8dea..5ac45e2 100644
--- a/util/include/chre/util/blocking_segmented_queue.h
+++ b/util/include/chre/util/blocking_segmented_queue.h
@@ -17,8 +17,8 @@
 #ifndef CHRE_UTIL_BLOCKING_SEGMENTED_QUEUE_H_
 #define CHRE_UTIL_BLOCKING_SEGMENTED_QUEUE_H_
 
-#include "chre/util/fixed_size_blocking_queue.h"
 #include "chre/util/segmented_queue.h"
+#include "chre/util/system/fixed_size_blocking_queue.h"
 namespace chre {
 /**
  * Similar but memory efficient version of chre::FixedSizeBlockingQueue.
diff --git a/util/include/chre/util/intrusive_list.h b/util/include/chre/util/intrusive_list.h
index 4a394d1..a74fa9c 100644
--- a/util/include/chre/util/intrusive_list.h
+++ b/util/include/chre/util/intrusive_list.h
@@ -110,7 +110,7 @@
     using Node = ::chre::intrusive_list_internal::Node;
 
    public:
-    Iterator(Node *node) : mNode(node){};
+    Iterator(Node *node) : mNode(node) {}
 
     ListNode<ElementType> &operator*() const {
       return *reinterpret_cast<ListNode<ElementType> *>(mNode);
diff --git a/util/include/chre/util/log_common.h b/util/include/chre/util/log_common.h
index 3032650..e00442a 100644
--- a/util/include/chre/util/log_common.h
+++ b/util/include/chre/util/log_common.h
@@ -61,6 +61,6 @@
 /**
  * Logs an out of memory error with file and line number.
  */
-#define LOG_OOM() LOGE("OOM at %s:%d", CHRE_FILENAME, __LINE__)
+#define LOG_OOM() LOGE("OOM at " CHRE_FILENAME ":%d", __LINE__)
 
 #endif  // CHRE_UTIL_LOG_COMMON_H_
diff --git a/util/include/chre/util/memory_impl.h b/util/include/chre/util/memory_impl.h
index 96f08ca..fc5e6e2 100644
--- a/util/include/chre/util/memory_impl.h
+++ b/util/include/chre/util/memory_impl.h
@@ -18,6 +18,7 @@
 #define CHRE_UTIL_MEMORY_IMPL_H_
 
 // IWYU pragma: private
+#include <cstddef>
 #include <cstring>
 #include <new>
 #include <type_traits>
diff --git a/util/include/chre/util/nanoapp/assert.h b/util/include/chre/util/nanoapp/assert.h
index ddc7afe..f4dbb85 100644
--- a/util/include/chre/util/nanoapp/assert.h
+++ b/util/include/chre/util/nanoapp/assert.h
@@ -37,7 +37,7 @@
 #define CHRE_ASSERT(condition)                                       \
   do {                                                               \
     if (!(condition)) {                                              \
-      chreLog(CHRE_LOG_ERROR, "CHRE_ASSERT at %s:%d", CHRE_FILENAME, \
+      chreLog(CHRE_LOG_ERROR, "CHRE_ASSERT at " CHRE_FILENAME ":%d", \
               __LINE__);                                             \
       chreAbort(UINT32_MAX);                                         \
     }                                                                \
diff --git a/util/include/chre/util/nanoapp/ble.h b/util/include/chre/util/nanoapp/ble.h
index fade65a..475d669 100644
--- a/util/include/chre/util/nanoapp/ble.h
+++ b/util/include/chre/util/nanoapp/ble.h
@@ -48,6 +48,17 @@
 /** Length of Google manufacturer data filter. */
 constexpr uint16_t kGoogleManufactureDataLength = 4;
 
+/**
+ * The public address of the known (bonded) BLE advertiser in big endian byte
+ * order. Change this address to the public identity address of the advertiser
+ * in the test.
+ *
+ * Example: To filter on the address (01:02:03:AB:CD:EF), use
+ * {0x01:0x02:0x03:0xAB:0xCD:0xEF}.
+ */
+constexpr uint8_t kBroadcasterAddress[CHRE_BLE_ADDRESS_LEN] = {
+    0x01, 0x02, 0x03, 0xAB, 0xCD, 0xEF};
+
 /** The Google manufacturer ID followed by some data. */
 constexpr uint8_t kGoogleManufactureData[kGoogleManufactureDataLength] = {
     0xE0, 0x00, 0xAA, 0xFE};
@@ -58,10 +69,14 @@
 
 /** The number of generic filters (equal to the number of known beacons). */
 constexpr uint8_t kNumScanFilters = 2;
-
 /** The number of manufacturer data filters. */
 constexpr uint8_t kNumManufacturerDataFilters = 1;
 
+/**
+ * The number of broadcaster address filters (equal to the number of known
+ * public advertiser addresses).
+ */
+constexpr uint8_t kNumBroadcasterFilters = 1;
 }  // namespace ble_constants
 
 /**
@@ -118,6 +133,24 @@
                                      chreBleGenericFilter *genericFilters,
                                      struct chreBleScanFilterV1_9 &filter);
 
+/**
+ * Creates a chreBleScanFilter that filters for the Google eddystone UUID,
+ * the Google nearby fastpair UUID, public identity address of a bonded device,
+ * and a RSSI threshold of kRssiThreshold.
+ *
+ * @param filter                   (out) the output filter.
+ * @param broadcasterFilters       (out) the output broadcaster address filters
+ * array.
+ * @param numBroadcasterFilters    the size of the broadcaster address filters
+ * array. must be >= kNumBroadcasterFilters.
+ *
+ * @return true                    the operation was successful
+ * @return false                   the operation was not successful
+ */
+bool createBleScanFilterForAdvertiser(
+    struct chreBleScanFilterV1_9 &filter,
+    chreBleBroadcasterAddressFilter *broadcasterFilters,
+    uint8_t numBroadcasterFilters);
 }  // namespace chre
 
 #endif  // CHRE_UTIL_NANOAPP_BLE_H_
diff --git a/util/include/chre/util/optional.h b/util/include/chre/util/optional.h
index d3fc092..24394cc 100644
--- a/util/include/chre/util/optional.h
+++ b/util/include/chre/util/optional.h
@@ -36,7 +36,7 @@
   /**
    * Default constructs the optional object with no initial value.
    */
-  Optional() = default;
+  constexpr Optional() : mObject() {}
 
   /**
    * Default copy constructor.
diff --git a/util/include/chre/util/fixed_size_blocking_queue.h b/util/include/chre/util/system/fixed_size_blocking_queue.h
similarity index 93%
rename from util/include/chre/util/fixed_size_blocking_queue.h
rename to util/include/chre/util/system/fixed_size_blocking_queue.h
index 58277fe..81e631e 100644
--- a/util/include/chre/util/fixed_size_blocking_queue.h
+++ b/util/include/chre/util/system/fixed_size_blocking_queue.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef CHRE_UTIL_FIXED_SIZE_BLOCKING_QUEUE_H_
-#define CHRE_UTIL_FIXED_SIZE_BLOCKING_QUEUE_H_
+#ifndef CHRE_UTIL_SYSTEM_FIXED_SIZE_BLOCKING_QUEUE_H_
+#define CHRE_UTIL_SYSTEM_FIXED_SIZE_BLOCKING_QUEUE_H_
 
 #include <deque>
 
@@ -120,6 +120,6 @@
 
 }  // namespace chre
 
-#include "chre/util/fixed_size_blocking_queue_impl.h"  // IWYU pragma: export
+#include "chre/util/system/fixed_size_blocking_queue_impl.h"  // IWYU pragma: export
 
-#endif  // CHRE_UTIL_FIXED_SIZE_BLOCKING_QUEUE_H_
+#endif  // CHRE_UTIL_SYSTEM_FIXED_SIZE_BLOCKING_QUEUE_H_
diff --git a/util/include/chre/util/fixed_size_blocking_queue_impl.h b/util/include/chre/util/system/fixed_size_blocking_queue_impl.h
similarity index 94%
rename from util/include/chre/util/fixed_size_blocking_queue_impl.h
rename to util/include/chre/util/system/fixed_size_blocking_queue_impl.h
index 43911df..adea3f8 100644
--- a/util/include/chre/util/fixed_size_blocking_queue_impl.h
+++ b/util/include/chre/util/system/fixed_size_blocking_queue_impl.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef CHRE_UTIL_FIXED_SIZE_BLOCKING_QUEUE_IMPL_H_
-#define CHRE_UTIL_FIXED_SIZE_BLOCKING_QUEUE_IMPL_H_
+#ifndef CHRE_UTIL_SYSTEM_FIXED_SIZE_BLOCKING_QUEUE_IMPL_H_
+#define CHRE_UTIL_SYSTEM_FIXED_SIZE_BLOCKING_QUEUE_IMPL_H_
 
 // IWYU pragma: private
-#include "chre/util/fixed_size_blocking_queue.h"
 #include "chre/util/lock_guard.h"
+#include "chre/util/system/fixed_size_blocking_queue.h"
 
 namespace chre {
 
diff --git a/util/include/chre/util/system/message_common.h b/util/include/chre/util/system/message_common.h
new file mode 100644
index 0000000..f314f32
--- /dev/null
+++ b/util/include/chre/util/system/message_common.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_UTIL_SYSTEM_MESSAGE_COMMON_H_
+#define CHRE_UTIL_SYSTEM_MESSAGE_COMMON_H_
+
+#include <pw_allocator/unique_ptr.h>
+#include <pw_function/function.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+
+namespace chre::message {
+
+//! The ID of a MessageHub
+using MessageHubId = uint64_t;
+
+//! The ID of an endpoint
+using EndpointId = uint64_t;
+
+//! The ID of a session
+using SessionId = uint16_t;
+
+//! An invalid MessageHub ID
+constexpr MessageHubId MESSAGE_HUB_ID_INVALID = 0;
+
+//! An invalid endpoint ID
+constexpr EndpointId ENDPOINT_ID_INVALID = 0;
+
+//! An invalid session ID
+constexpr SessionId SESSION_ID_INVALID = UINT16_MAX;
+
+//! Endpoint types
+enum class EndpointType : uint8_t {
+  HOST_FRAMEWORK = 1,
+  HOST_APP = 2,
+  HOST_NATIVE = 3,
+  NANOAPP = 4,
+  GENERIC = 5,
+};
+
+//! Endpoint permissions
+//! This should match CHRE permissions.
+// TODO(b/373417024): Update permissions to this typed name in all MessageRouter
+// code
+enum class EndpointPermission : uint32_t {
+  NONE = 0,
+  AUDIO = 1,
+  GNSS = 1 << 1,
+  WIFI = 1 << 2,
+  WWAN = 1 << 3,
+  BLE = 1 << 4,
+};
+
+//! Represents a single endpoint connected to a MessageHub
+struct Endpoint {
+  MessageHubId messageHubId;
+  EndpointId endpointId;
+
+  bool operator==(const Endpoint &other) const {
+    return messageHubId == other.messageHubId && endpointId == other.endpointId;
+  }
+
+  bool operator!=(const Endpoint &other) const {
+    return !(*this == other);
+  }
+};
+
+//! Represents a session between two endpoints
+struct Session {
+  SessionId sessionId;
+  Endpoint initiator;
+  Endpoint peer;
+
+  bool operator==(const Session &other) const {
+    return sessionId == other.sessionId && initiator == other.initiator &&
+           peer == other.peer;
+  }
+
+  bool operator!=(const Session &other) const {
+    return !(*this == other);
+  }
+
+  bool isEquivalent(const Session &other) const {
+    return (initiator == other.initiator && peer == other.peer) ||
+           (initiator == other.peer && peer == other.initiator);
+  }
+};
+
+//! Represents a message sent using the MessageRouter
+struct Message {
+  Endpoint sender;
+  Endpoint recipient;
+  SessionId sessionId;
+  pw::UniquePtr<std::byte[]> data;
+  size_t length;
+  uint32_t messageType;
+  uint32_t messagePermissions;
+
+  Message()
+      : sessionId(SESSION_ID_INVALID),
+        data(nullptr),
+        length(0),
+        messageType(0),
+        messagePermissions(0) {}
+  Message(pw::UniquePtr<std::byte[]> &&data, size_t length,
+          uint32_t messageType, uint32_t messagePermissions, Session session,
+          bool sentBySessionInitiator)
+      : sender(sentBySessionInitiator ? session.initiator : session.peer),
+        recipient(sentBySessionInitiator ? session.peer : session.initiator),
+        sessionId(session.sessionId),
+        data(std::move(data)),
+        length(length),
+        messageType(messageType),
+        messagePermissions(messagePermissions) {}
+  Message(Message &&other)
+      : sender(other.sender),
+        recipient(other.recipient),
+        sessionId(other.sessionId),
+        data(std::move(other.data)),
+        length(other.length),
+        messageType(other.messageType),
+        messagePermissions(other.messagePermissions) {}
+
+  Message(const Message &) = delete;
+  Message &operator=(const Message &) = delete;
+
+  Message &operator=(Message &&other) {
+    sender = other.sender;
+    recipient = other.recipient;
+    sessionId = other.sessionId;
+    data = std::move(other.data);
+    length = other.length;
+    messageType = other.messageType;
+    messagePermissions = other.messagePermissions;
+    return *this;
+  }
+};
+
+//! Represents information about an endpoint
+//! Service information is stored in ServiceManager
+struct EndpointInfo {
+  static constexpr size_t kMaxNameLength = 50;
+
+  EndpointInfo(EndpointId id, const char *name, uint32_t version,
+               EndpointType type, uint32_t requiredPermissions)
+      : id(id),
+        version(version),
+        type(type),
+        requiredPermissions(requiredPermissions) {
+    if (name != nullptr) {
+      std::strncpy(this->name, name, kMaxNameLength);
+    } else {
+      this->name[0] = '\0';
+    }
+    this->name[kMaxNameLength] = '\0';
+  }
+
+  EndpointId id;
+  char name[kMaxNameLength + 1];
+  uint32_t version;
+  EndpointType type;
+  uint32_t requiredPermissions;
+
+  bool operator==(const EndpointInfo &other) const {
+    return id == other.id && version == other.version && type == other.type &&
+           requiredPermissions == other.requiredPermissions &&
+           std::strcmp(name, other.name) == 0;
+  }
+
+  bool operator!=(const EndpointInfo &other) const {
+    return !(*this == other);
+  }
+};
+
+//! Represents information about a MessageHub
+struct MessageHubInfo {
+  MessageHubId id;
+  const char *name;
+
+  bool operator==(const MessageHubInfo &other) const {
+    return id == other.id && std::strcmp(name, other.name) == 0;
+  }
+
+  bool operator!=(const MessageHubInfo &other) const {
+    return !(*this == other);
+  }
+};
+
+}  // namespace chre::message
+
+#endif  // CHRE_UTIL_SYSTEM_MESSAGE_COMMON_H_
diff --git a/util/include/chre/util/system/message_router.h b/util/include/chre/util/system/message_router.h
new file mode 100644
index 0000000..089f149
--- /dev/null
+++ b/util/include/chre/util/system/message_router.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_H_
+#define CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_H_
+
+#include <pw_allocator/unique_ptr.h>
+#include <pw_containers/vector.h>
+#include <pw_function/function.h>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+
+#include "chre/platform/mutex.h"
+#include "chre/util/singleton.h"
+#include "chre/util/system/message_common.h"
+
+namespace chre::message {
+
+//! MessageRouter routes messages between endpoints connected to MessageHubs. It
+//! provides an API for registering MessageHubs, opening and closing sessions,
+//! and sending messages between endpoints. Each MessageHub is expected to
+//! register a callback to handle messages sent to its endpoints and other
+//! functions to provide information about the endpoints connected to it.
+//!
+//! MessageRouter is thread-safe.
+//!
+//! Usage:
+//! 1. Create a MessageRouter instance.
+//! 2. Register MessageHubs with the MessageRouter. Each MessageHub must have
+//!    a unique ID and a callback to handle messages sent to its endpoints.
+//! 3. Open sessions from endpoints connected to MessageHubs to endpoints
+//!    connected to other MessageHubs.
+//! 4. Send messages to endpoints using the MessageRouter API.
+//! 5. Close sessions when they are no longer needed.
+class MessageRouter {
+ public:
+  //! The callback used to register a MessageHub with the MessageRouter
+  class MessageHubCallback {
+   public:
+    virtual ~MessageHubCallback() = default;
+
+    //! Message processing callback. If this function returns true,
+    //! the MessageHub received the message and will deliver it to the
+    //! receiving endpoint, or close the session if an error occurs.
+    //! @see sendMessage
+    //! @param session The session that the message was sent on (this reference
+    //!                is only valid for the duration of the callback)
+    //! @param sentBySessionInitiator Whether the message was sent by the
+    //! initiator of the session
+    //! @return true if the message was accepted for processing
+    virtual bool onMessageReceived(pw::UniquePtr<std::byte[]> &&data,
+                                   size_t length, uint32_t messageType,
+                                   uint32_t messagePermissions,
+                                   const Session &session,
+                                   bool sentBySessionInitiator) = 0;
+
+    //! Callback called when the session is closed
+    virtual void onSessionClosed(const Session &session) = 0;
+
+    //! Callback called to iterate over all endpoints connected to the
+    //! MessageHub. Underlying endpoint storage must not change during this
+    //! callback. If function returns true, the MessageHub can stop iterating
+    //! over future endpoints. This function should not call any MessageRouter
+    //! or MessageHub functions.
+    virtual void forEachEndpoint(
+        const pw::Function<bool(const EndpointInfo &)> &function) = 0;
+
+    //! @return The EndpointInfo for the given endpoint ID. This function should
+    //! not call any MessageRouter or MessageHub functions.
+    virtual std::optional<EndpointInfo> getEndpointInfo(
+        EndpointId endpointId) = 0;
+  };
+
+  //! The API returned when registering a MessageHub with the MessageRouter.
+  class MessageHub {
+   public:
+    //! Creates an empty MessageHub that is not usable, similar to a moved-from
+    //! MessageHub. Attempting to call any method on this object results in
+    //! undefined behavior.
+    MessageHub();
+
+    ~MessageHub() {
+      if (mRouter != nullptr) {
+        mRouter->unregisterMessageHub(mHubId);
+      }
+    }
+    // There can only be one live MessageHub instance for a given hub ID, so
+    // only move operations are supported.
+    MessageHub(const MessageHub &) = delete;
+    MessageHub &operator=(const MessageHub &) = delete;
+    MessageHub(MessageHub &&other);
+    MessageHub &operator=(MessageHub &&other);
+
+    //! Opens a session from an endpoint connected to the current MessageHub
+    //! to the listed MessageHub ID and endpoint ID
+    //! @return The session ID or SESSION_ID_INVALID if the session could
+    //! not be opened
+    SessionId openSession(EndpointId fromEndpointId,
+                          MessageHubId toMessageHubId, EndpointId toEndpointId);
+
+    //! Closes the session with sessionId
+    //! @return true if the session was closed, false if the session was not
+    //! found
+    bool closeSession(SessionId sessionId);
+
+    //! Returns a session if it exists
+    //! @return The session or std::nullopt if the session was not found
+    std::optional<Session> getSessionWithId(SessionId sessionId);
+
+    //! Sends a message to the session specified by sessionId.
+    //! @see chreSendReliableMessageAsync. Sends the message in a reliable
+    //! manner if possible. If the message cannot be delivered, the session
+    //! is closed and subsequent calls to this function with the same sessionId
+    //! will return false.
+    //! @param data The data to send
+    //! @param length The length of the data to send
+    //! @param messageType The type of the message, a bit flagged value
+    //! @param messagePermissions The permissions of the message, a bit flagged
+    //! value
+    //! @param sessionId The session to send the message on
+    //! @return true if the message was sent, false if the message could not be
+    //! sent
+    bool sendMessage(pw::UniquePtr<std::byte[]> &&data, size_t length,
+                     uint32_t messageType, uint32_t messagePermissions,
+                     SessionId sessionId);
+
+    //! @return The MessageHub ID of the currently connected MessageHub
+    MessageHubId getId();
+
+   private:
+    friend class MessageRouter;
+
+    MessageHub(MessageRouter &router, MessageHubId id);
+
+    //! The MessageRouter that this MessageHub is connected to
+    MessageRouter *mRouter;
+
+    //! The id of this message hub
+    MessageHubId mHubId;
+  };
+
+  //! Represents a MessageHub and its connected endpoints
+  struct MessageHubRecord {
+    MessageHubInfo info;
+    MessageHubCallback *callback;
+  };
+
+  MessageRouter() = delete;
+  MessageRouter(pw::Vector<MessageHubRecord> &messageHubs,
+                pw::Vector<Session> &sessions)
+      : mMessageHubs(messageHubs), mSessions(sessions) {}
+
+  //! Registers a MessageHub with the MessageRouter.
+  //! The provided name must be unique and not registered before and be a valid
+  //! C string. The data underlying name must outlive the MessageHub. The
+  //! callback must outlive the MessageHub. The ID must be unique and not
+  //! registered before. When the returned MessageHub is destroyed, it will
+  //! unregister itself from the MessageRouter.
+  //! @param name The name of the MessageHub
+  //! @param id The ID of the MessageHub
+  //! @param callback The callback to handle messages sent to the MessageHub
+  //! @return The MessageHub API or std::nullopt if the MessageHub could not be
+  //! registered
+  std::optional<MessageHub> registerMessageHub(const char *name,
+                                               MessageHubId id,
+                                               MessageHubCallback &callback);
+
+  //! Executes the function for each endpoint connected to this MessageHub.
+  //! If function returns true, the iteration will stop.
+  //! @return true if the MessageHub is found, false otherwise
+  bool forEachEndpointOfHub(
+      MessageHubId messageHubId,
+      const pw::Function<bool(const EndpointInfo &)> &function);
+
+  //! Executes the function for each endpoint connected to all Message Hubs.
+  //! The lock is held when calling the callback.
+  void forEachEndpoint(
+      const pw::Function<void(const MessageHubInfo &, const EndpointInfo &)>
+          &function);
+
+  //! @return The EndpointInfo for the given hub and endpoint IDs
+  std::optional<EndpointInfo> getEndpointInfo(MessageHubId messageHubId,
+                                              EndpointId endpointId);
+
+  //! Executes the function for each MessageHub connected to the MessageRouter.
+  //! If function returns true, the iteration will stop.
+  //! The lock is held when calling the callback.
+  void forEachMessageHub(
+      const pw::Function<bool(const MessageHubInfo &)> &function);
+
+ private:
+  //! Unregisters a MessageHub from the MessageRouter. This function will
+  //! close all sessions that were initiated by or connected to the MessageHub
+  //! and destroy the MessageHubRecord. This function will call the callback
+  //! for each session that was closed only for the other message hub in the
+  //! session.
+  //! @return true if the MessageHub was unregistered, false if the MessageHub
+  //! was not found.
+  bool unregisterMessageHub(MessageHubId fromMessageHubId);
+
+  //! Opens a session from an endpoint connected to the current MessageHub
+  //! to the listed MessageHub ID and endpoint ID
+  //! @return The session ID or SESSION_ID_INVALID if the session could not be
+  //! opened
+  SessionId openSession(MessageHubId fromMessageHubId,
+                        EndpointId fromEndpointId, MessageHubId toMessageHubId,
+                        EndpointId toEndpointId);
+
+  //! Closes the session with sessionId
+  //! @return true if the session was closed, false if the session was not
+  //! found
+  bool closeSession(MessageHubId fromMessageHubId, SessionId sessionId);
+
+  //! Returns a session if it exists
+  //! @return The session or std::nullopt if the session was not found
+  std::optional<Session> getSessionWithId(MessageHubId fromMessageHubId,
+                                          SessionId sessionId);
+
+  //! Sends a message to the session specified by sessionId.
+  //! @see chreSendReliableMessageAsync. Sends the message in a reliable
+  //! manner if possible. If the message cannot be delivered, the session
+  //! is closed and subsequent calls to this function with the same sessionId
+  //! will return false.
+  //! @see MessageHub::sendMessage
+  //! @return true if the message was sent, false if the message could not be
+  //! sent
+  bool sendMessage(pw::UniquePtr<std::byte[]> &&data, size_t length,
+                   uint32_t messageType, uint32_t messagePermissions,
+                   SessionId sessionId, MessageHubId fromMessageHubId);
+
+  //! @return The MessageHubRecord for the given MessageHub ID
+  const MessageHubRecord *getMessageHubRecordLocked(MessageHubId messageHubId);
+
+  //! @return The index of the session if it exists
+  //! Requires the caller to hold the mutex
+  std::optional<size_t> findSessionIndexLocked(MessageHubId fromMessageHubId,
+                                               SessionId sessionId);
+
+  //! @return The callback for the given MessageHub ID or nullptr if not found
+  //! Requires the caller to hold the mutex
+  MessageHubCallback *getCallbackFromMessageHubId(MessageHubId messageHubId);
+
+  //! @return The callback for the given MessageHub ID or nullptr if not found
+  MessageHubCallback *getCallbackFromMessageHubIdLocked(
+      MessageHubId messageHubId);
+
+  //! @return true if the endpoint exists in the MessageHub with the given
+  //! callback
+  bool checkIfEndpointExists(MessageHubCallback *callback, EndpointId endpointId);
+
+  //! The mutex to protect MessageRouter state
+  Mutex mMutex;
+
+  //! The next available Session ID
+  SessionId mNextSessionId = 0;
+
+  //! The list of MessageHubs connected to the MessageRouter
+  pw::Vector<MessageHubRecord> &mMessageHubs;
+
+  //! The list of sessions connected to the MessageRouter
+  pw::Vector<Session> &mSessions;
+};
+
+//! Define the singleton instance of the MessageRouter
+typedef Singleton<MessageRouter> MessageRouterSingleton;
+
+//! Routes messages between MessageHubs
+template <size_t kMaxMessageHubs, size_t kMaxSessions>
+class MessageRouterWithStorage : public MessageRouter {
+ public:
+  MessageRouterWithStorage():
+      MessageRouter(mMessageHubs, mSessions) {}
+
+ private:
+  //! The list of MessageHubs connected to the MessageRouter
+  pw::Vector<MessageHubRecord, kMaxMessageHubs> mMessageHubs;
+
+  //! The list of sessions connected to the MessageRouter
+  pw::Vector<Session, kMaxSessions> mSessions;
+};
+
+}  // namespace chre::message
+
+#endif  // CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_H_
diff --git a/util/include/chre/util/system/message_router_callback_allocator.h b/util/include/chre/util/system/message_router_callback_allocator.h
new file mode 100644
index 0000000..e3aabd8
--- /dev/null
+++ b/util/include/chre/util/system/message_router_callback_allocator.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_CALLBACK_ALLOCATOR_H_
+#define CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_CALLBACK_ALLOCATOR_H_
+
+#include <pw_allocator/allocator.h>
+#include <pw_allocator/capability.h>
+#include <pw_allocator/unique_ptr.h>
+#include <pw_containers/vector.h>
+#include <pw_function/function.h>
+#include <cstddef>
+#include <optional>
+
+#include "chre/platform/mutex.h"
+
+namespace chre::message {
+
+//! An allocator for message free callbacks
+//! This allocator is used to store message free callbacks in a vector
+//! The allocator will call the free callback when the message is deallocated
+//! This is used to create pw::UniquePtrs for messages that have a free
+//! callback.
+//! @param Metadata The metadata type for the callback function
+template <typename Metadata>
+class MessageRouterCallbackAllocator : public pw::Allocator {
+ public:
+  static constexpr Capabilities kCapabilities = 0;
+
+  //! The callback used to free a message
+  using MessageFreeCallback = pw::Function<void(
+      std::byte *message, size_t length, const Metadata &metadata)>;
+
+  //! A record of a message and its free callback
+  struct FreeCallbackRecord {
+    std::byte *message;
+    Metadata metadata;
+    size_t messageSize;
+  };
+
+  MessageRouterCallbackAllocator(
+      MessageFreeCallback &&callback,
+      pw::Vector<FreeCallbackRecord> &freeCallbackRecords);
+
+  //! @see pw::Allocator::DoAllocate
+  virtual void *DoAllocate(Layout /* layout */) override;
+
+  //! @see pw::Allocator::DoDeallocate
+  virtual void DoDeallocate(void *ptr) override;
+
+  //! Creates a pw::UniquePtr for a message with a free callback.
+  //! The free callback will be called when the message is deallocated.
+  //! @return A pw::UniquePtr containing the message
+  [[nodiscard]] pw::UniquePtr<std::byte[]> MakeUniqueArrayWithCallback(
+      std::byte *ptr, size_t size, Metadata &&metadata);
+
+ private:
+  //! The callback used to free a message
+  MessageFreeCallback mCallback;
+
+  //! The mutex to protect mFreeCallbackRecords
+  Mutex mMutex;
+
+  //! The map of message pointers to free callbacks
+  pw::Vector<FreeCallbackRecord> &mFreeCallbackRecords;
+};
+
+}  // namespace chre::message
+
+#include "chre/util/system/message_router_callback_allocator_impl.h"
+
+#endif  // CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_CALLBACK_ALLOCATOR_H_
diff --git a/util/include/chre/util/system/message_router_callback_allocator_impl.h b/util/include/chre/util/system/message_router_callback_allocator_impl.h
new file mode 100644
index 0000000..fc27e90
--- /dev/null
+++ b/util/include/chre/util/system/message_router_callback_allocator_impl.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_CALLBACK_ALLOCATOR_IMPL_H_
+#define CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_CALLBACK_ALLOCATOR_IMPL_H_
+
+#include <pw_allocator/allocator.h>
+#include <pw_allocator/capability.h>
+#include <pw_allocator/unique_ptr.h>
+#include <pw_containers/vector.h>
+#include <pw_function/function.h>
+#include <cstddef>
+#include <optional>
+
+#include "chre/util/lock_guard.h"
+#include "chre/util/system/message_common.h"
+#include "chre/util/system/message_router_callback_allocator.h"
+
+namespace chre::message {
+
+template <typename Metadata>
+MessageRouterCallbackAllocator<Metadata>::MessageRouterCallbackAllocator(
+    MessageFreeCallback &&callback,
+    pw::Vector<FreeCallbackRecord> &freeCallbackRecords)
+        : pw::Allocator(kCapabilities),
+          mCallback(std::move(callback)),
+          mFreeCallbackRecords(freeCallbackRecords) {}
+
+template <typename Metadata>
+void *MessageRouterCallbackAllocator<Metadata>::DoAllocate(
+    Layout /* layout */) {
+  return nullptr;
+}
+
+template <typename Metadata>
+void MessageRouterCallbackAllocator<Metadata>::DoDeallocate(void *ptr) {
+  std::optional<FreeCallbackRecord> freeCallbackRecord;
+  {
+    LockGuard<Mutex> lock(mMutex);
+    for (FreeCallbackRecord &record : mFreeCallbackRecords) {
+      if (record.message == ptr) {
+        freeCallbackRecord = std::move(record);
+        mFreeCallbackRecords.erase(&record);
+        break;
+      }
+    }
+  }
+
+  if (freeCallbackRecord.has_value()) {
+    mCallback(freeCallbackRecord->message, freeCallbackRecord->messageSize,
+              freeCallbackRecord->metadata);
+  }
+}
+
+template <typename Metadata>
+pw::UniquePtr<std::byte[]>
+MessageRouterCallbackAllocator<Metadata>::MakeUniqueArrayWithCallback(
+    std::byte *ptr, size_t size, Metadata &&metadata) {
+  {
+    LockGuard<Mutex> lock(mMutex);
+    if (mFreeCallbackRecords.full()) {
+      return pw::UniquePtr<std::byte[]>();
+    }
+
+    mFreeCallbackRecords.push_back(
+        {.message = ptr, .metadata = std::move(metadata), .messageSize = size});
+  }
+
+  return WrapUniqueArray(ptr, size);
+}
+
+}  // namespace chre::message
+
+#endif  // CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_CALLBACK_ALLOCATOR_IMPL_H_
diff --git a/util/include/chre/util/synchronized_expandable_memory_pool.h b/util/include/chre/util/system/synchronized_expandable_memory_pool.h
similarity index 93%
rename from util/include/chre/util/synchronized_expandable_memory_pool.h
rename to util/include/chre/util/system/synchronized_expandable_memory_pool.h
index 72c8c62..3a5cd46 100644
--- a/util/include/chre/util/synchronized_expandable_memory_pool.h
+++ b/util/include/chre/util/system/synchronized_expandable_memory_pool.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef CHRE_UTIL_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_H_
-#define CHRE_UTIL_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_H_
+#ifndef CHRE_UTIL_SYSTEM_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_H_
+#define CHRE_UTIL_SYSTEM_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_H_
 
 #include "chre/platform/mutex.h"
 #include "chre/util/fixed_size_vector.h"
@@ -124,6 +124,6 @@
 
 }  // namespace chre
 
-#include "chre/util/synchronized_expandable_memory_pool_impl.h"  // IWYU pragma: export
+#include "chre/util/system/synchronized_expandable_memory_pool_impl.h"  // IWYU pragma: export
 
-#endif  // CHRE_UTIL_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_H_
+#endif  // CHRE_UTIL_SYSTEM_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_H_
diff --git a/util/include/chre/util/synchronized_expandable_memory_pool_impl.h b/util/include/chre/util/system/synchronized_expandable_memory_pool_impl.h
similarity index 94%
rename from util/include/chre/util/synchronized_expandable_memory_pool_impl.h
rename to util/include/chre/util/system/synchronized_expandable_memory_pool_impl.h
index 31b9007..f5698b4 100644
--- a/util/include/chre/util/synchronized_expandable_memory_pool_impl.h
+++ b/util/include/chre/util/system/synchronized_expandable_memory_pool_impl.h
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef CHRE_UTIL_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_IMPL_H_
-#define CHRE_UTIL_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_IMPL_H_
+#ifndef CHRE_UTIL_SYSTEM_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_IMPL_H_
+#define CHRE_UTIL_SYSTEM_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_IMPL_H_
 
 // IWYU pragma: private
 #include <algorithm>
 
 #include "chre/util/lock_guard.h"
 #include "chre/util/memory_pool.h"
-#include "chre/util/synchronized_expandable_memory_pool.h"
+#include "chre/util/system/synchronized_expandable_memory_pool.h"
 
 namespace chre {
 
@@ -147,4 +147,4 @@
 }
 }  // namespace chre
 
-#endif  // CHRE_UTIL_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_IMPL_H_
+#endif  // CHRE_UTIL_SYSTEM_SYNCHRONIZED_EXPANDABLE_MEMORY_POOL_IMPL_H_
diff --git a/util/include/chre/util/synchronized_memory_pool.h b/util/include/chre/util/system/synchronized_memory_pool.h
similarity index 92%
rename from util/include/chre/util/synchronized_memory_pool.h
rename to util/include/chre/util/system/synchronized_memory_pool.h
index 7e5294d..85cdaf6 100644
--- a/util/include/chre/util/synchronized_memory_pool.h
+++ b/util/include/chre/util/system/synchronized_memory_pool.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef CHRE_UTIL_SYNCHRONIZED_MEMORY_POOL_H_
-#define CHRE_UTIL_SYNCHRONIZED_MEMORY_POOL_H_
+#ifndef CHRE_UTIL_SYSTEM_SYNCHRONIZED_MEMORY_POOL_H_
+#define CHRE_UTIL_SYSTEM_SYNCHRONIZED_MEMORY_POOL_H_
 
 #include "chre/platform/mutex.h"
 #include "chre/util/memory_pool.h"
@@ -94,6 +94,6 @@
 
 }  // namespace chre
 
-#include "chre/util/synchronized_memory_pool_impl.h"  // IWYU pragma: export
+#include "chre/util/system/synchronized_memory_pool_impl.h"  // IWYU pragma: export
 
-#endif  // CHRE_UTIL_SYNCHRONIZED_MEMORY_POOL_H_
+#endif  // CHRE_UTIL_SYSTEM_SYNCHRONIZED_MEMORY_POOL_H_
diff --git a/util/include/chre/util/synchronized_memory_pool_impl.h b/util/include/chre/util/system/synchronized_memory_pool_impl.h
similarity index 87%
rename from util/include/chre/util/synchronized_memory_pool_impl.h
rename to util/include/chre/util/system/synchronized_memory_pool_impl.h
index 6856500..6d78686 100644
--- a/util/include/chre/util/synchronized_memory_pool_impl.h
+++ b/util/include/chre/util/system/synchronized_memory_pool_impl.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef CHRE_UTIL_SYNCHRONIZED_MEMORY_POOL_IMPL_H_
-#define CHRE_UTIL_SYNCHRONIZED_MEMORY_POOL_IMPL_H_
+#ifndef CHRE_UTIL_SYSTEM_SYNCHRONIZED_MEMORY_POOL_IMPL_H_
+#define CHRE_UTIL_SYSTEM_SYNCHRONIZED_MEMORY_POOL_IMPL_H_
 
 // IWYU pragma: private
 #include "chre/util/lock_guard.h"
-#include "chre/util/synchronized_memory_pool.h"
+#include "chre/util/system/synchronized_memory_pool.h"
 
 namespace chre {
 
@@ -53,4 +53,4 @@
 
 }  // namespace chre
 
-#endif  // CHRE_UTIL_SYNCHRONIZED_MEMORY_POOL_IMPL_H_
+#endif  // CHRE_UTIL_SYSTEM_SYNCHRONIZED_MEMORY_POOL_IMPL_H_
diff --git a/util/include/chre/util/transaction_manager.h b/util/include/chre/util/system/transaction_manager.h
similarity index 97%
rename from util/include/chre/util/transaction_manager.h
rename to util/include/chre/util/system/transaction_manager.h
index 7fd4b0e..28656b2 100644
--- a/util/include/chre/util/transaction_manager.h
+++ b/util/include/chre/util/system/transaction_manager.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef CHRE_UTIL_TRANSACTION_MANAGER_H_
-#define CHRE_UTIL_TRANSACTION_MANAGER_H_
+#ifndef CHRE_UTIL_SYSTEM_TRANSACTION_MANAGER_H_
+#define CHRE_UTIL_SYSTEM_TRANSACTION_MANAGER_H_
 
 #include <cstdint>
 
@@ -226,6 +226,6 @@
 
 }  // namespace chre
 
-#include "chre/util/transaction_manager_impl.h"  // IWYU pragma: export
+#include "chre/util/system/transaction_manager_impl.h"  // IWYU pragma: export
 
-#endif  // CHRE_UTIL_TRANSACTION_MANAGER_H_
+#endif  // CHRE_UTIL_SYSTEM_TRANSACTION_MANAGER_H_
diff --git a/util/include/chre/util/transaction_manager_impl.h b/util/include/chre/util/system/transaction_manager_impl.h
similarity index 97%
rename from util/include/chre/util/transaction_manager_impl.h
rename to util/include/chre/util/system/transaction_manager_impl.h
index 3aa5108..4d689b3 100644
--- a/util/include/chre/util/transaction_manager_impl.h
+++ b/util/include/chre/util/system/transaction_manager_impl.h
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef CHRE_UTIL_TRANSACTION_MANAGER_IMPL_H_
-#define CHRE_UTIL_TRANSACTION_MANAGER_IMPL_H_
+#ifndef CHRE_UTIL_SYSTEM_TRANSACTION_MANAGER_IMPL_H_
+#define CHRE_UTIL_SYSTEM_TRANSACTION_MANAGER_IMPL_H_
 
 // IWYU pragma: private
-#include "chre/util/transaction_manager.h"
+#include "chre/util/system/transaction_manager.h"
 
 #include "chre/core/event_loop_common.h"
 #include "chre/platform/system_time.h"
@@ -230,4 +230,4 @@
 
 }  // namespace chre
 
-#endif  // CHRE_UTIL_TRANSACTION_MANAGER_IMPL_H_
+#endif  // CHRE_UTIL_SYSTEM_TRANSACTION_MANAGER_IMPL_H_
diff --git a/util/include/chre/util/throttle.h b/util/include/chre/util/throttle.h
index 5026afb..f6c54fd 100644
--- a/util/include/chre/util/throttle.h
+++ b/util/include/chre/util/throttle.h
@@ -17,10 +17,6 @@
 #ifndef CHRE_UTIL_THROTTLE_H_
 #define CHRE_UTIL_THROTTLE_H_
 
-#include "chre/util/optional.h"
-
-using ::chre::Optional;
-
 /**
  * Throttles an action to a given interval and maximum number of times.
  * The action will be called at most maxCount in every interval.
@@ -30,20 +26,21 @@
  * @param maxCount The maximum number of times to call the action
  * @param getTime A function to get the current time
  */
-#define CHRE_THROTTLE(action, interval, maxCount, getTime) \
-  do {                                                     \
-    static uint32_t _count = 0;                            \
-    static Optional<Nanoseconds> _lastCallTime;            \
-    Nanoseconds _now = getTime;                            \
-    if (!_lastCallTime.has_value() ||                      \
-        _now - _lastCallTime.value() >= interval) {        \
-      _count = 0;                                          \
-      _lastCallTime = _now;                                \
-    }                                                      \
-    if (++_count > maxCount) {                             \
-      break;                                               \
-    }                                                      \
-    action;                                                \
+#define CHRE_THROTTLE(action, interval, maxCount, getTime)       \
+  do {                                                           \
+    static uint32_t _count = 0;                                  \
+    static bool _hasLastCallTime = false;                        \
+    static Nanoseconds _lastCallTime;                            \
+    Nanoseconds _now = getTime;                                  \
+    if (!_hasLastCallTime || _now - _lastCallTime >= interval) { \
+      _hasLastCallTime = true;                                   \
+      _count = 0;                                                \
+      _lastCallTime = _now;                                      \
+    }                                                            \
+    if (++_count > maxCount) {                                   \
+      break;                                                     \
+    }                                                            \
+    action;                                                      \
   } while (0)
 
 #endif  // CHRE_UTIL_THROTTLE_H_
diff --git a/util/nanoapp/ble.cc b/util/nanoapp/ble.cc
index 8283ab6..7a6c0fc 100644
--- a/util/nanoapp/ble.cc
+++ b/util/nanoapp/ble.cc
@@ -19,6 +19,7 @@
 
 namespace chre {
 
+using ble_constants::kBroadcasterAddress;
 using ble_constants::kGoogleEddystoneUuid;
 using ble_constants::kGoogleManufactureData;
 using ble_constants::kGoogleManufactureDataLength;
@@ -26,6 +27,7 @@
 using ble_constants::kGoogleNearbyFastpairUuid;
 using ble_constants::kGoogleUuidDataLength;
 using ble_constants::kGoogleUuidMask;
+using ble_constants::kNumBroadcasterFilters;
 using ble_constants::kNumManufacturerDataFilters;
 using ble_constants::kNumScanFilters;
 using ble_constants::kRssiThreshold;
@@ -108,4 +110,25 @@
   return true;
 }
 
+bool createBleScanFilterForAdvertiser(
+    struct chreBleScanFilterV1_9 &filter,
+    chreBleBroadcasterAddressFilter *broadcasterFilters,
+    uint8_t numBroadcasterFilters) {
+  if (numBroadcasterFilters < kNumBroadcasterFilters) {
+    return false;
+  }
+
+  memcpy(&broadcasterFilters[0], kBroadcasterAddress,
+         sizeof(broadcasterFilters[0]));
+
+  memset(&filter, 0, sizeof(filter));
+  filter.rssiThreshold = kRssiThreshold;
+  filter.genericFilterCount = 0;
+  filter.genericFilters = nullptr;
+
+  filter.broadcasterAddressFilterCount = kNumBroadcasterFilters;
+  filter.broadcasterAddressFilters = broadcasterFilters;
+  return true;
+}
+
 }  // namespace chre
diff --git a/util/pigweed/rpc_server.cc b/util/pigweed/rpc_server.cc
index 42be93b..3fd6424 100644
--- a/util/pigweed/rpc_server.cc
+++ b/util/pigweed/rpc_server.cc
@@ -102,8 +102,9 @@
                   hostMessage->messageSize);
 
   pw::Result<uint32_t> result = pw::rpc::ExtractChannelId(packet);
-  if (result.status() != PW_STATUS_OK) {
-    LOGE("Unable to extract channel ID from packet");
+  if (!result.status().ok()) {
+    LOGE("Unable to extract channel ID from packet: %" PRIu8,
+         static_cast<uint8_t>(result.status().code()));
     return false;
   }
 
@@ -124,13 +125,13 @@
   mHostOutput.setHostEndpoint(hostMessage->hostEndpoint);
   pw::Status status = mServer.OpenChannel(result.value(), mHostOutput);
   if (status != pw::OkStatus() && status != pw::Status::AlreadyExists()) {
-    LOGE("Failed to open channel");
+    LOGE("Failed to open channel: %" PRIu8, static_cast<uint8_t>(status.code()));
     return false;
   }
 
   status = mServer.ProcessPacket(packet);
-  if (status != pw::OkStatus()) {
-    LOGE("Failed to process the packet");
+  if (!status.ok()) {
+    LOGE("Failed to process the packet: %" PRIu8, static_cast<uint8_t>(status.code()));
     return false;
   }
 
@@ -145,8 +146,9 @@
                   data->msgSize);
 
   pw::Result<uint32_t> result = pw::rpc::ExtractChannelId(packet);
-  if (result.status() != PW_STATUS_OK) {
-    LOGE("Unable to extract channel ID from packet");
+  if (!result.status().ok()) {
+    LOGE("Unable to extract channel ID from packet: %" PRIu8,
+         static_cast<uint8_t>(result.status().code()));
     return false;
   }
 
@@ -159,13 +161,13 @@
   mNanoappOutput.setClient(senderInstanceId);
   pw::Status status = mServer.OpenChannel(result.value(), mNanoappOutput);
   if (status != pw::OkStatus() && status != pw::Status::AlreadyExists()) {
-    LOGE("Failed to open channel");
+    LOGE("Failed to open channel: %" PRIu8, static_cast<uint8_t>(status.code()));
     return false;
   }
 
   status = mServer.ProcessPacket(packet);
-  if (status != pw::OkStatus()) {
-    LOGE("Failed to process the packet");
+  if (!status.ok()) {
+    LOGE("Failed to process the packet: %" PRIu8, static_cast<uint8_t>(status.code()));
     return false;
   }
 
@@ -196,9 +198,12 @@
   auto info = static_cast<const struct chreNanoappInfo *>(eventData);
 
   if (info->instanceId > kRpcNanoappMaxId) {
-    LOGE("Invalid nanoapp Id 0x%08" PRIx32, info->instanceId);
-  } else if (!mServer.CloseChannel(info->instanceId).ok()) {
-    LOGE("Failed to close channel for nanoapp 0x%08" PRIx32, info->instanceId);
+    LOGE("Invalid nanoapp instance ID %" PRIu32, info->instanceId);
+  } else if (pw::Status status = mServer.CloseChannel(info->instanceId);
+             !status.ok()) {
+    LOGE("Failed to close channel for nanoapp with instance ID %"
+         PRIu32 ": %" PRIu8, info->instanceId,
+         static_cast<uint8_t>(status.code()));
   }
 }
 
diff --git a/util/system/message_router.cc b/util/system/message_router.cc
new file mode 100644
index 0000000..3582889
--- /dev/null
+++ b/util/system/message_router.cc
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <cstring>
+#include <optional>
+#include <utility>
+
+#include "chre/platform/log.h"
+#include "chre/util/dynamic_vector.h"
+#include "chre/util/lock_guard.h"
+#include "chre/util/system/message_common.h"
+#include "chre/util/system/message_router.h"
+
+namespace chre::message {
+
+MessageRouter::MessageHub::MessageHub()
+    : mRouter(nullptr), mHubId(MESSAGE_HUB_ID_INVALID) {}
+
+MessageRouter::MessageHub::MessageHub(MessageRouter &router, MessageHubId id)
+    : mRouter(&router), mHubId(id) {}
+
+MessageRouter::MessageHub::MessageHub(MessageHub &&other)
+    : mRouter(other.mRouter), mHubId(other.mHubId) {
+  other.mRouter = nullptr;
+  other.mHubId = MESSAGE_HUB_ID_INVALID;
+}
+
+MessageRouter::MessageHub &MessageRouter::MessageHub::operator=(
+    MessageHub &&other) {
+  mRouter = other.mRouter;
+  mHubId = other.mHubId;
+  other.mRouter = nullptr;
+  other.mHubId = MESSAGE_HUB_ID_INVALID;
+  return *this;
+}
+
+SessionId MessageRouter::MessageHub::openSession(EndpointId fromEndpointId,
+                                                 MessageHubId toMessageHubId,
+                                                 EndpointId toEndpointId) {
+  return mRouter == nullptr
+             ? SESSION_ID_INVALID
+             : mRouter->openSession(mHubId, fromEndpointId, toMessageHubId,
+                                    toEndpointId);
+}
+
+bool MessageRouter::MessageHub::closeSession(SessionId sessionId) {
+  return mRouter == nullptr ? false : mRouter->closeSession(mHubId, sessionId);
+}
+
+std::optional<Session> MessageRouter::MessageHub::getSessionWithId(
+    SessionId sessionId) {
+  return mRouter == nullptr ? std::nullopt
+                            : mRouter->getSessionWithId(mHubId, sessionId);
+}
+
+bool MessageRouter::MessageHub::sendMessage(pw::UniquePtr<std::byte[]> &&data,
+                                            size_t length, uint32_t messageType,
+                                            uint32_t messagePermissions,
+                                            SessionId sessionId) {
+  return mRouter == nullptr
+             ? false
+             : mRouter->sendMessage(std::move(data), length, messageType,
+                                    messagePermissions, sessionId, mHubId);
+}
+
+MessageHubId MessageRouter::MessageHub::getId() {
+  return mHubId;
+}
+
+std::optional<typename MessageRouter::MessageHub>
+MessageRouter::registerMessageHub(
+    const char *name, MessageHubId id,
+    MessageRouter::MessageRouter::MessageHubCallback &callback) {
+  LockGuard<Mutex> lock(mMutex);
+  if (mMessageHubs.full()) {
+    LOGE(
+        "Message hub '%s' not registered: maximum number of message hubs "
+        "reached",
+        name);
+    return std::nullopt;
+  }
+
+  for (MessageHubRecord &messageHub : mMessageHubs) {
+    if (std::strcmp(messageHub.info.name, name) == 0 ||
+        messageHub.info.id == id) {
+      LOGE(
+          "Message hub '%s' not registered: hub with same name or ID already "
+          "exists",
+          name);
+      return std::nullopt;
+    }
+  }
+
+  MessageHubRecord messageHubRecord = {
+      .info = {.id = id, .name = name},
+      .callback = &callback,
+  };
+  mMessageHubs.push_back(std::move(messageHubRecord));
+  return MessageHub(*this, id);
+}
+
+bool MessageRouter::forEachEndpointOfHub(
+    MessageHubId messageHubId,
+    const pw::Function<bool(const EndpointInfo &)> &function) {
+  MessageRouter::MessageHubCallback *callback =
+      getCallbackFromMessageHubId(messageHubId);
+  if (callback == nullptr) {
+    LOGE("Failed to find message hub with ID %" PRIu64, messageHubId);
+    return false;
+  }
+
+  callback->forEachEndpoint(function);
+  return true;
+}
+
+void MessageRouter::forEachEndpoint(
+    const pw::Function<void(const MessageHubInfo &, const EndpointInfo &)>
+        &function) {
+  LockGuard<Mutex> lock(mMutex);
+
+  struct Context {
+    decltype(function) function;
+    MessageHubInfo &messageHubInfo;
+  };
+  for (MessageHubRecord &messageHubRecord : mMessageHubs) {
+    Context context = {
+        .function = function,
+        .messageHubInfo = messageHubRecord.info,
+    };
+
+    messageHubRecord.callback->forEachEndpoint(
+        [&context](const EndpointInfo &endpointInfo) {
+          context.function(context.messageHubInfo, endpointInfo);
+          return false;
+        });
+  }
+}
+
+std::optional<EndpointInfo> MessageRouter::getEndpointInfo(
+    MessageHubId messageHubId, EndpointId endpointId) {
+  MessageRouter::MessageHubCallback *callback =
+      getCallbackFromMessageHubId(messageHubId);
+  if (callback == nullptr) {
+    LOGE("Failed to get endpoint info for message hub with ID %" PRIu64
+         " and endpoint ID %" PRIu64 ": hub not found",
+         messageHubId, endpointId);
+    return std::nullopt;
+  }
+
+  return callback->getEndpointInfo(endpointId);
+}
+
+void MessageRouter::forEachMessageHub(
+    const pw::Function<bool(const MessageHubInfo &)> &function) {
+  LockGuard<Mutex> lock(mMutex);
+  for (MessageHubRecord &messageHubRecord : mMessageHubs) {
+    function(messageHubRecord.info);
+  }
+}
+
+bool MessageRouter::unregisterMessageHub(MessageHubId fromMessageHubId) {
+  DynamicVector<std::pair<MessageHubCallback *, Session>> sessionsToDestroy;
+
+  {
+    LockGuard<Mutex> lock(mMutex);
+
+    bool success = false;
+    for (MessageHubRecord &messageHubRecord : mMessageHubs) {
+      if (messageHubRecord.info.id == fromMessageHubId) {
+        mMessageHubs.erase(&messageHubRecord);
+        success = true;
+        break;
+      }
+    }
+    if (!success) {
+      return false;
+    }
+
+    for (size_t i = 0; i < mSessions.size();) {
+      Session &session = mSessions[i];
+      bool initiatorIsFromHub =
+          session.initiator.messageHubId == fromMessageHubId;
+      bool peerIsFromHub = session.peer.messageHubId == fromMessageHubId;
+
+      if (initiatorIsFromHub || peerIsFromHub) {
+        MessageHubCallback *callback = getCallbackFromMessageHubIdLocked(
+            initiatorIsFromHub ? session.peer.messageHubId
+                               : session.initiator.messageHubId);
+        sessionsToDestroy.push_back(std::make_pair(callback, session));
+        mSessions.erase(&mSessions[i]);
+      } else {
+        ++i;
+      }
+    }
+  }
+
+  for (auto [callback, session] : sessionsToDestroy) {
+    if (callback != nullptr) {
+      callback->onSessionClosed(session);
+    }
+  }
+  return true;
+}
+
+SessionId MessageRouter::openSession(MessageHubId fromMessageHubId,
+                                     EndpointId fromEndpointId,
+                                     MessageHubId toMessageHubId,
+                                     EndpointId toEndpointId) {
+  if (fromMessageHubId == toMessageHubId) {
+    LOGE(
+        "Failed to open session: initiator and peer message hubs are the "
+        "same");
+    return SESSION_ID_INVALID;
+  }
+
+  MessageRouter::MessageHubCallback *initiatorCallback =
+      getCallbackFromMessageHubId(fromMessageHubId);
+  MessageRouter::MessageHubCallback *peerCallback =
+      getCallbackFromMessageHubId(toMessageHubId);
+  if (initiatorCallback == nullptr || peerCallback == nullptr) {
+    LOGE("Failed to open session: initiator or peer message hub not found");
+    return SESSION_ID_INVALID;
+  }
+
+  if (!checkIfEndpointExists(initiatorCallback, fromEndpointId)) {
+    LOGE("Failed to open session: endpoint with ID %" PRIu64
+         " not found in message hub with ID %" PRIu64,
+         fromEndpointId, fromMessageHubId);
+    return SESSION_ID_INVALID;
+  }
+
+  if (!checkIfEndpointExists(peerCallback, toEndpointId)) {
+    LOGE("Failed to open session: endpoint with ID %" PRIu64
+         " not found in message hub with ID %" PRIu64,
+         toEndpointId, toMessageHubId);
+    return SESSION_ID_INVALID;
+  }
+
+  {
+    LockGuard<Mutex> lock(mMutex);
+    if (mSessions.full()) {
+      LOGE("Failed to open session: maximum number of sessions reached");
+      return SESSION_ID_INVALID;
+    }
+
+    Session insertSession = {
+        .sessionId = mNextSessionId,
+        .initiator = {.messageHubId = fromMessageHubId,
+                      .endpointId = fromEndpointId},
+        .peer = {.messageHubId = toMessageHubId, .endpointId = toEndpointId},
+    };
+
+    for (Session &session : mSessions) {
+      if (session.isEquivalent(insertSession)) {
+        LOGD("Session with ID %" PRIu16 " already exists", session.sessionId);
+        return session.sessionId;
+      }
+    }
+
+    mSessions.push_back(std::move(insertSession));
+    return mNextSessionId++;
+  }
+}
+
+bool MessageRouter::closeSession(MessageHubId fromMessageHubId,
+                                 SessionId sessionId) {
+  Session session;
+  MessageRouter::MessageHubCallback *initiatorCallback = nullptr;
+  MessageRouter::MessageHubCallback *peerCallback = nullptr;
+  {
+    LockGuard<Mutex> lock(mMutex);
+
+    std::optional<size_t> index =
+        findSessionIndexLocked(fromMessageHubId, sessionId);
+    if (!index.has_value()) {
+      LOGE("Failed to close session with ID %" PRIu16 ": session not found",
+           sessionId);
+      return false;
+    }
+
+    session = mSessions[*index];
+    initiatorCallback =
+        getCallbackFromMessageHubIdLocked(session.initiator.messageHubId);
+    peerCallback = getCallbackFromMessageHubIdLocked(session.peer.messageHubId);
+    mSessions.erase(&mSessions[*index]);
+  }
+
+  if (initiatorCallback != nullptr) {
+    initiatorCallback->onSessionClosed(session);
+  }
+  if (peerCallback != nullptr) {
+    peerCallback->onSessionClosed(session);
+  }
+  return true;
+}
+
+std::optional<Session> MessageRouter::getSessionWithId(
+    MessageHubId fromMessageHubId, SessionId sessionId) {
+  LockGuard<Mutex> lock(mMutex);
+
+  std::optional<size_t> index =
+      findSessionIndexLocked(fromMessageHubId, sessionId);
+  return index.has_value() ? std::optional<Session>(mSessions[*index])
+                           : std::nullopt;
+}
+
+bool MessageRouter::sendMessage(pw::UniquePtr<std::byte[]> &&data,
+                                size_t length, uint32_t messageType,
+                                uint32_t messagePermissions,
+                                SessionId sessionId,
+                                MessageHubId fromMessageHubId) {
+  MessageRouter::MessageHubCallback *receiverCallback = nullptr;
+  Session session;
+  {
+    LockGuard<Mutex> lock(mMutex);
+
+    std::optional<size_t> index =
+        findSessionIndexLocked(fromMessageHubId, sessionId);
+    if (!index.has_value()) {
+      LOGE("Failed to send message: session with ID %" PRIu16 " not found",
+           sessionId);
+      return false;
+    }
+
+    session = mSessions[*index];
+    receiverCallback = getCallbackFromMessageHubIdLocked(
+        session.initiator.messageHubId == fromMessageHubId
+            ? session.peer.messageHubId
+            : session.initiator.messageHubId);
+  }
+
+  bool success = false;
+  if (receiverCallback != nullptr) {
+    success = receiverCallback->onMessageReceived(
+        std::move(data), length, messageType, messagePermissions, session,
+        session.initiator.messageHubId == fromMessageHubId);
+  }
+
+  if (!success) {
+    closeSession(fromMessageHubId, sessionId);
+  }
+  return success;
+}
+
+const MessageRouter::MessageHubRecord *MessageRouter::getMessageHubRecordLocked(
+    MessageHubId messageHubId) {
+  for (MessageHubRecord &messageHubRecord : mMessageHubs) {
+    if (messageHubRecord.info.id == messageHubId) {
+      return &messageHubRecord;
+    }
+  }
+  return nullptr;
+}
+
+std::optional<size_t> MessageRouter::findSessionIndexLocked(
+    MessageHubId fromMessageHubId, SessionId sessionId) {
+  for (size_t i = 0; i < mSessions.size(); ++i) {
+    if (mSessions[i].sessionId == sessionId) {
+      if (mSessions[i].initiator.messageHubId == fromMessageHubId ||
+          mSessions[i].peer.messageHubId == fromMessageHubId) {
+        return i;
+      }
+
+      LOGE("Hub mismatch for session with ID %" PRIu16
+           ": requesting hub ID %" PRIu64
+           " but session is between hubs %" PRIu64 " and %" PRIu64,
+           sessionId, fromMessageHubId, mSessions[i].initiator.messageHubId,
+           mSessions[i].peer.messageHubId);
+      break;
+    }
+  }
+  return std::nullopt;
+}
+
+MessageRouter::MessageHubCallback *MessageRouter::getCallbackFromMessageHubId(
+    MessageHubId messageHubId) {
+  LockGuard<Mutex> lock(mMutex);
+  return getCallbackFromMessageHubIdLocked(messageHubId);
+}
+
+MessageRouter::MessageHubCallback *
+MessageRouter::getCallbackFromMessageHubIdLocked(MessageHubId messageHubId) {
+  const MessageHubRecord *messageHubRecord =
+      getMessageHubRecordLocked(messageHubId);
+  return messageHubRecord == nullptr ? nullptr : messageHubRecord->callback;
+}
+
+bool MessageRouter::checkIfEndpointExists(
+    MessageRouter::MessageHubCallback *callback, EndpointId endpointId) {
+  struct EndpointContext {
+    EndpointId endpointId;
+    bool foundEndpoint = false;
+  };
+  EndpointContext context = {
+      .endpointId = endpointId,
+  };
+
+  callback->forEachEndpoint([&context](const EndpointInfo &endpointInfo) {
+    if (context.endpointId == endpointInfo.id) {
+      context.foundEndpoint = true;
+      return true;
+    }
+    return false;
+  });
+  return context.foundEndpoint;
+}
+
+}  // namespace chre::message
diff --git a/util/tests/array_queue_test.cc b/util/tests/array_queue_test.cc
index bc55d19..ed8e2c6 100644
--- a/util/tests/array_queue_test.cc
+++ b/util/tests/array_queue_test.cc
@@ -36,17 +36,20 @@
  public:
   FakeElement() {
     constructor_count++;
-  };
+  }
+
   FakeElement(int i) {
     val_ = i;
     constructor_count++;
-  };
+  }
+
   ~FakeElement() {
     total_destructor_count++;
     if (val_ >= 0 && val_ < kMaxTestCapacity) {
       destructor_count[val_]++;
     }
-  };
+  }
+
   void setValue(int i) {
     val_ = i;
   }
diff --git a/util/tests/atomic_spsc_queue_test.cc b/util/tests/atomic_spsc_queue_test.cc
index 2915af2..8ebb27a 100644
--- a/util/tests/atomic_spsc_queue_test.cc
+++ b/util/tests/atomic_spsc_queue_test.cc
@@ -37,17 +37,20 @@
  public:
   FakeElement() {
     constructor_count++;
-  };
+  }
+
   FakeElement(int i) {
     val_ = i;
     constructor_count++;
-  };
+  }
+
   ~FakeElement() {
     total_destructor_count++;
     if (val_ >= 0 && val_ < kMaxTestCapacity) {
       destructor_count[val_]++;
     }
-  };
+  }
+
   void setValue(int i) {
     val_ = i;
   }
diff --git a/util/tests/blocking_queue_test.cc b/util/tests/blocking_queue_test.cc
index a05f657..461a7bc 100644
--- a/util/tests/blocking_queue_test.cc
+++ b/util/tests/blocking_queue_test.cc
@@ -17,7 +17,7 @@
 #include "gtest/gtest.h"
 
 #include "chre/util/blocking_segmented_queue.h"
-#include "chre/util/fixed_size_blocking_queue.h"
+#include "chre/util/system/fixed_size_blocking_queue.h"
 #include "chre/util/unique_ptr.h"
 
 using chre::BlockingSegmentedQueue;
diff --git a/util/tests/dynamic_vector_test.cc b/util/tests/dynamic_vector_test.cc
index e69bd4c..80befe1 100644
--- a/util/tests/dynamic_vector_test.cc
+++ b/util/tests/dynamic_vector_test.cc
@@ -38,7 +38,8 @@
     if (mValue >= 0) {
       gDestructorCount[mValue]++;
     }
-  };
+  }
+
   void setValue(int value) {
     mValue = value;
   }
diff --git a/util/tests/fixed_size_vector_test.cc b/util/tests/fixed_size_vector_test.cc
index 6bdffc2..4145658 100644
--- a/util/tests/fixed_size_vector_test.cc
+++ b/util/tests/fixed_size_vector_test.cc
@@ -30,7 +30,8 @@
     if (mValue >= 0) {
       destructor_count[mValue]++;
     }
-  };
+  }
+
   void setValue(int value) {
     mValue = value;
   }
diff --git a/util/tests/message_router_test.cc b/util/tests/message_router_test.cc
new file mode 100644
index 0000000..8c6f875
--- /dev/null
+++ b/util/tests/message_router_test.cc
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pw_allocator/allocator.h>
+#include <pw_allocator/capability.h>
+#include <pw_allocator/unique_ptr.h>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <utility>
+
+#include "chre/util/dynamic_vector.h"
+#include "chre/util/system/message_common.h"
+#include "chre/util/system/message_router.h"
+#include "chre/util/system/message_router_callback_allocator.h"
+#include "chre_api/chre.h"
+#include "gtest/gtest.h"
+
+namespace chre::message {
+namespace {
+
+constexpr size_t kMaxMessageHubs = 3;
+constexpr size_t kMaxSessions = 10;
+constexpr size_t kMaxFreeCallbackRecords = kMaxSessions * 2;
+constexpr size_t kNumEndpoints = 3;
+
+const EndpointInfo kEndpointInfos[kNumEndpoints] = {
+    EndpointInfo(/* id= */ 1, /* name= */ "endpoint1", /* version= */ 1,
+                 EndpointType::NANOAPP, CHRE_MESSAGE_PERMISSION_NONE),
+    EndpointInfo(/* id= */ 2, /* name= */ "endpoint2", /* version= */ 10,
+                 EndpointType::HOST_NATIVE, CHRE_MESSAGE_PERMISSION_BLE),
+    EndpointInfo(/* id= */ 3, /* name= */ "endpoint3", /* version= */ 100,
+                 EndpointType::GENERIC, CHRE_MESSAGE_PERMISSION_AUDIO)};
+
+class TestAllocator : public pw::Allocator {
+ public:
+  static constexpr Capabilities kCapabilities = 0;
+
+  TestAllocator() : pw::Allocator(kCapabilities) {}
+
+  virtual void *DoAllocate(Layout layout) override {
+    if (layout.alignment() > alignof(std::max_align_t)) {
+      void *ptr;
+      return posix_memalign(&ptr, layout.alignment(), layout.size()) == 0
+                 ? ptr
+                 : nullptr;
+    } else {
+      return malloc(layout.size());
+    }
+  }
+
+  virtual void DoDeallocate(void *ptr) override {
+    free(ptr);
+  }
+};
+
+class MessageRouterTest : public ::testing::Test {
+ protected:
+  void SetUp() override {}
+
+  TestAllocator mAllocator;
+};
+
+//! Base class for MessageHubCallbacks used in tests
+class MessageHubCallbackBase : public MessageRouter::MessageHubCallback {
+ public:
+  void forEachEndpoint(
+      const pw::Function<bool(const EndpointInfo &)> &function) override {
+    for (const EndpointInfo &endpointInfo : kEndpointInfos) {
+      if (function(endpointInfo)) {
+        return;
+      }
+    }
+  }
+
+  std::optional<EndpointInfo> getEndpointInfo(EndpointId endpointId) override {
+    for (const EndpointInfo &endpointInfo : kEndpointInfos) {
+      if (endpointInfo.id == endpointId) {
+        return endpointInfo;
+      }
+    }
+    return std::nullopt;
+  }
+};
+
+//! MessageHubCallback that stores the data passed to onMessageReceived and
+//! onSessionClosed
+class MessageHubCallbackStoreData : public MessageHubCallbackBase {
+ public:
+  MessageHubCallbackStoreData(Message *message, Session *session)
+      : mMessage(message), mSession(session) {}
+
+  bool onMessageReceived(pw::UniquePtr<std::byte[]> &&data, size_t length,
+                         uint32_t messageType, uint32_t messagePermissions,
+                         const Session &session,
+                         bool sentBySessionInitiator) override {
+    if (mMessage != nullptr) {
+      mMessage->sender = sentBySessionInitiator ? session.initiator
+                                                : session.peer;
+      mMessage->recipient =
+          sentBySessionInitiator ? session.peer : session.initiator;
+      mMessage->sessionId = session.sessionId;
+      mMessage->data = std::move(data);
+      mMessage->length = length;
+      mMessage->messageType = messageType;
+      mMessage->messagePermissions = messagePermissions;
+    }
+    return true;
+  }
+
+  void onSessionClosed(const Session &session) override {
+    if (mSession != nullptr) {
+      *mSession = session;
+    }
+  }
+
+ private:
+  Message *mMessage;
+  Session *mSession;
+};
+
+//! MessageHubCallback that always fails to process messages
+class MessageHubCallbackAlwaysFails : public MessageHubCallbackBase {
+ public:
+  MessageHubCallbackAlwaysFails(bool *wasMessageReceivedCalled,
+                                bool *wasSessionClosedCalled)
+      : mWasMessageReceivedCalled(wasMessageReceivedCalled),
+        mWasSessionClosedCalled(wasSessionClosedCalled) {}
+
+  bool onMessageReceived(pw::UniquePtr<std::byte[]> && /* data */,
+                         size_t /* length */, uint32_t /* messageType */,
+                         uint32_t /* messagePermissions */,
+                         const Session & /* session */,
+                         bool /* sentBySessionInitiator */) override {
+    if (mWasMessageReceivedCalled != nullptr) {
+      *mWasMessageReceivedCalled = true;
+    }
+    return false;
+  }
+
+  void onSessionClosed(const Session & /* session */) override {
+    if (mWasSessionClosedCalled != nullptr) {
+      *mWasSessionClosedCalled = true;
+    }
+  }
+
+ private:
+  bool *mWasMessageReceivedCalled;
+  bool *mWasSessionClosedCalled;
+};
+
+//! MessageHubCallback that calls MessageHub APIs during callbacks
+class MessageHubCallbackCallsMessageHubApisDuringCallback
+    : public MessageHubCallbackBase {
+ public:
+  bool onMessageReceived(pw::UniquePtr<std::byte[]> && /* data */,
+                         size_t /* length */, uint32_t /* messageType */,
+                         uint32_t /* messagePermissions */,
+                         const Session & /* session */,
+                         bool /* sentBySessionInitiator */) override {
+    if (mMessageHub != nullptr) {
+      // Call a function that locks the MessageRouter mutex
+      mMessageHub->openSession(kEndpointInfos[0].id, mMessageHub->getId(),
+                               kEndpointInfos[1].id);
+    }
+    return true;
+  }
+
+  void onSessionClosed(const Session & /* session */) override {
+    if (mMessageHub != nullptr) {
+      // Call a function that locks the MessageRouter mutex
+      mMessageHub->openSession(kEndpointInfos[0].id, mMessageHub->getId(),
+                               kEndpointInfos[1].id);
+    }
+  }
+
+  void setMessageHub(MessageRouter::MessageHub *messageHub) {
+    mMessageHub = messageHub;
+  }
+
+ private:
+  MessageRouter::MessageHub *mMessageHub = nullptr;
+};
+
+TEST_F(MessageRouterTest, RegisterMessageHubNameIsUnique) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub1 =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub1.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback);
+  EXPECT_TRUE(messageHub2.has_value());
+
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_FALSE(messageHub3.has_value());
+}
+
+TEST_F(MessageRouterTest, RegisterMessageHubIdIsUnique) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub1 =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub1.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback);
+  EXPECT_TRUE(messageHub2.has_value());
+
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 1, callback);
+  EXPECT_FALSE(messageHub3.has_value());
+}
+
+TEST_F(MessageRouterTest, RegisterMessageHubGetListOfHubs) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub1 =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub1.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  DynamicVector<MessageHubInfo> messageHubs;
+  router.forEachMessageHub(
+      [&messageHubs](const MessageHubInfo &messageHubInfo) {
+        messageHubs.push_back(messageHubInfo);
+        return false;
+      });
+  EXPECT_EQ(messageHubs.size(), 3);
+  EXPECT_EQ(messageHubs[0].name, "hub1");
+  EXPECT_EQ(messageHubs[1].name, "hub2");
+  EXPECT_EQ(messageHubs[2].name, "hub3");
+  EXPECT_EQ(messageHubs[0].id, 1);
+  EXPECT_EQ(messageHubs[1].id, 2);
+  EXPECT_EQ(messageHubs[2].id, 3);
+  EXPECT_EQ(messageHubs[0].id, messageHub1->getId());
+  EXPECT_EQ(messageHubs[1].id, messageHub2->getId());
+  EXPECT_EQ(messageHubs[2].id, messageHub3->getId());
+}
+
+TEST_F(MessageRouterTest, RegisterMessageHubGetListOfHubsWithUnregister) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub1 =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub1.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  DynamicVector<MessageHubInfo> messageHubs;
+  router.forEachMessageHub(
+      [&messageHubs](const MessageHubInfo &messageHubInfo) {
+        messageHubs.push_back(messageHubInfo);
+        return false;
+      });
+  EXPECT_EQ(messageHubs.size(), 3);
+  EXPECT_EQ(messageHubs[0].name, "hub1");
+  EXPECT_EQ(messageHubs[1].name, "hub2");
+  EXPECT_EQ(messageHubs[2].name, "hub3");
+  EXPECT_EQ(messageHubs[0].id, 1);
+  EXPECT_EQ(messageHubs[1].id, 2);
+  EXPECT_EQ(messageHubs[2].id, 3);
+  EXPECT_EQ(messageHubs[0].id, messageHub1->getId());
+  EXPECT_EQ(messageHubs[1].id, messageHub2->getId());
+  EXPECT_EQ(messageHubs[2].id, messageHub3->getId());
+
+  // Clear messageHubs and reset messageHub2
+  messageHubs.clear();
+  messageHub2.reset();
+
+  router.forEachMessageHub(
+      [&messageHubs](const MessageHubInfo &messageHubInfo) {
+        messageHubs.push_back(messageHubInfo);
+        return false;
+      });
+  EXPECT_EQ(messageHubs.size(), 2);
+  EXPECT_EQ(messageHubs[0].name, "hub1");
+  EXPECT_EQ(messageHubs[1].name, "hub3");
+  EXPECT_EQ(messageHubs[0].id, 1);
+  EXPECT_EQ(messageHubs[1].id, 3);
+  EXPECT_EQ(messageHubs[0].id, messageHub1->getId());
+  EXPECT_EQ(messageHubs[1].id, messageHub3->getId());
+}
+
+TEST_F(MessageRouterTest, RegisterMessageHubTooManyFails) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  static_assert(kMaxMessageHubs == 3);
+  constexpr const char *kNames[3] = {"hub1", "hub2", "hub3"};
+
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  MessageRouter::MessageHub messageHubs[kMaxMessageHubs];
+  for (size_t i = 0; i < kMaxMessageHubs; ++i) {
+    std::optional<MessageRouter::MessageHub> messageHub =
+        router.registerMessageHub(kNames[i], /* id= */ i, callback);
+    EXPECT_TRUE(messageHub.has_value());
+    messageHubs[i] = std::move(*messageHub);
+  }
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("shouldfail", /* id= */ kMaxMessageHubs * 2,
+                                callback);
+  EXPECT_FALSE(messageHub.has_value());
+}
+
+TEST_F(MessageRouterTest, GetEndpointInfo) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub1 =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub1.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  for (size_t i = 0; i < kNumEndpoints; ++i) {
+    EXPECT_EQ(
+        router.getEndpointInfo(messageHub1->getId(), kEndpointInfos[i].id),
+        kEndpointInfos[i]);
+    EXPECT_EQ(
+        router.getEndpointInfo(messageHub2->getId(), kEndpointInfos[i].id),
+        kEndpointInfos[i]);
+    EXPECT_EQ(
+        router.getEndpointInfo(messageHub3->getId(), kEndpointInfos[i].id),
+        kEndpointInfos[i]);
+  }
+}
+
+TEST_F(MessageRouterTest, RegisterSessionTwoDifferentMessageHubs) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  Session sessionFromCallback1;
+  Session sessionFromCallback2;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       &sessionFromCallback1);
+  MessageHubCallbackStoreData callback2(/* message= */ nullptr,
+                                        &sessionFromCallback2);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Get session from messageHub and compare it with messageHub2
+  std::optional<Session> sessionAfterRegistering =
+      messageHub->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering.has_value());
+  EXPECT_EQ(sessionAfterRegistering->sessionId, sessionId);
+  EXPECT_EQ(sessionAfterRegistering->initiator.messageHubId,
+            messageHub->getId());
+  EXPECT_EQ(sessionAfterRegistering->initiator.endpointId,
+            kEndpointInfos[0].id);
+  EXPECT_EQ(sessionAfterRegistering->peer.messageHubId, messageHub2->getId());
+  EXPECT_EQ(sessionAfterRegistering->peer.endpointId, kEndpointInfos[1].id);
+  std::optional<Session> sessionAfterRegistering2 =
+      messageHub2->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering2.has_value());
+  EXPECT_EQ(*sessionAfterRegistering, *sessionAfterRegistering2);
+
+  // Close the session and verify it is closed on both message hubs
+  EXPECT_NE(*sessionAfterRegistering, sessionFromCallback1);
+  EXPECT_NE(*sessionAfterRegistering, sessionFromCallback2);
+  EXPECT_TRUE(messageHub->closeSession(sessionId));
+  EXPECT_EQ(*sessionAfterRegistering, sessionFromCallback1);
+  EXPECT_EQ(*sessionAfterRegistering, sessionFromCallback2);
+  EXPECT_FALSE(messageHub->getSessionWithId(sessionId).has_value());
+  EXPECT_FALSE(messageHub2->getSessionWithId(sessionId).has_value());
+}
+
+TEST_F(MessageRouterTest, UnregisterMessageHubCausesSessionClosed) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  Session sessionFromCallback1;
+  Session sessionFromCallback2;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       &sessionFromCallback1);
+  MessageHubCallbackStoreData callback2(/* message= */ nullptr,
+                                        &sessionFromCallback2);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Get session from messageHub and compare it with messageHub2
+  std::optional<Session> sessionAfterRegistering =
+      messageHub->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering.has_value());
+  EXPECT_EQ(sessionAfterRegistering->sessionId, sessionId);
+  EXPECT_EQ(sessionAfterRegistering->initiator.messageHubId,
+            messageHub->getId());
+  EXPECT_EQ(sessionAfterRegistering->initiator.endpointId,
+            kEndpointInfos[0].id);
+  EXPECT_EQ(sessionAfterRegistering->peer.messageHubId, messageHub2->getId());
+  EXPECT_EQ(sessionAfterRegistering->peer.endpointId, kEndpointInfos[1].id);
+  std::optional<Session> sessionAfterRegistering2 =
+      messageHub2->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering2.has_value());
+  EXPECT_EQ(*sessionAfterRegistering, *sessionAfterRegistering2);
+
+  // Close the session and verify it is closed on the other hub
+  EXPECT_NE(*sessionAfterRegistering, sessionFromCallback1);
+  messageHub2.reset();
+  EXPECT_EQ(*sessionAfterRegistering, sessionFromCallback1);
+  EXPECT_FALSE(messageHub->getSessionWithId(sessionId).has_value());
+}
+
+TEST_F(MessageRouterTest, RegisterSessionSameMessageHubInvalid) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  Session sessionFromCallback1;
+  Session sessionFromCallback2;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       &sessionFromCallback1);
+  MessageHubCallbackStoreData callback2(/* message= */ nullptr,
+                                        &sessionFromCallback2);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+
+  // Open session from messageHub:2 to messageHub:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[1].id, messageHub->getId(), kEndpointInfos[1].id);
+  EXPECT_EQ(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub:1 to messageHub:3
+  sessionId = messageHub->openSession(kEndpointInfos[0].id, messageHub->getId(),
+                                      kEndpointInfos[2].id);
+  EXPECT_EQ(sessionId, SESSION_ID_INVALID);
+}
+
+TEST_F(MessageRouterTest, RegisterSessionDifferentMessageHubsSameEndpoints) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  Session sessionFromCallback1;
+  Session sessionFromCallback2;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       &sessionFromCallback1);
+  MessageHubCallbackStoreData callback2(/* message= */ nullptr,
+                                        &sessionFromCallback2);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+
+  // Open session from messageHub:1 to messageHub:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[0].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+}
+
+TEST_F(MessageRouterTest,
+       RegisterSessionTwoDifferentMessageHubsInvalidEndpoint) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  MessageHubCallbackStoreData callback2(/* message= */ nullptr,
+                                        /* session= */ nullptr);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+
+  // Open session from messageHub with other non-registered endpoint - not
+  // valid
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), /* toEndpointId= */ 10);
+  EXPECT_EQ(sessionId, SESSION_ID_INVALID);
+}
+
+TEST_F(MessageRouterTest, ThirdMessageHubTriesToFindOthersSession) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  Session sessionFromCallback1;
+  Session sessionFromCallback2;
+  Session sessionFromCallback3;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       &sessionFromCallback1);
+  MessageHubCallbackStoreData callback2(/* message= */ nullptr,
+                                        &sessionFromCallback2);
+  MessageHubCallbackStoreData callback3(/* message= */ nullptr,
+                                        &sessionFromCallback3);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback3);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Get session from messageHub and compare it with messageHub2
+  std::optional<Session> sessionAfterRegistering =
+      messageHub->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering.has_value());
+  EXPECT_EQ(sessionAfterRegistering->sessionId, sessionId);
+  EXPECT_EQ(sessionAfterRegistering->initiator.messageHubId,
+            messageHub->getId());
+  EXPECT_EQ(sessionAfterRegistering->initiator.endpointId,
+            kEndpointInfos[0].id);
+  EXPECT_EQ(sessionAfterRegistering->peer.messageHubId, messageHub2->getId());
+  EXPECT_EQ(sessionAfterRegistering->peer.endpointId, kEndpointInfos[1].id);
+  std::optional<Session> sessionAfterRegistering2 =
+      messageHub2->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering2.has_value());
+  EXPECT_EQ(*sessionAfterRegistering, *sessionAfterRegistering2);
+
+  // Third message hub tries to find the session - not found
+  EXPECT_FALSE(messageHub3->getSessionWithId(sessionId).has_value());
+  // Third message hub tries to close the session - not found
+  EXPECT_FALSE(messageHub3->closeSession(sessionId));
+
+  // Get session from messageHub and compare it with messageHub2 again
+  sessionAfterRegistering = messageHub->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering.has_value());
+  EXPECT_EQ(sessionAfterRegistering->sessionId, sessionId);
+  EXPECT_EQ(sessionAfterRegistering->initiator.messageHubId,
+            messageHub->getId());
+  EXPECT_EQ(sessionAfterRegistering->initiator.endpointId,
+            kEndpointInfos[0].id);
+  EXPECT_EQ(sessionAfterRegistering->peer.messageHubId, messageHub2->getId());
+  EXPECT_EQ(sessionAfterRegistering->peer.endpointId, kEndpointInfos[1].id);
+  sessionAfterRegistering2 = messageHub2->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering2.has_value());
+  EXPECT_EQ(*sessionAfterRegistering, *sessionAfterRegistering2);
+
+  // Close the session and verify it is closed on both message hubs
+  EXPECT_NE(*sessionAfterRegistering, sessionFromCallback1);
+  EXPECT_NE(*sessionAfterRegistering, sessionFromCallback2);
+  EXPECT_TRUE(messageHub->closeSession(sessionId));
+  EXPECT_EQ(*sessionAfterRegistering, sessionFromCallback1);
+  EXPECT_EQ(*sessionAfterRegistering, sessionFromCallback2);
+  EXPECT_NE(*sessionAfterRegistering, sessionFromCallback3);
+  EXPECT_FALSE(messageHub->getSessionWithId(sessionId).has_value());
+  EXPECT_FALSE(messageHub2->getSessionWithId(sessionId).has_value());
+}
+
+TEST_F(MessageRouterTest, ThreeMessageHubsAndThreeSessions) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  MessageHubCallbackStoreData callback2(/* message= */ nullptr,
+                                        /* session= */ nullptr);
+  MessageHubCallbackStoreData callback3(/* message= */ nullptr,
+                                        /* session= */ nullptr);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback3);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub2:2 to messageHub3:3
+  SessionId sessionId2 = messageHub2->openSession(
+      kEndpointInfos[1].id, messageHub3->getId(), kEndpointInfos[2].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub3:3 to messageHub1:1
+  SessionId sessionId3 = messageHub3->openSession(
+      kEndpointInfos[2].id, messageHub->getId(), kEndpointInfos[0].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Get sessions and compare
+  // Find session: MessageHub1:1 -> MessageHub2:2
+  std::optional<Session> sessionAfterRegistering =
+      messageHub->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering.has_value());
+  std::optional<Session> sessionAfterRegistering2 =
+      messageHub2->getSessionWithId(sessionId);
+  EXPECT_TRUE(sessionAfterRegistering2.has_value());
+  EXPECT_FALSE(messageHub3->getSessionWithId(sessionId).has_value());
+  EXPECT_EQ(*sessionAfterRegistering, *sessionAfterRegistering2);
+
+  // Find session: MessageHub2:2 -> MessageHub3:3
+  sessionAfterRegistering = messageHub2->getSessionWithId(sessionId2);
+  EXPECT_TRUE(sessionAfterRegistering.has_value());
+  sessionAfterRegistering2 = messageHub3->getSessionWithId(sessionId2);
+  EXPECT_TRUE(sessionAfterRegistering2.has_value());
+  EXPECT_FALSE(messageHub->getSessionWithId(sessionId2).has_value());
+  EXPECT_EQ(*sessionAfterRegistering, *sessionAfterRegistering2);
+
+  // Find session: MessageHub3:3 -> MessageHub1:1
+  sessionAfterRegistering = messageHub3->getSessionWithId(sessionId3);
+  EXPECT_TRUE(sessionAfterRegistering.has_value());
+  sessionAfterRegistering2 = messageHub->getSessionWithId(sessionId3);
+  EXPECT_TRUE(sessionAfterRegistering2.has_value());
+  EXPECT_FALSE(messageHub2->getSessionWithId(sessionId3).has_value());
+  EXPECT_EQ(*sessionAfterRegistering, *sessionAfterRegistering2);
+
+  // Close sessions from receivers and verify they are closed on all hubs
+  EXPECT_TRUE(messageHub2->closeSession(sessionId));
+  EXPECT_TRUE(messageHub3->closeSession(sessionId2));
+  EXPECT_TRUE(messageHub->closeSession(sessionId3));
+  for (SessionId id : {sessionId, sessionId2, sessionId3}) {
+    EXPECT_FALSE(messageHub->getSessionWithId(id).has_value());
+    EXPECT_FALSE(messageHub2->getSessionWithId(id).has_value());
+    EXPECT_FALSE(messageHub3->getSessionWithId(id).has_value());
+  }
+}
+
+TEST_F(MessageRouterTest, SendMessageToSession) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  constexpr size_t kMessageSize = 5;
+  pw::UniquePtr<std::byte[]> messageData =
+      mAllocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+
+  Message messageFromCallback1;
+  Message messageFromCallback2;
+  Message messageFromCallback3;
+  Session sessionFromCallback1;
+  Session sessionFromCallback2;
+  Session sessionFromCallback3;
+  MessageHubCallbackStoreData callback(&messageFromCallback1,
+                                       &sessionFromCallback1);
+  MessageHubCallbackStoreData callback2(&messageFromCallback2,
+                                        &sessionFromCallback2);
+  MessageHubCallbackStoreData callback3(&messageFromCallback3,
+                                        &sessionFromCallback3);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback3);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub2:2 to messageHub3:3
+  SessionId sessionId2 = messageHub2->openSession(
+      kEndpointInfos[1].id, messageHub3->getId(), kEndpointInfos[2].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub3:3 to messageHub1:1
+  SessionId sessionId3 = messageHub3->openSession(
+      kEndpointInfos[2].id, messageHub->getId(), kEndpointInfos[0].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Send message from messageHub:1 to messageHub2:2
+  ASSERT_TRUE(messageHub->sendMessage(std::move(messageData), kMessageSize,
+                                      /* messageType= */ 1,
+                                      /* messagePermissions= */ 0, sessionId));
+  EXPECT_EQ(messageFromCallback2.sessionId, sessionId);
+  EXPECT_EQ(messageFromCallback2.sender.messageHubId, messageHub->getId());
+  EXPECT_EQ(messageFromCallback2.sender.endpointId, kEndpointInfos[0].id);
+  EXPECT_EQ(messageFromCallback2.recipient.messageHubId, messageHub2->getId());
+  EXPECT_EQ(messageFromCallback2.recipient.endpointId, kEndpointInfos[1].id);
+  EXPECT_EQ(messageFromCallback2.messageType, 1);
+  EXPECT_EQ(messageFromCallback2.messagePermissions, 0);
+  EXPECT_EQ(messageFromCallback2.length, kMessageSize);
+  for (size_t i = 0; i < kMessageSize; ++i) {
+    EXPECT_EQ(messageFromCallback2.data[i], static_cast<std::byte>(i + 1));
+  }
+
+  messageData = mAllocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+
+  // Send message from messageHub2:2 to messageHub:1
+  ASSERT_TRUE(messageHub2->sendMessage(std::move(messageData), kMessageSize,
+                                       /* messageType= */ 2,
+                                       /* messagePermissions= */ 3, sessionId));
+  EXPECT_EQ(messageFromCallback1.sessionId, sessionId);
+  EXPECT_EQ(messageFromCallback1.sender.messageHubId, messageHub2->getId());
+  EXPECT_EQ(messageFromCallback1.sender.endpointId, kEndpointInfos[1].id);
+  EXPECT_EQ(messageFromCallback1.recipient.messageHubId, messageHub->getId());
+  EXPECT_EQ(messageFromCallback1.recipient.endpointId, kEndpointInfos[0].id);
+  EXPECT_EQ(messageFromCallback1.messageType, 2);
+  EXPECT_EQ(messageFromCallback1.messagePermissions, 3);
+  EXPECT_EQ(messageFromCallback1.length, kMessageSize);
+  for (size_t i = 0; i < kMessageSize; ++i) {
+    EXPECT_EQ(messageFromCallback1.data[i], static_cast<std::byte>(i + 1));
+  }
+}
+
+TEST_F(MessageRouterTest, SendMessageToSessionUsingPointerAndFreeCallback) {
+  struct FreeCallbackContext {
+    bool *freeCallbackCalled;
+    std::byte *message;
+    size_t length;
+  };
+
+  pw::Vector<
+      MessageRouterCallbackAllocator<FreeCallbackContext>::FreeCallbackRecord,
+      10>
+      freeCallbackRecords;
+  MessageRouterCallbackAllocator<FreeCallbackContext> allocator(
+      [](std::byte *message, size_t length,
+         const FreeCallbackContext &context) {
+        *context.freeCallbackCalled =
+            message == context.message && length == context.length;
+      },
+      freeCallbackRecords);
+
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  constexpr size_t kMessageSize = 5;
+  std::byte messageData[kMessageSize];
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+
+  Message messageFromCallback1;
+  Message messageFromCallback2;
+  Message messageFromCallback3;
+  Session sessionFromCallback1;
+  Session sessionFromCallback2;
+  Session sessionFromCallback3;
+  MessageHubCallbackStoreData callback(&messageFromCallback1,
+                                       &sessionFromCallback1);
+  MessageHubCallbackStoreData callback2(&messageFromCallback2,
+                                        &sessionFromCallback2);
+  MessageHubCallbackStoreData callback3(&messageFromCallback3,
+                                        &sessionFromCallback3);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback3);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub2:2 to messageHub3:3
+  SessionId sessionId2 = messageHub2->openSession(
+      kEndpointInfos[1].id, messageHub3->getId(), kEndpointInfos[2].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub3:3 to messageHub1:1
+  SessionId sessionId3 = messageHub3->openSession(
+      kEndpointInfos[2].id, messageHub->getId(), kEndpointInfos[0].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Send message from messageHub:1 to messageHub2:2
+  bool freeCallbackCalled = false;
+  FreeCallbackContext freeCallbackContext = {
+      .freeCallbackCalled = &freeCallbackCalled,
+      .message = messageData,
+      .length = kMessageSize,
+  };
+  pw::UniquePtr<std::byte[]> data = allocator.MakeUniqueArrayWithCallback(
+      messageData, kMessageSize, std::move(freeCallbackContext));
+  ASSERT_NE(data.get(), nullptr);
+
+  ASSERT_TRUE(messageHub->sendMessage(std::move(data), kMessageSize,
+                                      /* messageType= */ 1,
+                                      /* messagePermissions= */ 0, sessionId));
+  EXPECT_EQ(messageFromCallback2.sessionId, sessionId);
+  EXPECT_EQ(messageFromCallback2.sender.messageHubId, messageHub->getId());
+  EXPECT_EQ(messageFromCallback2.sender.endpointId, kEndpointInfos[0].id);
+  EXPECT_EQ(messageFromCallback2.recipient.messageHubId, messageHub2->getId());
+  EXPECT_EQ(messageFromCallback2.recipient.endpointId, kEndpointInfos[1].id);
+  EXPECT_EQ(messageFromCallback2.messageType, 1);
+  EXPECT_EQ(messageFromCallback2.messagePermissions, 0);
+  EXPECT_EQ(messageFromCallback2.length, kMessageSize);
+  for (size_t i = 0; i < kMessageSize; ++i) {
+    EXPECT_EQ(messageFromCallback2.data[i], static_cast<std::byte>(i + 1));
+  }
+
+  // Check if free callback was called
+  EXPECT_FALSE(freeCallbackCalled);
+  EXPECT_EQ(messageFromCallback2.data.get(), messageData);
+  messageFromCallback2.data.Reset();
+  EXPECT_TRUE(freeCallbackCalled);
+
+  // Send message from messageHub2:2 to messageHub:1
+  freeCallbackCalled = false;
+  FreeCallbackContext freeCallbackContext2 = {
+      .freeCallbackCalled = &freeCallbackCalled,
+      .message = messageData,
+      .length = kMessageSize,
+  };
+  data = allocator.MakeUniqueArrayWithCallback(messageData, kMessageSize,
+                                               std::move(freeCallbackContext2));
+  ASSERT_NE(data.get(), nullptr);
+
+  ASSERT_TRUE(messageHub2->sendMessage(std::move(data), kMessageSize,
+                                       /* messageType= */ 2,
+                                       /* messagePermissions= */ 3, sessionId));
+  EXPECT_EQ(messageFromCallback1.sessionId, sessionId);
+  EXPECT_EQ(messageFromCallback1.sender.messageHubId, messageHub2->getId());
+  EXPECT_EQ(messageFromCallback1.sender.endpointId, kEndpointInfos[1].id);
+  EXPECT_EQ(messageFromCallback1.recipient.messageHubId, messageHub->getId());
+  EXPECT_EQ(messageFromCallback1.recipient.endpointId, kEndpointInfos[0].id);
+  EXPECT_EQ(messageFromCallback1.messageType, 2);
+  EXPECT_EQ(messageFromCallback1.messagePermissions, 3);
+  EXPECT_EQ(messageFromCallback1.length, kMessageSize);
+  for (size_t i = 0; i < kMessageSize; ++i) {
+    EXPECT_EQ(messageFromCallback1.data[i], static_cast<std::byte>(i + 1));
+  }
+
+  // Check if free callback was called
+  EXPECT_FALSE(freeCallbackCalled);
+  EXPECT_EQ(messageFromCallback1.data.get(), messageData);
+  messageFromCallback1.data.Reset();
+  EXPECT_TRUE(freeCallbackCalled);
+}
+
+TEST_F(MessageRouterTest, SendMessageToSessionInvalidHubAndSession) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  constexpr size_t kMessageSize = 5;
+  pw::UniquePtr<std::byte[]> messageData =
+      mAllocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+
+  Message messageFromCallback1;
+  Message messageFromCallback2;
+  Message messageFromCallback3;
+  Session sessionFromCallback1;
+  Session sessionFromCallback2;
+  Session sessionFromCallback3;
+  MessageHubCallbackStoreData callback(&messageFromCallback1,
+                                       &sessionFromCallback1);
+  MessageHubCallbackStoreData callback2(&messageFromCallback2,
+                                        &sessionFromCallback2);
+  MessageHubCallbackStoreData callback3(&messageFromCallback3,
+                                        &sessionFromCallback3);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback3);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub2:2 to messageHub3:3
+  SessionId sessionId2 = messageHub2->openSession(
+      kEndpointInfos[1].id, messageHub3->getId(), kEndpointInfos[2].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub3:3 to messageHub1:1
+  SessionId sessionId3 = messageHub3->openSession(
+      kEndpointInfos[2].id, messageHub->getId(), kEndpointInfos[0].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Send message from messageHub:1 to messageHub2:2
+  EXPECT_FALSE(messageHub->sendMessage(std::move(messageData), kMessageSize,
+                                       /* messageType= */ 1,
+                                       /* messagePermissions= */ 0,
+                                       sessionId2));
+  EXPECT_FALSE(messageHub2->sendMessage(std::move(messageData), kMessageSize,
+                                        /* messageType= */ 2,
+                                        /* messagePermissions= */ 3,
+                                        sessionId3));
+  EXPECT_FALSE(messageHub3->sendMessage(std::move(messageData), kMessageSize,
+                                        /* messageType= */ 2,
+                                        /* messagePermissions= */ 3,
+                                        sessionId));
+}
+
+TEST_F(MessageRouterTest, SendMessageToSessionCallbackFailureClosesSession) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  constexpr size_t kMessageSize = 5;
+  pw::UniquePtr<std::byte[]> messageData =
+      mAllocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+
+  bool wasMessageReceivedCalled1 = false;
+  bool wasMessageReceivedCalled2 = false;
+  bool wasMessageReceivedCalled3 = false;
+  MessageHubCallbackAlwaysFails callback1(
+      &wasMessageReceivedCalled1,
+      /* wasSessionClosedCalled= */ nullptr);
+  MessageHubCallbackAlwaysFails callback2(
+      &wasMessageReceivedCalled2,
+      /* wasSessionClosedCalled= */ nullptr);
+  MessageHubCallbackAlwaysFails callback3(
+      &wasMessageReceivedCalled3,
+      /* wasSessionClosedCalled= */ nullptr);
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback1);
+  EXPECT_TRUE(messageHub.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback3);
+  EXPECT_TRUE(messageHub3.has_value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub2:2 to messageHub3:3
+  SessionId sessionId2 = messageHub2->openSession(
+      kEndpointInfos[1].id, messageHub3->getId(), kEndpointInfos[2].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub3:3 to messageHub1:1
+  SessionId sessionId3 = messageHub3->openSession(
+      kEndpointInfos[2].id, messageHub->getId(), kEndpointInfos[0].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Send message from messageHub2:2 to messageHub3:3
+  EXPECT_FALSE(wasMessageReceivedCalled1);
+  EXPECT_FALSE(wasMessageReceivedCalled2);
+  EXPECT_FALSE(wasMessageReceivedCalled3);
+  EXPECT_FALSE(messageHub->getSessionWithId(sessionId2).has_value());
+  EXPECT_TRUE(messageHub2->getSessionWithId(sessionId2).has_value());
+  EXPECT_TRUE(messageHub3->getSessionWithId(sessionId2).has_value());
+
+  EXPECT_FALSE(messageHub2->sendMessage(std::move(messageData), kMessageSize,
+                                        /* messageType= */ 1,
+                                        /* messagePermissions= */ 0,
+                                        sessionId2));
+  EXPECT_FALSE(wasMessageReceivedCalled1);
+  EXPECT_FALSE(wasMessageReceivedCalled2);
+  EXPECT_TRUE(wasMessageReceivedCalled3);
+  EXPECT_FALSE(messageHub->getSessionWithId(sessionId2).has_value());
+  EXPECT_FALSE(messageHub2->getSessionWithId(sessionId2).has_value());
+  EXPECT_FALSE(messageHub3->getSessionWithId(sessionId2).has_value());
+
+  // Try to send a message on the same session - should fail
+  wasMessageReceivedCalled1 = false;
+  wasMessageReceivedCalled2 = false;
+  wasMessageReceivedCalled3 = false;
+  messageData = mAllocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+  EXPECT_FALSE(messageHub2->sendMessage(std::move(messageData), kMessageSize,
+                                        /* messageType= */ 1,
+                                        /* messagePermissions= */ 0,
+                                        sessionId2));
+  messageData = mAllocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+  EXPECT_FALSE(messageHub3->sendMessage(std::move(messageData), kMessageSize,
+                                        /* messageType= */ 1,
+                                        /* messagePermissions= */ 0,
+                                        sessionId2));
+  EXPECT_FALSE(wasMessageReceivedCalled1);
+  EXPECT_FALSE(wasMessageReceivedCalled2);
+  EXPECT_FALSE(wasMessageReceivedCalled3);
+}
+
+TEST_F(MessageRouterTest, MessageHubCallbackCanCallOtherMessageHubAPIs) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  constexpr size_t kMessageSize = 5;
+  pw::UniquePtr<std::byte[]> messageData =
+      mAllocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+
+  MessageHubCallbackCallsMessageHubApisDuringCallback callback;
+  MessageHubCallbackCallsMessageHubApisDuringCallback callback2;
+  MessageHubCallbackCallsMessageHubApisDuringCallback callback3;
+
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+  callback.setMessageHub(&messageHub.value());
+  std::optional<MessageRouter::MessageHub> messageHub2 =
+      router.registerMessageHub("hub2", /* id= */ 2, callback2);
+  EXPECT_TRUE(messageHub2.has_value());
+  callback2.setMessageHub(&messageHub2.value());
+  std::optional<MessageRouter::MessageHub> messageHub3 =
+      router.registerMessageHub("hub3", /* id= */ 3, callback3);
+  EXPECT_TRUE(messageHub3.has_value());
+  callback3.setMessageHub(&messageHub3.value());
+
+  // Open session from messageHub:1 to messageHub2:2
+  SessionId sessionId = messageHub->openSession(
+      kEndpointInfos[0].id, messageHub2->getId(), kEndpointInfos[1].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub2:2 to messageHub3:3
+  SessionId sessionId2 = messageHub2->openSession(
+      kEndpointInfos[1].id, messageHub3->getId(), kEndpointInfos[2].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Open session from messageHub3:3 to messageHub1:1
+  SessionId sessionId3 = messageHub3->openSession(
+      kEndpointInfos[2].id, messageHub->getId(), kEndpointInfos[0].id);
+  EXPECT_NE(sessionId, SESSION_ID_INVALID);
+
+  // Send message from messageHub:1 to messageHub2:2
+  EXPECT_TRUE(messageHub->sendMessage(std::move(messageData), kMessageSize,
+                                      /* messageType= */ 1,
+                                      /* messagePermissions= */ 0, sessionId));
+
+  // Send message from messageHub2:2 to messageHub:1
+  messageData = mAllocator.MakeUniqueArray<std::byte>(kMessageSize);
+  for (size_t i = 0; i < 5; ++i) {
+    messageData[i] = static_cast<std::byte>(i + 1);
+  }
+  EXPECT_TRUE(messageHub2->sendMessage(std::move(messageData), kMessageSize,
+                                       /* messageType= */ 2,
+                                       /* messagePermissions= */ 3, sessionId));
+
+  // Close all sessions
+  EXPECT_TRUE(messageHub->closeSession(sessionId));
+  EXPECT_TRUE(messageHub2->closeSession(sessionId2));
+  EXPECT_TRUE(messageHub3->closeSession(sessionId3));
+
+  // If we finish the test, both callbacks should have been called
+  // If the router holds the lock during the callback, this test will timeout
+}
+
+TEST_F(MessageRouterTest, ForEachEndpointOfHub) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+
+  DynamicVector<EndpointInfo> endpoints;
+  EXPECT_TRUE(router.forEachEndpointOfHub(
+      /* messageHubId= */ 1, [&endpoints](const EndpointInfo &info) {
+        endpoints.push_back(info);
+        return false;
+      }));
+  EXPECT_EQ(endpoints.size(), kNumEndpoints);
+  for (size_t i = 0; i < endpoints.size(); ++i) {
+    EXPECT_EQ(endpoints[i].id, kEndpointInfos[i].id);
+    EXPECT_STREQ(endpoints[i].name, kEndpointInfos[i].name);
+    EXPECT_EQ(endpoints[i].version, kEndpointInfos[i].version);
+    EXPECT_EQ(endpoints[i].type, kEndpointInfos[i].type);
+    EXPECT_EQ(endpoints[i].requiredPermissions,
+              kEndpointInfos[i].requiredPermissions);
+  }
+}
+
+TEST_F(MessageRouterTest, ForEachEndpoint) {
+  const char *kHubName = "hub1";
+  constexpr MessageHubId kHubId = 1;
+
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub(kHubName, kHubId, callback);
+  EXPECT_TRUE(messageHub.has_value());
+
+  DynamicVector<std::pair<MessageHubInfo, EndpointInfo>> endpoints;
+  router.forEachEndpoint(
+      [&endpoints](const MessageHubInfo &hubInfo, const EndpointInfo &info) {
+        endpoints.push_back(std::make_pair(hubInfo, info));
+      });
+  EXPECT_EQ(endpoints.size(), kNumEndpoints);
+  for (size_t i = 0; i < endpoints.size(); ++i) {
+    EXPECT_EQ(endpoints[i].first.id, kHubId);
+    EXPECT_STREQ(endpoints[i].first.name, kHubName);
+
+    EXPECT_EQ(endpoints[i].second.id, kEndpointInfos[i].id);
+    EXPECT_STREQ(endpoints[i].second.name, kEndpointInfos[i].name);
+    EXPECT_EQ(endpoints[i].second.version, kEndpointInfos[i].version);
+    EXPECT_EQ(endpoints[i].second.type, kEndpointInfos[i].type);
+    EXPECT_EQ(endpoints[i].second.requiredPermissions,
+              kEndpointInfos[i].requiredPermissions);
+  }
+}
+
+TEST_F(MessageRouterTest, ForEachEndpointOfHubInvalidHub) {
+  MessageRouterWithStorage<kMaxMessageHubs, kMaxSessions> router;
+  MessageHubCallbackStoreData callback(/* message= */ nullptr,
+                                       /* session= */ nullptr);
+  std::optional<MessageRouter::MessageHub> messageHub =
+      router.registerMessageHub("hub1", /* id= */ 1, callback);
+  EXPECT_TRUE(messageHub.has_value());
+
+  DynamicVector<EndpointInfo> endpoints;
+  EXPECT_FALSE(router.forEachEndpointOfHub(
+      /* messageHubId= */ 2, [&endpoints](const EndpointInfo &info) {
+        endpoints.push_back(info);
+        return false;
+      }));
+  EXPECT_EQ(endpoints.size(), 0);
+}
+
+}  // namespace
+}  // namespace chre::message
diff --git a/util/tests/priority_queue_test.cc b/util/tests/priority_queue_test.cc
index f6deb66..1d9a173 100644
--- a/util/tests/priority_queue_test.cc
+++ b/util/tests/priority_queue_test.cc
@@ -22,12 +22,14 @@
 namespace {
 class FakeElement {
  public:
-  FakeElement(){};
+  FakeElement() {}
   FakeElement(int index, int value) {
     mValue = value;
     mIndex = index;
-  };
-  ~FakeElement(){};
+  }
+
+  ~FakeElement() {}
+
   void setValue(int value) {
     mValue = value;
   }
@@ -45,7 +47,7 @@
 
 bool compareFunction(const FakeElement &left, const FakeElement &right) {
   return left.getValue() > right.getValue();
-};
+}
 
 class CompareClass {
  public:
diff --git a/util/tests/synchronized_expandable_memory_pool_test.cc b/util/tests/synchronized_expandable_memory_pool_test.cc
index f5957a9..ee62dfb 100644
--- a/util/tests/synchronized_expandable_memory_pool_test.cc
+++ b/util/tests/synchronized_expandable_memory_pool_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "chre/util/synchronized_expandable_memory_pool.h"
+#include "chre/util/system/synchronized_expandable_memory_pool.h"
 
 #include "gtest/gtest.h"
 
diff --git a/util/tests/synchronized_memory_pool_test.cc b/util/tests/synchronized_memory_pool_test.cc
index cecff88..35127ec 100644
--- a/util/tests/synchronized_memory_pool_test.cc
+++ b/util/tests/synchronized_memory_pool_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "chre/util/synchronized_memory_pool.h"
+#include "chre/util/system/synchronized_memory_pool.h"
 
 #include "gtest/gtest.h"
 
diff --git a/util/tests/transaction_manager_test.cc b/util/tests/transaction_manager_test.cc
index 0c70b2f..7df617b 100644
--- a/util/tests/transaction_manager_test.cc
+++ b/util/tests/transaction_manager_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "chre/util/transaction_manager.h"
+#include "chre/util/system/transaction_manager.h"
 
 #include <algorithm>
 #include <map>
diff --git a/util/util.mk b/util/util.mk
index b03bd26..4e5cd1e 100644
--- a/util/util.mk
+++ b/util/util.mk
@@ -2,11 +2,35 @@
 # Util Makefile
 #
 
+# Location of various Pigweed modules  #########################################
+
+PIGWEED_DIR = $(ANDROID_BUILD_TOP)/external/pigweed
+PIGWEED_CHRE_DIR = $(ANDROID_BUILD_TOP)/system/chre/external/pigweed
+
 # Common Compiler Flags ########################################################
 
 # Include paths.
 COMMON_CFLAGS += -I$(CHRE_PREFIX)/util/include
 
+# Pigweed ######################################################################
+
+COMMON_CFLAGS += -I$(PIGWEED_CHRE_DIR)/pw_log_nanoapp/public_overrides
+COMMON_CFLAGS += -I$(PIGWEED_CHRE_DIR)/pw_assert_nanoapp/public_overrides
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_allocator/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_assert/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_containers/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_function/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_log/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_polyfill/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_preprocessor/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_result/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_span/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/pw_status/public
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/third_party/fuchsia/repo/sdk/lib/fit/include
+COMMON_CFLAGS += -I$(PIGWEED_DIR)/third_party/fuchsia/repo/sdk/lib/stdcompat/include
+
+COMMON_SRCS += $(PIGWEED_DIR)/pw_allocator/unique_ptr.cc
+
 # Common Source Files ##########################################################
 
 COMMON_SRCS += $(CHRE_PREFIX)/util/buffer_base.cc
@@ -23,6 +47,7 @@
 COMMON_SRCS += $(CHRE_PREFIX)/util/system/ble_util.cc
 COMMON_SRCS += $(CHRE_PREFIX)/util/system/event_callbacks.cc
 COMMON_SRCS += $(CHRE_PREFIX)/util/system/debug_dump.cc
+COMMON_SRCS += $(CHRE_PREFIX)/util/system/message_router.cc
 
 # GoogleTest Source Files ######################################################
 
diff --git a/variant/CMakeLists.txt b/variant/CMakeLists.txt
new file mode 100644
index 0000000..1a09fb8
--- /dev/null
+++ b/variant/CMakeLists.txt
@@ -0,0 +1,13 @@
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_module_config(chre_variant_CONFIG)
+pw_add_library(chre.variant.config INTERFACE
+  HEADERS
+    include/chre/variant/config.h
+  PUBLIC_INCLUDES
+    include
+  PUBLIC_DEPS
+    "${chre_variant_CONFIG}"
+  PUBLIC_COMPILE_OPTIONS
+    --include=${CMAKE_CURRENT_SOURCE_DIR}/include/chre/variant/config.h
+)
diff --git a/variant/include/chre/variant/config.h b/variant/include/chre/variant/config.h
new file mode 100644
index 0000000..9a94d52
--- /dev/null
+++ b/variant/include/chre/variant/config.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// TODO: b/376532038 - Refactor the platform layer to provide static nanoapps
+// instead of having it conditionally come out of core/static_nanoapps.cc to
+// ensure the build graph can properly represent chre.core.
+#if defined(CHRE_INCLUDE_DEFAULT_STATIC_NANOAPPS)
+// This cannot be supported due to how the build rules are set up. Ideally this
+// would be part of a shared platform layer, but it's in fact part of core.
+#error "CMake does not permit the built in default static nanoapps"
+#endif  // defined(CHRE_INCLUDE_DEFAULT_STATIC_NANOAPPS)
+
+// This should provide all CHRE_* configuration defines.
+#include "chre/target_variant/config.h"
diff --git a/variant/tinysys/variant.mk b/variant/tinysys/variant.mk
index 5849305..ccb2db5 100644
--- a/variant/tinysys/variant.mk
+++ b/variant/tinysys/variant.mk
@@ -53,20 +53,21 @@
 TINYSYS_CFLAGS += -I$(RISCV_TINYSYS_PREFIX)/scp/project/RV55_A/$(TINYSYS_PLATFORM)/platform/inc
 TINYSYS_CFLAGS += -I$(RISCV_TINYSYS_PREFIX)/scp/project/RV55_A/common/platform/inc
 
-# Clang include paths
-TINYSYS_CFLAGS += -I$(RISCV_TOOLCHAIN_PATH)/lib/clang/9.0.1/include
-TINYSYS_CFLAGS += -I$(RISCV_TOOLCHAIN_PATH)/dkwlib/MRV55E03v/include
-
 # Common Compiler Flags ########################################################
 
 # Supply a symbol to indicate that the build variant supplies the static
 # nanoapp list.
 COMMON_CFLAGS += -DCHRE_VARIANT_SUPPLIES_STATIC_NANOAPP_LIST
 
+# Enable nanoapp authentication by default
+TINYSYS_CFLAGS += -DCHRE_NAPP_AUTHENTICATION_ENABLED
+
 # CHRE event count #############################################################
 
 TINYSYS_CFLAGS += -DCHRE_EVENT_PER_BLOCK=32
 TINYSYS_CFLAGS += -DCHRE_MAX_EVENT_BLOCKS=4
+TINYSYS_CFLAGS += -DCHRE_UNSCHEDULED_EVENT_PER_BLOCK=32
+TINYSYS_CFLAGS += -DCHRE_MAX_UNSCHEDULED_EVENT_BLOCKS=4
 
 # Optional Features ############################################################
 
@@ -74,7 +75,7 @@
 CHRE_GNSS_SUPPORT_ENABLED = true
 CHRE_SENSORS_SUPPORT_ENABLED = true
 CHRE_WIFI_SUPPORT_ENABLED = true
-CHRE_WWAN_SUPPORT_ENABLED = false
+CHRE_WWAN_SUPPORT_ENABLED = true
 CHRE_BLE_SUPPORT_ENABLED = true
 
 # Common Source Files ##########################################################