Use Parser Code for GNSS CHPP->CHRE Conversion am: c825b356a0
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/chre/+/12674979
Change-Id: I6530a35e12472c52cefa3a1a52c1d7077bfc1ebd
diff --git a/Android.mk b/Android.mk
index 6b93aa5..0bf822e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -16,6 +16,14 @@
LOCAL_PATH := $(call my-dir)
+# If the daemon is using USF, but USF isn't enabled, disable the daemon.
+# TODO (b/157659611): Remove when USF is available on partner with a prebuilt.
+ifeq ($(CHRE_DAEMON_USES_USF),true)
+ ifneq ($(USF_ENABLED),true)
+ CHRE_DAEMON_ENABLED := false
+ endif
+endif
+
# Don't build the daemon for targets that don't contain a vendor image as
# libsdsprpc and libadsprpc are provided by vendor code
ifeq ($(BUILDING_VENDOR_IMAGE),true)
@@ -56,23 +64,41 @@
host/common/log_message_parser_base.cc \
host/common/socket_server.cc \
host/common/st_hal_lpma_handler.cc \
- host/msm/daemon/fastrpc_daemon.cc \
- host/msm/daemon/main.cc \
- host/msm/daemon/generated/chre_slpi_stub.c \
platform/shared/host_protocol_common.cc
+MSM_DAEMON_SRC_FILES := \
+ host/msm/daemon/fastrpc_daemon.cc \
+ host/msm/daemon/main.cc \
+ host/msm/daemon/generated/chre_slpi_stub.c
+
+USF_DAEMON_SRC_FILES := \
+ host/usf_daemon/usf_daemon.cc \
+ host/usf_daemon/main.cc
+
+USF_DAEMON_CFLAGS := \
+ -DFLATBUFFERS_USF
+
LOCAL_C_INCLUDES := \
- external/fastrpc/inc \
system/chre/external/flatbuffers/include \
system/chre/host/common/include \
- system/chre/host/msm/daemon \
system/chre/platform/shared/include \
- system/chre/platform/slpi/include \
system/chre/util/include \
system/core/base/include \
system/core/libcutils/include \
system/core/liblog/include \
- system/core/libutils/include \
+ system/core/libutils/include
+
+MSM_DAEMON_INCLUDES := \
+ external/fastrpc/inc \
+ system/chre/platform/slpi/include \
+ system/chre/host/msm/daemon
+
+USF_DAEMON_INCLUDES := \
+ system/chre/host/usf_daemon \
+ vendor/google/sensors/usf/core/include \
+ vendor/google/sensors/usf/pal/android/include \
+ vendor/google/sensors/usf/pal/include \
+ vendor/google/sensors/usf/core/fbs
LOCAL_SHARED_LIBRARIES := \
libjsoncpp \
@@ -84,6 +110,23 @@
android.hardware.soundtrigger@2.0 \
libpower
+USF_DAEMON_SHARED_LIBRARIES := libusf
+
+ifeq ($(CHRE_DAEMON_USES_USF),true)
+LOCAL_C_INCLUDES += $(USF_DAEMON_INCLUDES)
+LOCAL_SRC_FILES += $(USF_DAEMON_SRC_FILES)
+LOCAL_CFLAGS += $(USF_DAEMON_CFLAGS)
+LOCAL_SHARED_LIBRARIES += $(USF_DAEMON_SHARED_LIBRARIES)
+else
+LOCAL_SRC_FILES += $(MSM_DAEMON_SRC_FILES)
+LOCAL_C_INCLUDES += $(MSM_DAEMON_INCLUDES)
+ifeq ($(CHRE_DAEMON_USE_SDSPRPC),true)
+LOCAL_SHARED_LIBRARIES += libsdsprpc
+else
+LOCAL_SHARED_LIBRARIES += libadsprpc
+endif
+endif
+
# Enable tokenized logging
ifeq ($(CHRE_USE_TOKENIZED_LOGGING),true)
LOCAL_CFLAGS += -DCHRE_USE_TOKENIZED_LOGGING
@@ -102,10 +145,9 @@
LOCAL_SRC_FILES += $(PIGWEED_TOKENIZER_DIR_RELPATH)/pw_varint/varint.cc
endif
-ifeq ($(CHRE_DAEMON_USE_SDSPRPC),true)
-LOCAL_SHARED_LIBRARIES += libsdsprpc
-else
-LOCAL_SHARED_LIBRARIES += libadsprpc
+ifeq ($(CHRE_DAEMON_LPMA_ENABLED),true)
+LOCAL_SHARED_LIBRARIES += android.hardware.soundtrigger@2.0
+LOCAL_SHARED_LIBRARIES += libpower
endif
include $(BUILD_EXECUTABLE)
diff --git a/Makefile b/Makefile
index 6c178a7..631a4b3 100644
--- a/Makefile
+++ b/Makefile
@@ -97,6 +97,7 @@
# implementation of CHRE. Example: this CHRE implementation is never built for
# google_cm4_nanohub as Nanohub itself is a CHRE implementation.
include $(CHRE_PREFIX)/build/variant/google_arm64_android.mk
+include $(CHRE_PREFIX)/build/variant/google_armv8a_aoc.mk
include $(CHRE_PREFIX)/build/variant/google_hexagonv55_slpi-see.mk
include $(CHRE_PREFIX)/build/variant/google_hexagonv60_slpi.mk
include $(CHRE_PREFIX)/build/variant/google_hexagonv62_slpi.mk
diff --git a/apps/apps.mk b/apps/apps.mk
index a76dc71..6ab4d6d 100644
--- a/apps/apps.mk
+++ b/apps/apps.mk
@@ -7,7 +7,6 @@
COMMON_CFLAGS += -Iapps/include
# App makefiles ################################################################
-
ifeq ($(CHRE_AUDIO_SUPPORT_ENABLED), true)
include apps/audio_world/audio_world.mk
endif
diff --git a/apps/sensor_world/sensor_world.cc b/apps/sensor_world/sensor_world.cc
index f1a6687..fd08f5e 100644
--- a/apps/sensor_world/sensor_world.cc
+++ b/apps/sensor_world/sensor_world.cc
@@ -352,6 +352,7 @@
const auto *ev = static_cast<const chreSensorThreeAxisData *>(eventData);
const auto header = ev->header;
const auto *data = ev->readings;
+ const auto accuracy = header.accuracy;
sampleTime = header.baseTimestamp;
float x = 0, y = 0, z = 0;
@@ -365,9 +366,9 @@
y /= header.readingCount;
z /= header.readingCount;
- CLOGI("%s, %d samples: %f %f %f, t=%" PRIu64 " ms",
+ CLOGI("%s, %d samples: %f %f %f, accuracy: %u, t=%" PRIu64 " ms",
getSensorName(header.sensorHandle), header.readingCount, x, y, z,
- header.baseTimestamp / kOneMillisecondInNanoseconds);
+ accuracy, header.baseTimestamp / kOneMillisecondInNanoseconds);
if (eventType == CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA) {
CLOGI("UncalGyro time: first %" PRIu64 " last %" PRIu64 " chre %" PRIu64
@@ -395,8 +396,9 @@
}
v /= header.readingCount;
- CLOGI("%s, %d samples: %f, t=%" PRIu64 " ms",
+ CLOGI("%s, %d samples: %f, accuracy = %u, t=%" PRIu64 " ms",
getSensorName(header.sensorHandle), header.readingCount, v,
+ header.accuracy,
header.baseTimestamp / kOneMillisecondInNanoseconds);
break;
}
@@ -407,9 +409,9 @@
const auto reading = ev->readings[0];
sampleTime = header.baseTimestamp;
- CLOGI("%s, %d samples: isNear %d, invalid %d",
+ CLOGI("%s, %d samples: isNear %d, invalid %d, accuracy: %u",
getSensorName(header.sensorHandle), header.readingCount,
- reading.isNear, reading.invalid);
+ reading.isNear, reading.invalid, header.accuracy);
CLOGI("Prox time: sample %" PRIu64 " chre %" PRIu64 " delta %" PRId64
"ms",
@@ -446,8 +448,8 @@
const auto *ev = static_cast<const chreSensorOccurrenceData *>(eventData);
const auto header = ev->header;
- CLOGI("%s, %d samples", getSensorName(header.sensorHandle),
- header.readingCount);
+ CLOGI("%s, %d samples, accuracy: %u", getSensorName(header.sensorHandle),
+ header.readingCount, header.accuracy);
break;
}
diff --git a/build/arch/armv8a.mk b/build/arch/armv8a.mk
new file mode 100644
index 0000000..3073cb9
--- /dev/null
+++ b/build/arch/armv8a.mk
@@ -0,0 +1,36 @@
+#
+# Build targets for an ARMv8 based processor
+#
+
+# The ARMV8A_XX binary path needs to be specified by a
+# higher level makefile
+TARGET_AR = $(ARMV8A_AR)
+TARGET_CC = $(ARMV8A_CC)
+TARGET_LD = $(ARMV8A_LD)
+
+TARGET_CFLAGS += $(DEFINES)
+TARGET_CFLAGS += $(CPPFLAGS)
+COMMON_CXX_CFLAGS += $(CXXFLAGS)
+COMMON_C_CFLAGS += $(CFLAGS)
+
+TARGET_SO_LDFLAGS += $(LDFLAGS)
+TARGET_SO_EARLY_LIBS += $(LDLIBS)
+
+# TODO: Fix ar_client so the following two can be removed.
+TARGET_CFLAGS += -Wno-strict-prototypes
+TARGET_CFLAGS += -Wno-missing-prototypes
+
+# TODO: Fix log_null so this can be removed.
+TARGET_CFLAGS += -Wno-unused-parameter
+
+# CHRE's stack size is 8KB so leave a little room to ensure the stack isn't
+# blown when calling nanoapps since nanoappStart will be invoked with some data
+# on the stack.
+TARGET_CFLAGS += -Wframe-larger-than=7000
+
+# Don't add these flags when building just the static archive as they generate
+# sections that will break static linking.
+ifeq ($(IS_ARCHIVE_ONLY_BUILD),)
+TARGET_CFLAGS += -fPIC
+TARGET_SO_LDFLAGS += -shared
+endif
diff --git a/build/build_template.mk b/build/build_template.mk
index cb2867a..0a956fd 100644
--- a/build/build_template.mk
+++ b/build/build_template.mk
@@ -110,7 +110,11 @@
$(1)_header: $$($$(1)_HEADER)
.PHONY: $(1)
+ifeq ($(IS_ARCHIVE_ONLY_BUILD),true)
+$(1): $(1)_ar
+else
$(1): $(1)_ar $(1)_so $(1)_bin $(1)_header
+endif
# If building the runtime, simply add the archive and shared object to the all
# target. When building CHRE, it is expected that this runtime just be linked
@@ -135,7 +139,7 @@
# uint32_t magic; // "NANO"
# uint64_t appId; // App Id, contains vendor id
# uint32_t appVersion; // Version of the app
-# uint32_t flags; // Signed, encrypted
+# uint32_t flags; // Signed, encrypted, TCM-capable
# uint64_t hwHubType; // Which hub type is this compiled for
# uint8_t targetChreApiMajorVersion; // CHRE API version
# uint8_t targetChreApiMinorVersion;
@@ -151,13 +155,20 @@
# the nanoapp, the version and the app ID. Marshalling this data from the
# Makefile environment into something like python or even a small C program
# is an unnecessary step.
+#
+# For the flags field of the struct, the following values are currently defined:
+# Signed = 0x00000001
+# Encrypted = 0x00000002
+# TCM-capable = 0x00000004
+#
+# The highest order byte is reserved for platform-specific usage.
$$($$(1)_HEADER): $$(OUT)/$$$(1) $$($$$(1)_DIRS)
printf "00000000 %.8x " `$(BE_TO_LE_SCRIPT) 0x00000001` > $$@
printf "%.8x " `$(BE_TO_LE_SCRIPT) 0x4f4e414e` >> $$@
printf "%.16x\n" `$(BE_TO_LE_SCRIPT) $(NANOAPP_ID)` >> $$@
printf "00000010 %.8x " `$(BE_TO_LE_SCRIPT) $(NANOAPP_VERSION)` >> $$@
- printf "%.8x " `$(BE_TO_LE_SCRIPT) 0x00000001` >> $$@
+ printf "%.8x " `$(BE_TO_LE_SCRIPT) $(TARGET_NANOAPP_FLAGS)` >> $$@
printf "%.16x\n" `$(BE_TO_LE_SCRIPT) $(13)` >> $$@
printf "00000020 %.2x " \
`$(BE_TO_LE_SCRIPT) $(TARGET_CHRE_API_VERSION_MAJOR)` >> $$@
@@ -261,6 +272,9 @@
TARGET_CFLAGS_LOCAL = $(TARGET_CFLAGS)
TARGET_CFLAGS_LOCAL += -DCHRE_PLATFORM_ID=$(TARGET_PLATFORM_ID)
+
+# Default the nanoapp header flag values to signed if not overidden.
+TARGET_NANOAPP_FLAGS ?= 0x00000001
$(eval $(call BUILD_TEMPLATE, $(TARGET_NAME), \
$(COMMON_CFLAGS) $(TARGET_CFLAGS_LOCAL), \
$(TARGET_CC), \
diff --git a/build/nanoapp/app.mk b/build/nanoapp/app.mk
index 25f7791..b153345 100644
--- a/build/nanoapp/app.mk
+++ b/build/nanoapp/app.mk
@@ -112,6 +112,7 @@
GOOGLE_HEXAGONV66_SLPI-SEE_SRCS += $(DSO_SUPPORT_LIB_SRCS)
GOOGLE_HEXAGONV66_SLPI-SEE-UIMG_SRCS += $(DSO_SUPPORT_LIB_SRCS)
GOOGLE_ARM64_ANDROID_SRCS += $(DSO_SUPPORT_LIB_SRCS)
+GOOGLE_AOC_SRCS += $(DSO_SUPPORT_LIB_SRCS)
GOOGLE_X86_LINUX_SRCS += $(DSO_SUPPORT_LIB_SRCS)
QCOM_HEXAGONV60_NANOHUB_SRCS += $(APP_SUPPORT_PATH)/qcom_nanohub/app_support.cc
QCOM_HEXAGONV60_NANOHUB-UIMG_SRCS += $(APP_SUPPORT_PATH)/qcom_nanohub/app_support_uimg.cc
@@ -127,6 +128,7 @@
# Supported variants includes
include $(CHRE_PREFIX)/build/variant/google_arm64_android.mk
+include $(CHRE_PREFIX)/build/variant/google_armv8a_aoc.mk
include $(CHRE_PREFIX)/build/variant/google_cm4_nanohub.mk
include $(CHRE_PREFIX)/build/variant/google_hexagonv55_slpi-see.mk
include $(CHRE_PREFIX)/build/variant/google_hexagonv60_slpi.mk
diff --git a/build/nanoapp/google_aoc.mk b/build/nanoapp/google_aoc.mk
new file mode 100644
index 0000000..2828d2d
--- /dev/null
+++ b/build/nanoapp/google_aoc.mk
@@ -0,0 +1,31 @@
+#
+# Nanoapp Build Rules for Google CHRE on AoC
+#
+
+################################################################################
+#
+# Google Generic CHRE on AoC Nanoapp Build Template
+#
+# Invoke this to instantiate a set of Nanoapp post processing build targets.
+#
+# TARGET_NAME_nanoapp - The resulting nanoapp output.
+#
+# Argument List:
+# $1 - TARGET_NAME - The name of the target being built.
+#
+################################################################################
+
+ifndef GOOGLE_AOC_NANOAPP_BUILD_TEMPLATE
+define GOOGLE_AOC_NANOAPP_BUILD_TEMPLATE
+
+.PHONY: $(1)_nanoapp
+all: $(1)_nanoapp
+
+$(1)_nanoapp: $(1)
+
+endef
+endif
+
+# Template Invocation ##########################################################
+
+$(eval $(call GOOGLE_AOC_NANOAPP_BUILD_TEMPLATE, $(TARGET_NAME)))
diff --git a/build/variant/google_armv8a_aoc.mk b/build/variant/google_armv8a_aoc.mk
new file mode 100644
index 0000000..427aa88
--- /dev/null
+++ b/build/variant/google_armv8a_aoc.mk
@@ -0,0 +1,101 @@
+include $(CHRE_PREFIX)/build/clean_build_template_args.mk
+
+TARGET_NAME = google_armv8a_aoc
+ifneq ($(filter $(TARGET_NAME)% all, $(MAKECMDGOALS)),)
+
+# Environment Checks
+ifeq ($(AOC_TOP_DIR),)
+$(error "The AoC root source directory needs to be exported as the AOC_TOP_DIR \
+ environment variable")
+endif
+
+SUPPORTED_AOC_PLATFORMS_ON_CHRE := fpga_a32 whi_a0_a32
+ifeq ($(AOC_PLATFORM) ,)
+$(error "The platform we're building for needs to be exported as the \
+ AOC_PLATFORM environment variable. Supported platforms are \
+ [${SUPPORTED_AOC_PLATFORMS_ON_CHRE}]")
+endif
+
+ifeq ($(filter $(AOC_PLATFORM),$(SUPPORTED_AOC_PLATFORMS_ON_CHRE)),)
+$(error "Unsupported AoC Platform - $(AOC_PLATFORM) - Supported platforms are \
+ [${SUPPORTED_AOC_PLATFORMS_ON_CHRE}]")
+endif
+
+# The SRC_DIR variable is used in toolchain selection in AoC, add that as
+# a dependency before including the platform toolchain makefile
+ifeq ($(SRC_DIR),)
+SRC_DIR=$(AOC_TOP_DIR)/AOC
+endif
+
+include $(AOC_TOP_DIR)/AOC/build/$(AOC_PLATFORM)/toolchain.mk
+include $(AOC_TOP_DIR)/AOC/targets/aoc.$(AOC_PLATFORM)/local.mk
+
+# Sized based on the buffer allocated in the host daemon (4096 bytes), minus
+# FlatBuffer overhead (max 80 bytes), minus some extra space to make a nice
+# round number and allow for addition of new fields to the FlatBuffer
+TARGET_CFLAGS = -DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4000
+TARGET_CFLAGS += $(AOC_CFLAGS)
+TARGET_CFLAGS += $(FREERTOS_CFLAGS)
+TARGET_CFLAGS += -I$(AOC_TOP_DIR)/AOC/libs/common/basic/include
+TARGET_CFLAGS += -I$(AOC_TOP_DIR)/external/libcxx/include
+
+# libc / libm headers must be included after libcxx headers in case libcxx
+# headers utilize #include_next
+TARGET_CFLAGS += -I$(AOC_TOP_DIR)/AOC/libs/bionic_interface/include
+TARGET_CFLAGS += -I$(AOC_TOP_DIR)/AOC/libs/common/libc/include
+
+# 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
+
+# add platform specific flags
+ifeq ($(AOC_PLATFORM),fpga_a32)
+TARGET_CFLAGS += $(AOC_FPGA_A32_CFLAGS)
+# We need a function stack size of at least 400 bytes, which might not be
+# the case by default
+TARGET_CFLAGS += -Wframe-larger-than=420
+endif
+ifeq ($(AOC_PLATFORM),whi_a0_a32)
+TARGET_CFLAGS += $(AOC_WHI_A0_A32_CFLAGS)
+# We need a function stack size of at least 400 bytes, which might not be
+# the case by default
+TARGET_CFLAGS += -Wframe-larger-than=420
+endif
+
+TARGET_VARIANT_SRCS += $(AOC_SRCS)
+TARGET_VARIANT_SRCS += $(FREERTOS_SRCS)
+TARGET_VARIANT_SRCS += $(GOOGLE_AOC_SRCS)
+
+TARGET_PLATFORM_ID = 0x476F6F676C000008
+
+# Set platform-based build variables for arch armv8a
+ARMV8A_AR = $(CLANG_PATH)/llvm-ar
+ARMV8A_CC = $(CXX)
+ARMV8A_LD = $(LD)
+
+ifneq ($(IS_NANOAPP_BUILD),)
+ifeq ($(CHRE_NANOAPP_BUILD_ID),)
+# Set to "local" to indicate this was generated locally. Any production build
+# of the nanoapp will specify this ID.
+CHRE_NANOAPP_BUILD_ID=local
+endif
+
+# Provide the CHRE commit hash to make it easy to identify CHRE changes that
+# may be present in a nanoapp. This ID may change even if no nanoapp changes
+# have been made so it has to go in the unstable ID section.
+COMMIT_HASH_COMMAND = git describe --always --long --dirty
+CHRE_COMMIT_HASH = $(shell cd $(CHRE_PREFIX) && $(COMMIT_HASH_COMMAND))
+
+TARGET_CFLAGS += \
+ -DNANOAPP_UNSTABLE_ID=\"nanoapp_unstable_id=$(CHRE_NANOAPP_BUILD_ID)@chre-$(CHRE_COMMIT_HASH)\"
+
+include $(CHRE_PREFIX)/build/nanoapp/google_aoc.mk
+ifeq ($(CHRE_TCM_ENABLED),true)
+TARGET_CFLAGS += -DCHRE_TCM_ENABLED
+# Flags: Signed | TCM
+TARGET_NANOAPP_FLAGS = 0x00000005
+endif
+endif
+
+include $(CHRE_PREFIX)/build/arch/armv8a.mk
+include $(CHRE_PREFIX)/build/build_template.mk
+endif
diff --git a/chpp/platform/aoc/chpp_init.cc b/chpp/platform/aoc/chpp_init.cc
new file mode 100644
index 0000000..d9d539a
--- /dev/null
+++ b/chpp/platform/aoc/chpp_init.cc
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#include "chpp/platform/chpp_init.h"
+
+#include "chpp/app.h"
+#include "chpp/platform/chpp_uart_link_manager.h"
+#include "chpp/transport.h"
+#include "chre/util/fixed_size_vector.h"
+#include "chre/util/nested_data_ptr.h"
+#include "environment.h"
+#include "uart_map.h"
+
+#include "task.h"
+
+// Forward declaration
+constexpr uint8_t chpp::UartLinkManager::kWifiWakeOutGpioPinNumber;
+constexpr uint8_t chpp::UartLinkManager::kWifiWakeInGpiNumber;
+constexpr uint8_t chpp::UartLinkManager::kGnssWakeOutGpioPinNumber;
+constexpr uint8_t chpp::UartLinkManager::kGnssWakeInGpiNumber;
+constexpr uint8_t chpp::UartLinkManager::kWwanWakeOutGpioPinNumber;
+constexpr uint8_t chpp::UartLinkManager::kWwanWakeInGpiNumber;
+
+using chre::FixedSizeVector;
+using chre::NestedDataPtr;
+
+namespace chpp {
+namespace {
+
+constexpr configSTACK_DEPTH_TYPE kChppTaskStackDepthWords = 0x400;
+
+// Choose an appripriate priority to avoid initializing things too late.
+// TODO: Configure priority in relation to CHRE based on end to end testing.
+constexpr UBaseType_t kChppTaskPriority = tskIDLE_PRIORITY + 2;
+
+// Structures to hold CHPP task related variables
+constexpr size_t kChppLinkTotal = ChppLinkType::CHPP_LINK_TYPE_TOTAL;
+TaskHandle_t gChppTaskHandleList[kChppLinkTotal];
+ChppTransportState gChppTransportStateList[kChppLinkTotal];
+ChppAppState gChppAppStateList[kChppLinkTotal];
+FixedSizeVector<chpp::UartLinkManager, kChppLinkTotal> gManagerList;
+
+struct ChppClientServiceSet getClientServiceSet(ChppLinkType type) {
+ struct ChppClientServiceSet set = {};
+ switch (type) {
+ case ChppLinkType::CHPP_LINK_TYPE_WIFI:
+ set.wifiClient = 1;
+ break;
+ case ChppLinkType::CHPP_LINK_TYPE_GNSS:
+ set.gnssClient = 1;
+ break;
+ case ChppLinkType::CHPP_LINK_TYPE_WWAN:
+ set.wwanClient = 1;
+ break;
+ default:
+ LOGE("Invalid type %d to get client/service set", type);
+ CHRE_ASSERT(false);
+ }
+
+ return set;
+}
+
+// Initializes the CHPP instance and runs the work thread.
+void chppThreadEntry(void *context) {
+ NestedDataPtr<ChppLinkType> type;
+ type.dataPtr = context;
+
+ struct ChppTransportState *transportState =
+ &gChppTransportStateList[type.data];
+ struct ChppAppState *appState = &gChppAppStateList[type.data];
+
+ chppTransportInit(transportState, appState);
+ chppAppInitWithClientServiceSet(appState, transportState,
+ getClientServiceSet(type.data));
+
+ chppWorkThreadStart(transportState);
+
+ chppAppDeinit(appState);
+ chppTransportDeinit(transportState);
+
+ vTaskDelete(nullptr);
+ gChppTaskHandleList[type.data] = nullptr;
+}
+
+BaseType_t startChppWorkThread(ChppLinkType type, const char *taskName) {
+ struct ChppTransportState *transportState = &gChppTransportStateList[type];
+ transportState->linkParams.uartLinkManager = &gManagerList[type];
+
+ NestedDataPtr<ChppLinkType> linkType(type);
+ return xTaskCreate(chppThreadEntry, taskName, kChppTaskStackDepthWords,
+ linkType.dataPtr /* args */, kChppTaskPriority,
+ &gChppTaskHandleList[type]);
+}
+
+void stopChppWorkThread(ChppLinkType type) {
+ if (gChppTaskHandleList[type] != nullptr) {
+ chppWorkThreadStop(&gChppTransportStateList[type]);
+ }
+}
+
+} // namespace
+
+void init() {
+ gManagerList.emplace_back(
+ &gChppTransportStateList[ChppLinkType::CHPP_LINK_TYPE_WIFI],
+ Environment::Instance()->UART(UART_MAP::UART_MAP_WIFI),
+ chpp::UartLinkManager::kWifiWakeOutGpioPinNumber,
+ chpp::UartLinkManager::kWifiWakeInGpiNumber);
+
+ gManagerList.emplace_back(
+ &gChppTransportStateList[ChppLinkType::CHPP_LINK_TYPE_GNSS],
+ Environment::Instance()->UART(UART_MAP::UART_MAP_GNSS),
+ chpp::UartLinkManager::kGnssWakeOutGpioPinNumber,
+ chpp::UartLinkManager::kGnssWakeInGpiNumber);
+
+ gManagerList.emplace_back(
+ &gChppTransportStateList[ChppLinkType::CHPP_LINK_TYPE_WWAN],
+ Environment::Instance()->UART(UART_MAP::UART_MAP_MODEM),
+ chpp::UartLinkManager::kWwanWakeOutGpioPinNumber,
+ chpp::UartLinkManager::kWwanWakeInGpiNumber);
+
+ BaseType_t rc =
+ startChppWorkThread(ChppLinkType::CHPP_LINK_TYPE_WIFI, "CHPP WIFI");
+ CHRE_ASSERT(rc == pdPASS);
+
+ rc = startChppWorkThread(ChppLinkType::CHPP_LINK_TYPE_GNSS, "CHPP GNSS");
+ CHRE_ASSERT(rc == pdPASS);
+
+ rc = startChppWorkThread(ChppLinkType::CHPP_LINK_TYPE_WWAN, "CHPP WWAN");
+ CHRE_ASSERT(rc == pdPASS);
+}
+
+void deinit() {
+ stopChppWorkThread(ChppLinkType::CHPP_LINK_TYPE_WIFI);
+ stopChppWorkThread(ChppLinkType::CHPP_LINK_TYPE_GNSS);
+ stopChppWorkThread(ChppLinkType::CHPP_LINK_TYPE_WWAN);
+}
+
+ChppAppState *getChppAppState(ChppLinkType linkType) {
+ CHRE_ASSERT(linkType < ChppLinkType::CHPP_LINK_TYPE_TOTAL);
+ return (linkType < ChppLinkType::CHPP_LINK_TYPE_TOTAL)
+ ? &gChppAppStateList[linkType]
+ : nullptr;
+}
+
+} // namespace chpp
diff --git a/chpp/platform/aoc/chpp_task_util.cc b/chpp/platform/aoc/chpp_task_util.cc
new file mode 100644
index 0000000..785ae14
--- /dev/null
+++ b/chpp/platform/aoc/chpp_task_util.cc
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "chpp/platform/chpp_task_util.h"
+
+#include "chpp/platform/log.h"
+#include "chre/platform/fatal_error.h"
+#include "efw/include/timer.h"
+
+namespace chpp {
+
+TaskUtil::TaskUtil() {
+ mCvSemaphoreHandle = xSemaphoreCreateBinaryStatic(&mSemaphoreBuffer);
+ if (mCvSemaphoreHandle == nullptr) {
+ FATAL_ERROR("failed to create cv semaphore");
+ }
+}
+
+TaskUtil::~TaskUtil() {
+ if (mCvSemaphoreHandle != nullptr) {
+ vSemaphoreDelete(mCvSemaphoreHandle);
+ }
+}
+
+void TaskUtil::suspend(uint64_t suspendTimeNs) {
+ Timer *timer = Timer::Instance();
+ const TickType_t timeoutTicks =
+ static_cast<TickType_t>(timer->NsToTicks(suspendTimeNs));
+
+ auto timeoutCallback = [](void *context) -> bool {
+ SemaphoreHandle_t handle = static_cast<SemaphoreHandle_t>(context);
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xSemaphoreGiveFromISR(handle, &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+ return false; // one-shot
+ };
+
+ void *timerHandle;
+ int rc = timer->EventAddAtOffset(timeoutTicks, timeoutCallback,
+ mCvSemaphoreHandle, &timerHandle);
+ if (rc != 0) {
+ CHPP_LOGE("Failed to set suspend timeout timer");
+ } else {
+ xSemaphoreTake(mCvSemaphoreHandle, portMAX_DELAY /* xBlockTime */);
+ }
+}
+
+} // namespace chpp
diff --git a/chpp/platform/aoc/chpp_uart_link_manager.cc b/chpp/platform/aoc/chpp_uart_link_manager.cc
new file mode 100644
index 0000000..895a747
--- /dev/null
+++ b/chpp/platform/aoc/chpp_uart_link_manager.cc
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+
+#include "chpp/platform/chpp_uart_link_manager.h"
+
+#include "aoc.h"
+#include "chpp/macros.h"
+#include "chpp/platform/log.h"
+#include "chre/platform/fatal_error.h"
+#include "chre/platform/system_time.h"
+#include "efw/include/interrupt_controller.h"
+#include "efw/include/processor.h"
+#include "efw/include/timer.h"
+#include "ipc-regions.h"
+
+// Define as 0 to disable wake handshaking
+// TODO: Enable once ready
+#define WAKE_HANDSHAKE_ENABLE 0
+
+using chre::SystemTime;
+
+namespace chpp {
+
+namespace {
+
+void onUartRxInterrupt(void *context) {
+ UartLinkManager *manager = static_cast<UartLinkManager *>(context);
+ manager->getUart()->DisableRxInterrupt();
+ manager->pullRxSamples();
+ chppWorkThreadSignalFromLink(&manager->getTransportContext()->linkParams,
+ CHPP_TRANSPORT_SIGNAL_LINK_RX_PROCESS);
+ if (!manager->isRxBufferFull()) {
+ manager->getUart()->EnableRxInterrupt();
+ }
+}
+
+//! This is the interrupt to use when handling requests from the remote
+//! to start a transaction.
+void onTransactionRequestInterrupt(void *context, uint32_t /* interrupt */) {
+ UartLinkManager *manager = static_cast<UartLinkManager *>(context);
+ manager->getWakeInGpi()->SetTriggerFunction(GPIAoC::GPI_DISABLE);
+ manager->getWakeInGpi()->ClearInterrupt();
+ manager->setTransactionPending();
+ chppWorkThreadSignalFromLink(&manager->getTransportContext()->linkParams,
+ CHPP_TRANSPORT_SIGNAL_LINK_WAKE_IN_IRQ);
+}
+
+#if WAKE_HANDSHAKE_ENABLE
+//! This is the interrupt to use when handling signal handshaking during
+//! an on-going transaction.
+void onTransactionHandshakeInterrupt(void *context, uint32_t /* interrupt */) {
+ UartLinkManager *manager = static_cast<UartLinkManager *>(context);
+ manager->getWakeInGpi()->SetTriggerFunction(GPIAoC::GPI_DISABLE);
+ manager->getWakeInGpi()->ClearInterrupt();
+
+ BaseType_t xHigherPriorityTaskWoken = 0;
+ xTaskNotifyFromISR(manager->getTaskHandle(),
+ CHPP_TRANSPORT_SIGNAL_LINK_HANDSHAKE_IRQ, eSetBits,
+ &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+}
+#endif // WAKE_HANDSHAKE_ENABLE
+
+} // anonymous namespace
+
+UartLinkManager::UartLinkManager(struct ChppTransportState *context, UART *uart,
+ uint8_t wakeOutPinNumber,
+ uint8_t wakeInGpiNumber)
+ : mTransportContext(context),
+ mUart(uart),
+ mWakeOutGpio(wakeOutPinNumber, IP_LOCK_GPIO),
+ mWakeInGpi(wakeInGpiNumber) {
+ mWakeOutGpio.SetDirection(GPIO::DIRECTION::OUTPUT);
+ mWakeOutGpio.Clear();
+}
+
+void UartLinkManager::init(TaskHandle_t handle) {
+ mUart->RegisterRxCallback(onUartRxInterrupt, this);
+ mUart->EnableRxInterrupt();
+
+ mTaskHandle = handle;
+ mWakeInGpi.SetInterruptHandler(onTransactionRequestInterrupt, this);
+ mWakeInGpi.SetTriggerFunction(GPIAoC::GPI_RISING_EDGE);
+ InterruptController::Instance()->InterruptEnable(
+ IRQ_GPI0 + mWakeInGpi.GetGpiNumber(), Processor::Instance()->CoreID(),
+ true /* enable */);
+}
+
+void UartLinkManager::deinit() {
+ clearTxPacket();
+ mWakeOutGpio.Clear();
+
+ mUart->DisableRxInterrupt();
+
+ InterruptController::Instance()->InterruptEnable(
+ IRQ_GPI0 + mWakeInGpi.GetGpiNumber(), Processor::Instance()->CoreID(),
+ false /* enable */);
+ mWakeInGpi.SetTriggerFunction(GPIAoC::GPI_DISABLE);
+}
+
+bool UartLinkManager::prepareTxPacket(uint8_t *buf, size_t len) {
+ bool success = !hasTxPacket();
+ if (!success) {
+ CHPP_LOGE("Cannot prepare packet while one pending");
+ } else {
+ mCurrentBuffer = buf;
+ mCurrentBufferLen = len;
+ setTransactionPending();
+ }
+ return success;
+}
+
+bool UartLinkManager::waitForHandshakeIrq(uint64_t timeoutNs) {
+#if WAKE_HANDSHAKE_ENABLE
+ bool success = false;
+ Timer *timer = Timer::Instance();
+ const TickType_t timeoutTicks =
+ static_cast<TickType_t>(timer->NsToTicks(timeoutNs));
+
+ // Treat as if the IRQ occurred in the timeout case. We check if the timer
+ // was successfully cancelled to determine if we timed out or not.
+ auto timeoutCallback = [](void *context) -> bool {
+ onTransactionHandshakeInterrupt(context, 0);
+ return false; // one-shot
+ };
+
+ void *timerHandle;
+ int rc = timer->EventAddAtOffset(timeoutTicks, timeoutCallback, this,
+ &timerHandle);
+ if (rc != 0) {
+ CHPP_LOGE("Failed to set handshake timeout timer");
+ } else {
+ uint32_t signal = 0;
+ while ((signal & CHPP_TRANSPORT_SIGNAL_LINK_HANDSHAKE_IRQ) == 0) {
+ xTaskNotifyWait(
+ 0 /* ulBitsToClearOnEntry */,
+ CHPP_TRANSPORT_SIGNAL_LINK_HANDSHAKE_IRQ /* ulBitsToClearOnExit */,
+ &signal, portMAX_DELAY /* xTicksToWait */);
+ }
+
+ // If we cleared the notification while another one is in progress,
+ // re-notify the task so it doesn't get lost.
+ if ((signal & ~(CHPP_TRANSPORT_SIGNAL_LINK_HANDSHAKE_IRQ)) != 0) {
+ xTaskNotify(mTaskHandle, 0, eNoAction);
+ }
+
+ // EventRemove() will not return 0 if the timer already fired.
+ success = (timer->EventRemove(timerHandle) == 0);
+ }
+
+ return success;
+#else
+ return true;
+#endif // WAKE_HANDSHAKE_ENABLE
+}
+
+bool UartLinkManager::startTransaction() {
+ bool success = true;
+ mWakeInGpi.SetTriggerFunction(GPIAoC::GPI_DISABLE);
+
+ // Check if a transaction is pending (either from an IRQ or from a pending
+ // TX packet) before starting one. This check is required to avoid attempting
+ // to start a transaction when it has already been handled (e.g. an IRQ has
+ // signalled the transport layer, but it already had a packet to send and
+ // handled them together).
+ if (mTransactionPending.load()) {
+ mWakeOutGpio.Set(true /* set */);
+ uint64_t pulseEndTimeNs =
+ SystemTime::getMonotonicTime().toRawNanoseconds() + kPulseTimeNs;
+
+#if WAKE_HANDSHAKE_ENABLE
+ mWakeInGpi.SetInterruptHandler(onTransactionHandshakeInterrupt, this);
+ mWakeInGpi.SetTriggerFunction(GPIAoC::GPI_LEVEL_ACTIVE_HIGH);
+#endif // WAKE_HANDSHAKE_ENABLE
+
+ if (waitForHandshakeIrq(kStartTimeoutNs)) {
+ if (hasTxPacket()) {
+ int bytesTransmitted = mUart->Tx(mCurrentBuffer, mCurrentBufferLen);
+
+ // Deassert wake_out as soon as data is transmitted, per specifications.
+ mWakeOutGpio.Clear();
+
+ if (static_cast<size_t>(bytesTransmitted) != mCurrentBufferLen) {
+ CHPP_LOGE("Failed to transmit data");
+ success = false;
+ }
+
+ } else {
+ uint64_t now = SystemTime::getMonotonicTime().toRawNanoseconds();
+ if (now < pulseEndTimeNs) {
+ mTaskUtil.suspend(pulseEndTimeNs - now);
+ }
+
+ mWakeOutGpio.Clear();
+ }
+
+#if WAKE_HANDSHAKE_ENABLE
+ mWakeInGpi.SetTriggerFunction(GPIAoC::GPI_LEVEL_ACTIVE_LOW);
+ if (!waitForHandshakeIrq(kEndTimeoutNs)) {
+ CHPP_LOGE("Wake handshaking end timed out");
+ success = false;
+ }
+#endif // WAKE_HANDSHAKE_ENABLE
+ } else {
+ CHPP_LOGE("Wake handshaking start timed out");
+ success = false;
+ mWakeOutGpio.Clear();
+ }
+
+ mTransactionPending = false;
+ }
+
+ // Re-enable the interrupt to handle transaction requests.
+ mWakeInGpi.SetTriggerFunction(GPIAoC::GPI_DISABLE);
+ mWakeInGpi.SetInterruptHandler(onTransactionRequestInterrupt, this);
+ // TODO: Handle potential cases where we miss a rising edge
+ mWakeInGpi.SetTriggerFunction(GPIAoC::GPI_RISING_EDGE);
+
+ // Remove the TX packet to allow subsequent transmission.
+ clearTxPacket();
+
+ return success;
+}
+
+void UartLinkManager::pullRxSamples() {
+ int ch;
+ while ((ch = mUart->GetChar()) != EOF && mRxBufIndex < kRxBufSize) {
+ mRxBuf[mRxBufIndex++] = static_cast<uint8_t>(ch);
+ }
+}
+
+void UartLinkManager::processRxSamples() {
+ mUart->DisableRxInterrupt();
+ pullRxSamples();
+
+ chppRxDataCb(mTransportContext, const_cast<uint8_t *>(mRxBuf), mRxBufIndex);
+ mRxBufIndex = 0;
+ mUart->EnableRxInterrupt();
+}
+
+bool UartLinkManager::hasTxPacket() const {
+ return mCurrentBuffer != nullptr && mUart != nullptr;
+}
+
+void UartLinkManager::clearTxPacket() {
+ mCurrentBuffer = nullptr;
+}
+
+} // namespace chpp
diff --git a/chpp/platform/aoc/condition_variable.cc b/chpp/platform/aoc/condition_variable.cc
new file mode 100644
index 0000000..e4d9aca
--- /dev/null
+++ b/chpp/platform/aoc/condition_variable.cc
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#include "chpp/platform/platform_condition_variable.h"
+
+#include "chpp/platform/log.h"
+#include "chre/platform/fatal_error.h"
+#include "efw/include/interrupt_controller.h"
+#include "efw/include/timer.h"
+
+namespace {
+
+bool wait(struct ChppConditionVariable *cv, struct ChppMutex *mutex) {
+ chppMutexUnlock(mutex);
+ BaseType_t rc = xSemaphoreTake(cv->semaphoreHandle, portMAX_DELAY);
+ chppMutexLock(mutex);
+
+ return (rc == pdTRUE);
+}
+
+bool timeoutCallback(void *context) {
+ auto *cv = static_cast<struct ChppConditionVariable *>(context);
+ chppPlatformConditionVariableSignal(cv);
+ return false; // one-shot
+}
+
+} // anonymous namespace
+
+void chppPlatformConditionVariableInit(struct ChppConditionVariable *cv) {
+ cv->semaphoreHandle = xSemaphoreCreateBinaryStatic(&cv->staticSemaphore);
+ if (cv->semaphoreHandle == NULL) {
+ FATAL_ERROR("Failed to create cv semaphore");
+ }
+}
+
+void chppPlatformConditionVariableDeinit(struct ChppConditionVariable *cv) {
+ if (cv->semaphoreHandle != NULL) {
+ vSemaphoreDelete(cv->semaphoreHandle);
+ }
+}
+
+bool chppPlatformConditionVariableWait(struct ChppConditionVariable *cv,
+ struct ChppMutex *mutex) {
+ return wait(cv, mutex);
+}
+
+bool chppPlatformConditionVariableTimedWait(struct ChppConditionVariable *cv,
+ struct ChppMutex *mutex,
+ uint64_t timeoutNs) {
+ bool success = false;
+ Timer *timer = Timer::Instance();
+ const TickType_t timeoutTicks =
+ static_cast<TickType_t>(timer->NsToTicks(timeoutNs));
+
+ void *timerHandle;
+ int rc =
+ timer->EventAddAtOffset(timeoutTicks, timeoutCallback, cv, &timerHandle);
+ if (rc != 0) {
+ CHPP_LOGE("Failed to set cv timeout timer");
+ } else {
+ success = wait(cv, mutex) && (timer->EventRemove(timerHandle) == 0);
+ }
+
+ return success;
+}
+
+void chppPlatformConditionVariableSignal(struct ChppConditionVariable *cv) {
+ if (InterruptController::Instance()->IsInterruptContext()) {
+ BaseType_t xHigherPriorityTaskWoken = 0;
+ xSemaphoreGiveFromISR(cv->semaphoreHandle, &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+ } else {
+ xSemaphoreGive(cv->semaphoreHandle);
+ }
+}
diff --git a/chpp/platform/aoc/include/chpp/platform/chpp_init.h b/chpp/platform/aoc/include/chpp/platform/chpp_init.h
new file mode 100644
index 0000000..35aef3a
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/chpp_init.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_PLATFORM_INIT_H_
+#define CHPP_PLATFORM_INIT_H_
+
+#include "chpp/app.h"
+
+namespace chpp {
+
+/**
+ * Initializes all CHPP instance and threads.
+ */
+void init();
+
+/**
+ * Deinitializes all CHPP instance and threads.
+ */
+void deinit();
+
+/**
+ * @return The ChppAppState associated with the provided link type.
+ */
+ChppAppState *getChppAppState(ChppLinkType linkType);
+
+} // namespace chpp
+
+#endif // CHPP_PLATFORM_INIT_H_
diff --git a/chpp/platform/aoc/include/chpp/platform/chpp_task_util.h b/chpp/platform/aoc/include/chpp/platform/chpp_task_util.h
new file mode 100644
index 0000000..98db803
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/chpp_task_util.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_PLATFORM_TASK_UTIL_H_
+#define CHPP_PLATFORM_TASK_UTIL_H_
+
+#include "FreeRTOS.h"
+#include "semphr.h"
+
+namespace chpp {
+
+/**
+ * A utility class for task related operations.
+ */
+class TaskUtil {
+ public:
+ TaskUtil();
+ ~TaskUtil();
+
+ /**
+ * Suspends the task for a specified period of time.
+ *
+ * @param suspendTimeNs The time in nanoseconds to suspend.
+ */
+ void suspend(uint64_t suspendTimeNs);
+
+ private:
+ //! The semaphore handle
+ SemaphoreHandle_t mCvSemaphoreHandle;
+ //! Buffer used to store state used by the semaphore.
+ StaticSemaphore_t mSemaphoreBuffer;
+};
+
+} // namespace chpp
+
+#endif // CHPP_PLATFORM_TASK_UTIL_H_
diff --git a/chpp/platform/aoc/include/chpp/platform/chpp_uart_link_manager.h b/chpp/platform/aoc/include/chpp/platform/chpp_uart_link_manager.h
new file mode 100644
index 0000000..38f1c61
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/chpp_uart_link_manager.h
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_PLATFORM_UART_LINK_MANAGER_H_
+#define CHPP_PLATFORM_UART_LINK_MANAGER_H_
+
+#include "FreeRTOS.h"
+#include "chpp/condition_variable.h"
+#include "chpp/link.h"
+#include "chpp/mutex.h"
+#include "chpp/platform/chpp_task_util.h"
+#include "chpp/transport.h"
+#include "chre/platform/atomic.h"
+#include "chre/util/non_copyable.h"
+#include "chre/util/time.h"
+#include "gpi_aoc.h"
+#include "gpio_aoc.h"
+#include "uart.h"
+
+namespace chpp {
+
+/**
+ * A class to manage a CHPP link to a remote CHPP endpoint using UART as the
+ * physical layer. Each physical link must instantiate its own instance of
+ * the UartLinkManager.
+ *
+ * This class implements the wake handshaking protocol as follows. Each endpoint
+ * is expected to have a wake_in and wake_out GPIO and a bidirectional UART
+ * link.
+ *
+ * 1) A transaction begins by a request to transmit a packet from either
+ * end of the link. If the local endpoint has data to transmit, it must notify
+ * the remote endpoint by asserting its wake_out GPIO. Consequently, a request
+ * from the remote endpoint is driven by an IRQ from the wake_in GPIO.
+ *
+ * 2) If a packet is pending, it can be transmitted once the remote end has
+ * asserted its wake_out GPIO. Otherwise, the wake_out GPIO pulse should be
+ * asserted for at least the duration to satisfy the pulse width requirements of
+ * the specification.
+ *
+ * 3) The wake_out GPIO can be deasserted once the transmission is complete (if
+ * any). No transaction can begin until the current one has completed, and both
+ * GPIOs have been deasserted.
+ */
+class UartLinkManager : public chre::NonCopyable {
+ public:
+ /**
+ * @param context The context pointer of the CHPP transport.
+ * @param uart The pointer to the UART instance.
+ * @param wakeOutPinNumber The pin number of the wake_out GPIO.
+ * @param wakeInGpiNumber The GPI number of the wake_in GPIO.
+ */
+ UartLinkManager(struct ChppTransportState *context, UART *uart,
+ uint8_t wakeOutPinNumber, uint8_t wakeInGpiNumber);
+
+ /**
+ * This method must be called before invoking the rest of the public methods
+ * in this class.
+ *
+ * @param handle The task handle that this manager will run in.
+ */
+ void init(TaskHandle_t handle);
+
+ /**
+ * Resets the state and disables the UartLinkManager. init() must be called
+ * after invoking this method to use this class again.
+ */
+ void deinit();
+
+ /**
+ * @param buf The non-null pointer to the buffer.
+ * @param len The length of the above buffer.
+ *
+ * @return true if the packet was successfully prepared to be transmitted in
+ * the next transaction.
+ */
+ bool prepareTxPacket(uint8_t *buf, size_t len);
+
+ /**
+ * Starts a transaction to transmit any pending packets (if any, via a
+ * previous call to prepareTxPacket()), and handles the wake handshaking
+ * protocol.
+ *
+ * @return true if the transaction succeeded.
+ */
+ bool startTransaction();
+
+ /**
+ * Pulls RX data from the UART peripheral.
+ */
+ void pullRxSamples();
+
+ /**
+ * Same as pullRxSamples() but also sends the data to the CHPP transport.
+ */
+ void processRxSamples();
+
+ struct ChppTransportState *getTransportContext() const {
+ return mTransportContext;
+ }
+
+ UART *getUart() const {
+ return mUart;
+ }
+
+ GPIAoC *getWakeInGpi() {
+ return &mWakeInGpi;
+ }
+
+ void setTransactionPending() {
+ mTransactionPending = true;
+ }
+
+ TaskHandle_t getTaskHandle() const {
+ return mTaskHandle;
+ }
+
+ /**
+ * Waits for a wake handshaking IRQ to trigger, with a specified timeout.
+ *
+ * @param timeoutNs The timeout in nanoseconds.
+ *
+ * @return false if timed out
+ */
+ bool waitForHandshakeIrq(uint64_t timeoutNs);
+
+ bool isRxBufferFull() const {
+ return mRxBufIndex == kRxBufSize;
+ }
+
+ /**
+ * GPIO pin numbers to be used in the argument of UartLinkManager, which
+ * defines the physical pin used for the wake_out GPIO.
+ */
+ static constexpr uint8_t kWifiWakeOutGpioPinNumber = 43;
+ static constexpr uint8_t kGnssWakeOutGpioPinNumber = 86;
+ static constexpr uint8_t kWwanWakeOutGpioPinNumber = 84;
+
+ /**
+ * GPI numbers to be used in the argument of UartLinkManager, which defines
+ * the GPI used to generate the wake_in interrupts.
+ */
+ static constexpr uint8_t kWifiWakeInGpiNumber = 46;
+ static constexpr uint8_t kGnssWakeInGpiNumber = 44;
+ static constexpr uint8_t kWwanWakeInGpiNumber = 42;
+
+ private:
+ TaskHandle_t mTaskHandle = nullptr;
+
+ struct ChppTransportState *mTransportContext = nullptr;
+
+ UART *mUart = nullptr;
+
+ GPIOAoC mWakeOutGpio;
+
+ GPIAoC mWakeInGpi;
+
+ //! The pointer to the currently pending TX packet.
+ uint8_t *mCurrentBuffer = nullptr;
+ size_t mCurrentBufferLen = 0;
+
+ //! The temporary buffer to store RX data.
+ //! NOTE: Access to these variables must be done when the RX interrupt is
+ //! disabled.
+ static constexpr size_t kRxBufSize = CHPP_PLATFORM_LINK_RX_MTU_BYTES;
+ volatile uint8_t mRxBuf[kRxBufSize];
+ volatile size_t mRxBufIndex = 0;
+
+ //! The timeout for waiting on the remote to indicate transaction readiness
+ //! (t_start per specifications).
+ static constexpr uint64_t kStartTimeoutNs =
+ 100 * chre::kOneMillisecondInNanoseconds;
+
+ //! The timeout for waiting on the remote to indicate transaction ended
+ //! (t_end per specifications).
+ static constexpr uint64_t kEndTimeoutNs = chre::kOneSecondInNanoseconds;
+
+ //! The minimum amount of time to assert the wake_out GPIO (t_pulse per
+ //! specifications).
+ static constexpr uint64_t kPulseTimeNs =
+ 100 * chre::kOneMicrosecondInNanoseconds;
+
+ chre::AtomicBool mTransactionPending{false};
+
+ TaskUtil mTaskUtil;
+
+ /**
+ * @return if a TX packet is pending transmission.
+ */
+ bool hasTxPacket() const;
+
+ /**
+ * Clears a pending TX packet.
+ */
+ void clearTxPacket();
+};
+
+} // namespace chpp
+
+#endif // CHPP_PLATFORM_UART_LINK_MANAGER_H_
diff --git a/chpp/platform/aoc/include/chpp/platform/log.h b/chpp/platform/aoc/include/chpp/platform/log.h
new file mode 100644
index 0000000..7228fd8
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/log.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_LOG_H_
+#define CHPP_LOG_H_
+
+#include <chre.h>
+#include <inttypes.h>
+
+#include "FreeRTOS.h"
+#include "task.h"
+
+#ifndef PRIuSIZE
+#define PRIuSIZE "zu"
+#endif
+
+#define CHPP_AOC_LOG(level, fmt, ...) \
+ chreLog(level, "%s: " fmt, pcTaskGetName(xTaskGetCurrentTaskHandle()), \
+ ##__VA_ARGS__)
+
+#define CHPP_LOGE(fmt, ...) CHPP_AOC_LOG(CHRE_LOG_ERROR, fmt, ##__VA_ARGS__)
+#define CHPP_LOGW(fmt, ...) CHPP_AOC_LOG(CHRE_LOG_WARN, fmt, ##__VA_ARGS__)
+#define CHPP_LOGI(fmt, ...) CHPP_AOC_LOG(CHRE_LOG_INFO, fmt, ##__VA_ARGS__)
+#define CHPP_LOGD(fmt, ...) CHPP_AOC_LOG(CHRE_LOG_DEBUG, fmt, ##__VA_ARGS__)
+
+#define CHPP_LOG_OOM(fmt, ...) \
+ CHPP_AOC_LOG(CHRE_LOG_ERROR, "(OOM) " fmt, ##__VA_ARGS__)
+
+#endif // CHPP_LOG_H_
diff --git a/chpp/platform/aoc/include/chpp/platform/platform_condition_variable.h b/chpp/platform/aoc/include/chpp/platform/platform_condition_variable.h
new file mode 100644
index 0000000..79e4ccf
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/platform_condition_variable.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_PLATFORM_CONDITION_VARIABLE_H_
+#define CHPP_PLATFORM_CONDITION_VARIABLE_H_
+
+#include <stdbool.h>
+
+#include "FreeRTOS.h"
+#include "chpp/mutex.h"
+#include "semphr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ChppConditionVariable {
+ SemaphoreHandle_t semaphoreHandle;
+ StaticSemaphore_t staticSemaphore;
+};
+
+/**
+ * Platform implementation of chppConditionVariableInit().
+ */
+void chppPlatformConditionVariableInit(struct ChppConditionVariable *cv);
+
+/**
+ * Platform implementation of chppConditionVariableDeinit().
+ */
+void chppPlatformConditionVariableDeinit(struct ChppConditionVariable *cv);
+
+/**
+ * Platform implementation of chppConditionVariable[Timed]Wait().
+ */
+bool chppPlatformConditionVariableWait(struct ChppConditionVariable *cv,
+ struct ChppMutex *mutex);
+bool chppPlatformConditionVariableTimedWait(struct ChppConditionVariable *cv,
+ struct ChppMutex *mutex,
+ uint64_t timeoutNs);
+
+/**
+ * Platform implementation of chppConditionVariableSignal().
+ */
+void chppPlatformConditionVariableSignal(struct ChppConditionVariable *cv);
+
+static inline void chppConditionVariableInit(struct ChppConditionVariable *cv) {
+ chppPlatformConditionVariableInit(cv);
+}
+
+static inline void chppConditionVariableDeinit(
+ struct ChppConditionVariable *cv) {
+ chppPlatformConditionVariableDeinit(cv);
+}
+
+static inline bool chppConditionVariableWait(struct ChppConditionVariable *cv,
+ struct ChppMutex *mutex) {
+ return chppPlatformConditionVariableWait(cv, mutex);
+}
+
+static inline bool chppConditionVariableTimedWait(
+ struct ChppConditionVariable *cv, struct ChppMutex *mutex,
+ uint64_t timeoutNs) {
+ return chppPlatformConditionVariableTimedWait(cv, mutex, timeoutNs);
+}
+
+static inline void chppConditionVariableSignal(
+ struct ChppConditionVariable *cv) {
+ chppPlatformConditionVariableSignal(cv);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CHPP_PLATFORM_CONDITION_VARIABLE_H_
diff --git a/chpp/platform/aoc/include/chpp/platform/platform_link.h b/chpp/platform/aoc/include/chpp/platform/platform_link.h
new file mode 100644
index 0000000..e3b607e
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/platform_link.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_PLATFORM_LINK_H_
+#define CHPP_PLATFORM_LINK_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//! A signal to use when there is RX data to process at the UART.
+#define CHPP_TRANSPORT_SIGNAL_LINK_RX_PROCESS UINT32_C(1 << 16)
+
+//! A signal to use when there is an interrupt from the wake_in GPIO.
+#define CHPP_TRANSPORT_SIGNAL_LINK_WAKE_IN_IRQ UINT32_C(1 << 17)
+
+//! A signal to use when waiting for the handshaking wake_in GPIO IRQs.
+//! This signal is reserved for use internally in the chpp::UartLinkManager.
+#define CHPP_TRANSPORT_SIGNAL_LINK_HANDSHAKE_IRQ UINT32_C(1 << 18)
+
+#define CHPP_PLATFORM_LINK_TX_MTU_BYTES 1280
+#define CHPP_PLATFORM_LINK_RX_MTU_BYTES 1280
+
+#define CHPP_PLATFORM_TRANSPORT_TIMEOUT_MS 1000
+
+enum ChppLinkType {
+ CHPP_LINK_TYPE_WIFI = 0,
+ CHPP_LINK_TYPE_GNSS,
+ CHPP_LINK_TYPE_WWAN,
+ CHPP_LINK_TYPE_TOTAL,
+};
+
+struct ChppPlatformLinkParameters {
+ //! Must be of type chpp::UartLinkManager
+ void *uartLinkManager;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CHPP_PLATFORM_LINK_H_
diff --git a/chpp/platform/aoc/include/chpp/platform/platform_mutex.h b/chpp/platform/aoc/include/chpp/platform/platform_mutex.h
new file mode 100644
index 0000000..c745478
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/platform_mutex.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_PLATFORM_MUTEX_H_
+#define CHPP_PLATFORM_MUTEX_H_
+
+#include "chpp/platform/log.h"
+#include "FreeRTOS.h"
+#include "semphr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Encapsulates a mutex handle and its associated semaphore structure.
+ * The static semaphore is used to avoid heap allocations.
+ */
+struct ChppMutex {
+ SemaphoreHandle_t semaphoreHandle;
+ StaticSemaphore_t staticSemaphore;
+};
+
+static inline void chppMutexInit(struct ChppMutex *mutex) {
+ mutex->semaphoreHandle = xSemaphoreCreateMutexStatic(&mutex->staticSemaphore);
+}
+
+static inline void chppMutexDeinit(struct ChppMutex *mutex) {
+ if (mutex->semaphoreHandle) {
+ vSemaphoreDelete(mutex->semaphoreHandle);
+ }
+}
+
+static inline void chppMutexLock(struct ChppMutex *mutex) {
+ TickType_t blockForever = portMAX_DELAY;
+ if (pdTRUE != xSemaphoreTake(mutex->semaphoreHandle, blockForever)) {
+ // TODO: Use CHPP_ASSERT
+ CHPP_LOGE("Failed to lock mutex");
+ }
+}
+
+static inline void chppMutexUnlock(struct ChppMutex *mutex) {
+ if (pdTRUE != xSemaphoreGive(mutex->semaphoreHandle)) {
+ CHPP_LOGE("Failed to properly unlock mutex!");
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CHPP_PLATFORM_MUTEX_H_
diff --git a/chpp/platform/aoc/include/chpp/platform/platform_notifier.h b/chpp/platform/aoc/include/chpp/platform/platform_notifier.h
new file mode 100644
index 0000000..47364c4
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/platform_notifier.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_PLATFORM_SYNC_H_
+#define CHPP_PLATFORM_SYNC_H_
+
+#include "FreeRTOS.h"
+#include "task.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ChppNotifier {
+ //! The task that will be notified.
+ TaskHandle_t task;
+};
+
+/**
+ * Platform implementation of chppNotifierInit()
+ */
+void chppPlatformNotifierInit(struct ChppNotifier *notifier);
+
+/**
+ * Platform implementation of chppNotifierDeinit()
+ */
+void chppPlatformNotifierDeinit(struct ChppNotifier *notifier);
+
+/**
+ * Platform implementation of chppNotifierWait()
+ */
+uint32_t chppPlatformNotifierWait(struct ChppNotifier *notifier);
+
+/**
+ * Platform implementation of chppNotifierSignal()
+ */
+void chppPlatformNotifierSignal(struct ChppNotifier *notifier, uint32_t signal);
+
+static inline void chppNotifierInit(struct ChppNotifier *notifier) {
+ chppPlatformNotifierInit(notifier);
+}
+
+static inline void chppNotifierDeinit(struct ChppNotifier *notifier) {
+ chppPlatformNotifierDeinit(notifier);
+}
+
+static inline uint32_t chppNotifierWait(struct ChppNotifier *notifier) {
+ return chppPlatformNotifierWait(notifier);
+}
+
+static inline void chppNotifierSignal(struct ChppNotifier *notifier,
+ uint32_t signal) {
+ chppPlatformNotifierSignal(notifier, signal);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CHPP_PLATFORM_SYNC_H_
diff --git a/chpp/platform/aoc/include/chpp/platform/platform_time.h b/chpp/platform/aoc/include/chpp/platform/platform_time.h
new file mode 100644
index 0000000..a9c17a1
--- /dev/null
+++ b/chpp/platform/aoc/include/chpp/platform/platform_time.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef CHPP_PLATFORM_TIME_H_
+#define CHPP_PLATFORM_TIME_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Platform implementation of chppGetCurrentTimeNs().
+ */
+uint64_t chppPlatformGetCurrentTimeNs();
+
+static inline uint64_t chppGetCurrentTimeNs(void) {
+ return chppPlatformGetCurrentTimeNs();
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CHPP_PLATFORM_TIME_H_
diff --git a/chpp/platform/aoc/link.cc b/chpp/platform/aoc/link.cc
new file mode 100644
index 0000000..76f3f86
--- /dev/null
+++ b/chpp/platform/aoc/link.cc
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "chpp/link.h"
+
+#include "chpp/macros.h"
+#include "chpp/platform/chpp_uart_link_manager.h"
+#include "chpp/transport.h"
+
+using chpp::UartLinkManager;
+
+void chppPlatformLinkInit(struct ChppPlatformLinkParameters *params) {
+ UartLinkManager *manager =
+ static_cast<UartLinkManager *>(params->uartLinkManager);
+ manager->init(xTaskGetCurrentTaskHandle());
+}
+
+void chppPlatformLinkDeinit(struct ChppPlatformLinkParameters *params) {
+ UartLinkManager *manager =
+ static_cast<UartLinkManager *>(params->uartLinkManager);
+ manager->deinit();
+}
+
+enum ChppLinkErrorCode chppPlatformLinkSend(
+ struct ChppPlatformLinkParameters *params, uint8_t *buf, size_t len) {
+ bool success = false;
+
+ UartLinkManager *manager =
+ static_cast<UartLinkManager *>(params->uartLinkManager);
+ if (manager->prepareTxPacket(buf, len)) {
+ // TODO: Consider some other cases where asynchronous reporting
+ // is necessary, e.g. delayed transaction when waiting for a
+ // timeout.
+ success = manager->startTransaction();
+ }
+
+ return success ? CHPP_LINK_ERROR_NONE_SENT : CHPP_LINK_ERROR_UNSPECIFIED;
+}
+
+void chppPlatformLinkDoWork(struct ChppPlatformLinkParameters *params,
+ uint32_t signal) {
+ UartLinkManager *manager =
+ static_cast<UartLinkManager *>(params->uartLinkManager);
+
+ if (signal & CHPP_TRANSPORT_SIGNAL_LINK_WAKE_IN_IRQ) {
+ // This indicates that the remote end wants to start a transaction.
+ manager->startTransaction();
+ }
+ if (signal & CHPP_TRANSPORT_SIGNAL_LINK_RX_PROCESS) {
+ manager->processRxSamples();
+ }
+}
+
+void chppPlatformLinkReset(struct ChppPlatformLinkParameters *params) {
+ UartLinkManager *manager =
+ static_cast<UartLinkManager *>(params->uartLinkManager);
+ manager->deinit();
+ manager->init(xTaskGetCurrentTaskHandle());
+}
diff --git a/chpp/platform/aoc/memory.cc b/chpp/platform/aoc/memory.cc
new file mode 100644
index 0000000..eff5855
--- /dev/null
+++ b/chpp/platform/aoc/memory.cc
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#include "chpp/memory.h"
+#include "chpp/macros.h"
+
+#include "chre/platform/memory.h"
+#include "string.h"
+
+void *chppMalloc(const size_t size) {
+ return chre::memoryAlloc(size);
+}
+
+void chppFree(void *ptr) {
+ chre::memoryFree(ptr);
+}
+
+void *chppRealloc(void *oldPtr, const size_t newSize, const size_t oldSize) {
+ if (newSize == 0) {
+ chppFree(oldPtr);
+ return nullptr;
+ }
+ if (newSize == oldSize) {
+ return oldPtr;
+ }
+
+ void *ptr = chppMalloc(newSize);
+ if (ptr != nullptr) {
+ memcpy(ptr, oldPtr, newSize > oldSize ? oldSize : newSize);
+ chppFree(oldPtr);
+ }
+
+ return ptr;
+}
diff --git a/chpp/platform/aoc/notifier.cc b/chpp/platform/aoc/notifier.cc
new file mode 100644
index 0000000..3a7eac4
--- /dev/null
+++ b/chpp/platform/aoc/notifier.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include <stdbool.h>
+
+#include "chpp/macros.h"
+#include "chpp/memory.h"
+#include "chpp/notifier.h"
+#include "efw/include/interrupt_controller.h"
+
+void chppPlatformNotifierInit(struct ChppNotifier *notifier) {
+ notifier->task = xTaskGetCurrentTaskHandle();
+ xTaskNotifyStateClear(notifier->task);
+}
+
+void chppPlatformNotifierDeinit(struct ChppNotifier *notifier) {}
+
+uint32_t chppPlatformNotifierWait(struct ChppNotifier *notifier) {
+ uint32_t signal;
+ xTaskNotifyWait(0 /* ulBitsToClearOnEntry */,
+ UINT32_MAX /* ulBitsToClearOnExit */, &signal,
+ portMAX_DELAY /* xTicksToWait */);
+ return signal;
+}
+
+void chppPlatformNotifierSignal(struct ChppNotifier *notifier,
+ uint32_t signal) {
+ if (InterruptController::Instance()->IsInterruptContext()) {
+ BaseType_t xHigherPriorityTaskWoken = 0;
+ xTaskNotifyFromISR(notifier->task, signal, eSetBits,
+ &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+ } else {
+ xTaskNotify(notifier->task, signal, eSetBits);
+ }
+}
diff --git a/chpp/platform/aoc/time.cc b/chpp/platform/aoc/time.cc
new file mode 100644
index 0000000..51ec0d9
--- /dev/null
+++ b/chpp/platform/aoc/time.cc
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#include "chpp/platform/platform_time.h"
+
+#include "chre/platform/system_time.h"
+
+uint64_t chppPlatformGetCurrentTimeNs() {
+ return chre::SystemTime::getMonotonicTime().toRawNanoseconds();
+}
diff --git a/chre_daemon.rc b/chre_daemon.rc
index 120da10..544d3fe 100644
--- a/chre_daemon.rc
+++ b/chre_daemon.rc
@@ -17,7 +17,7 @@
service vendor.chre /vendor/bin/chre
class late_start
user context_hub
- group wakelock context_hub
+ group wakelock context_hub system
capabilities BLOCK_SUSPEND
socket chre seqpacket 0660 root context_hub
shutdown critical
diff --git a/core/include/chre/core/event_loop_common.h b/core/include/chre/core/event_loop_common.h
index 03765f0..ea1cdbc 100644
--- a/core/include/chre/core/event_loop_common.h
+++ b/core/include/chre/core/event_loop_common.h
@@ -53,6 +53,7 @@
SettingChangeEvent,
GnssLocationReportEvent,
GnssMeasurementReportEvent,
+ TimerSyncRequest,
};
//! The function signature of a system callback mirrors the CHRE event free
diff --git a/external/external.mk b/external/external.mk
index 4a04a13..c95b43e 100644
--- a/external/external.mk
+++ b/external/external.mk
@@ -29,7 +29,7 @@
#
# Kiss FFT
#
-
+ifeq ($(CHRE_AUDIO_SUPPORT_ENABLED), true)
# Common Compiler Flags ########################################################
# Include paths.
@@ -42,3 +42,4 @@
COMMON_SRCS += external/kiss_fft/kiss_fft.c
COMMON_SRCS += external/kiss_fft/kiss_fftr.c
+endif
diff --git a/external/kiss_fft/_kiss_fft_guts.h b/external/kiss_fft/_kiss_fft_guts.h
index e83771f..6aa102d 100644
--- a/external/kiss_fft/_kiss_fft_guts.h
+++ b/external/kiss_fft/_kiss_fft_guts.h
@@ -55,9 +55,13 @@
#define SAMP_MIN -SAMP_MAX
#if defined(CHECK_OVERFLOW)
+#ifdef CHRE_KISS_FFT_CAN_USE_STDIO
# define CHECK_OVERFLOW_OP(a,op,b) \
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
+#else
+#define CHECK_OVERFLOW(a, op, b) {}
+#endif
#endif
@@ -145,10 +149,13 @@
}while(0)
+#ifdef CHRE_KISS_FFT_CAN_USE_STDIO
/* a debugging function */
#define pcpx(c)\
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
-
+#else
+#define pcpx(c) {}
+#endif
#ifdef KISS_FFT_USE_ALLOCA
// define this to allow use of alloca instead of malloc for temporary buffers
diff --git a/external/kiss_fft/kiss_fft.c b/external/kiss_fft/kiss_fft.c
index 7f439c2..c1ea7e3 100644
--- a/external/kiss_fft/kiss_fft.c
+++ b/external/kiss_fft/kiss_fft.c
@@ -18,6 +18,14 @@
fixed or floating point complex numbers. It also delares the kf_ internal functions.
*/
+// CHRE modifications begin
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-align"
+#pragma clang diagnostic ignored "-Wbad-function-cast"
+#endif
+// CHRE modifications end
+
static void kf_bfly2(
kiss_fft_cpx * Fout,
const size_t fstride,
@@ -406,3 +414,9 @@
}
return n;
}
+
+// CHRE modifications begin
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+// CHRE modifications end
diff --git a/external/kiss_fft/kiss_fftr.c b/external/kiss_fft/kiss_fftr.c
index f31f23e..0dad8a2 100644
--- a/external/kiss_fft/kiss_fftr.c
+++ b/external/kiss_fft/kiss_fftr.c
@@ -15,6 +15,14 @@
#include "kiss_fftr.h"
#include "_kiss_fft_guts.h"
+// CHRE modifications begin
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-align"
+#pragma clang diagnostic ignored "-Wbad-function-cast"
+#endif
+// CHRE modifications end
+
struct kiss_fftr_state{
kiss_fft_cfg substate;
kiss_fft_cpx * tmpbuf;
@@ -31,7 +39,9 @@
size_t subsize, memneeded;
if (nfft & 1) {
+#if CHRE_KISS_FFT_CAN_USE_STDIO
fprintf(stderr,"Real FFT optimization must be even.\n");
+#endif
return NULL;
}
nfft >>= 1;
@@ -71,8 +81,12 @@
kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
if ( st->substate->inverse) {
+#if CHRE_KISS_FFT_CAN_USE_STDIO
fprintf(stderr,"kiss fft usage error: improper alloc\n");
exit(1);
+#else
+ return;
+#endif
}
ncfft = st->substate->nfft;
@@ -126,8 +140,12 @@
int k, ncfft;
if (st->substate->inverse == 0) {
+#if CHRE_KISS_FFT_CAN_USE_STDIO
fprintf (stderr, "kiss fft usage error: improper alloc\n");
exit (1);
+#else
+ return;
+#endif
}
ncfft = st->substate->nfft;
@@ -157,3 +175,9 @@
}
kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
}
+
+// CHRE modifications begin
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+// CHRE modifications end
diff --git a/host/common/audio_stress_test/audio_stress_test.cc b/host/common/audio_stress_test/audio_stress_test.cc
index f824786..0073718 100644
--- a/host/common/audio_stress_test/audio_stress_test.cc
+++ b/host/common/audio_stress_test/audio_stress_test.cc
@@ -15,6 +15,7 @@
*/
#include "chre/util/nanoapp/app_id.h"
+#include "chre/util/system/napp_header_utils.h"
#include "chre_host/host_protocol_host.h"
#include "chre_host/log.h"
#include "chre_host/socket_client.h"
@@ -92,12 +93,15 @@
return;
}
+ // All loaded nanoapps must be signed currently.
+ uint32_t appFlags = CHRE_NAPP_HEADER_SIGNED;
+
// Perform loading with 1 fragment for simplicity
FlatBufferBuilder builder(size + 128);
FragmentedLoadTransaction transaction =
FragmentedLoadTransaction(1 /* transactionId */, appId, appVersion,
- 0x01000000 /* targetApiVersion */, buffer,
- buffer.size() /* fragmentSize */);
+ appFlags, 0x01000000 /* targetApiVersion */,
+ buffer, buffer.size() /* fragmentSize */);
HostProtocolHost::encodeFragmentedLoadNanoappRequest(
builder, transaction.getNextRequest());
diff --git a/host/common/fragmented_load_transaction.cc b/host/common/fragmented_load_transaction.cc
index cf1f637..c3c26d3 100644
--- a/host/common/fragmented_load_transaction.cc
+++ b/host/common/fragmented_load_transaction.cc
@@ -48,8 +48,8 @@
FragmentedLoadTransaction::FragmentedLoadTransaction(
uint32_t transactionId, uint64_t appId, uint32_t appVersion,
- uint32_t targetApiVersion, const std::vector<uint8_t> &appBinary,
- size_t fragmentSize) {
+ uint32_t appFlags, uint32_t targetApiVersion,
+ const std::vector<uint8_t> &appBinary, size_t fragmentSize) {
mTransactionId = transactionId;
// Start with fragmentId at 1 since 0 is used to indicate
@@ -59,8 +59,9 @@
do {
if (fragmentId == 1) {
mFragmentRequests.emplace_back(
- fragmentId++, transactionId, appId, appVersion, targetApiVersion,
- appBinary.size(), getSubVector(appBinary, byteIndex, fragmentSize));
+ fragmentId++, transactionId, appId, appVersion, appFlags,
+ targetApiVersion, appBinary.size(),
+ getSubVector(appBinary, byteIndex, fragmentSize));
} else {
mFragmentRequests.emplace_back(
fragmentId++, transactionId,
diff --git a/host/common/host_protocol_host.cc b/host/common/host_protocol_host.cc
index 21904f5..365a237 100644
--- a/host/common/host_protocol_host.cc
+++ b/host/common/host_protocol_host.cc
@@ -103,8 +103,8 @@
const FragmentedLoadRequest &request) {
encodeLoadNanoappRequestForBinary(
builder, request.transactionId, request.appId, request.appVersion,
- request.targetApiVersion, request.binary, request.fragmentId,
- request.appTotalSizeBytes);
+ request.appFlags, request.targetApiVersion, request.binary,
+ request.fragmentId, request.appTotalSizeBytes);
}
void HostProtocolHost::encodeNanoappListRequest(FlatBufferBuilder &builder) {
@@ -170,13 +170,13 @@
void HostProtocolHost::encodeLoadNanoappRequestForBinary(
FlatBufferBuilder &builder, uint32_t transactionId, uint64_t appId,
- uint32_t appVersion, uint32_t targetApiVersion,
+ uint32_t appVersion, uint32_t appFlags, uint32_t targetApiVersion,
const std::vector<uint8_t> &nanoappBinary, uint32_t fragmentId,
size_t appTotalSizeBytes) {
auto appBinary = builder.CreateVector(nanoappBinary);
auto request = fbs::CreateLoadNanoappRequest(
builder, transactionId, appId, appVersion, targetApiVersion, appBinary,
- fragmentId, appTotalSizeBytes);
+ fragmentId, appTotalSizeBytes, 0 /* app_binary_file_name */, appFlags);
finalize(builder, fbs::ChreMessage::LoadNanoappRequest, request.Union());
}
diff --git a/host/common/include/chre_host/daemon_base.h b/host/common/include/chre_host/daemon_base.h
index 7423849..469f9a8 100644
--- a/host/common/include/chre_host/daemon_base.h
+++ b/host/common/include/chre_host/daemon_base.h
@@ -66,7 +66,8 @@
* @param message A buffer containing the message
* @param messageLen size of the message buffer in bytes
*/
- virtual void onMessageReceived(unsigned char *message, size_t messageLen) = 0;
+ virtual void onMessageReceived(const unsigned char *message,
+ size_t messageLen) = 0;
/**
* Function to query if a graceful shutdown of CHRE was requested
@@ -77,6 +78,16 @@
return mChreShutdownRequested;
}
+ /**
+ * Loads the supplied file into the provided buffer.
+ *
+ * @param filename The name of the file to load.
+ * @param buffer The buffer to load into.
+ * @return true if successful, false otherwise.
+ */
+ static bool readFileContents(const char *filename,
+ std::vector<uint8_t> *buffer);
+
protected:
//! The host ID to use when preloading nanoapps. This is used before the
//! server is started and is sufficiently high enough so as to not collide
@@ -112,8 +123,9 @@
* @param name The filename of the nanoapp to load.
* @param transactionId The transaction ID to use when loading the app.
*/
- void loadPreloadedNanoapp(const std::string &directory,
- const std::string &name, uint32_t transactionId);
+ virtual void loadPreloadedNanoapp(const std::string &directory,
+ const std::string &name,
+ uint32_t transactionId);
/**
* Sends a preloaded nanoapp filename / metadata to CHRE.
@@ -165,20 +177,11 @@
bool logOnError);
/**
- * Loads the supplied file into the provided buffer.
- *
- * @param filename The name of the file to load.
- * @param buffer The buffer to load into.
- * @return true if successful, false otherwise.
- */
- bool readFileContents(const char *filename, std::vector<uint8_t> *buffer);
-
- /**
* Handles a message that is directed towards the daemon.
*
* @param message The message sent to the daemon.
*/
- void handleDaemonMessage(const uint8_t *message);
+ virtual void handleDaemonMessage(const uint8_t *message);
private:
//! Set to true when we request a graceful shutdown of CHRE
diff --git a/host/common/include/chre_host/fragmented_load_transaction.h b/host/common/include/chre_host/fragmented_load_transaction.h
index 98a4e56..42ce478 100644
--- a/host/common/include/chre_host/fragmented_load_transaction.h
+++ b/host/common/include/chre_host/fragmented_load_transaction.h
@@ -34,22 +34,25 @@
uint32_t transactionId;
uint64_t appId;
uint32_t appVersion;
+ uint32_t appFlags;
uint32_t targetApiVersion;
size_t appTotalSizeBytes;
std::vector<uint8_t> binary;
FragmentedLoadRequest(size_t fragmentId, uint32_t transactionId,
const std::vector<uint8_t> &binary)
- : FragmentedLoadRequest(fragmentId, transactionId, 0, 0, 0, 0, binary) {}
+ : FragmentedLoadRequest(fragmentId, transactionId, 0, 0, 0, 0, 0,
+ binary) {}
FragmentedLoadRequest(size_t fragmentId, uint32_t transactionId,
- uint64_t appId, uint32_t appVersion,
+ uint64_t appId, uint32_t appVersion, uint32_t appFlags,
uint32_t targetApiVersion, size_t appTotalSizeBytes,
const std::vector<uint8_t> &binary)
: fragmentId(fragmentId),
transactionId(transactionId),
appId(appId),
appVersion(appVersion),
+ appFlags(appFlags),
targetApiVersion(targetApiVersion),
appTotalSizeBytes(appTotalSizeBytes),
binary(binary) {}
@@ -69,12 +72,14 @@
* @param transactionId the unique ID of the unfragmented load transaction
* @param appId the unique ID of the nanoapp
* @param appVersion the version of the nanoapp
+ * @param appFlags the flags specified by the nanoapp to be loaded.
* @param targetApiVersion the API version this nanoapp is targeted for
* @param appBinary the nanoapp binary data
* @param fragmentSize the size of each fragment in bytes
*/
FragmentedLoadTransaction(uint32_t transactionId, uint64_t appId,
- uint32_t appVersion, uint32_t targetApiVersion,
+ uint32_t appVersion, uint32_t appFlags,
+ uint32_t targetApiVersion,
const std::vector<uint8_t> &appBinary,
size_t fragmentSize = kDefaultFragmentSize);
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 4e280b6..7eb9317 100644
--- a/host/common/include/chre_host/generated/host_messages_generated.h
+++ b/host/common/include/chre_host/generated/host_messages_generated.h
@@ -1174,13 +1174,15 @@
uint32_t fragment_id;
uint32_t total_app_size;
std::vector<int8_t> app_binary_file_name;
+ uint32_t app_flags;
LoadNanoappRequestT()
: transaction_id(0),
app_id(0),
app_version(0),
target_api_version(0),
fragment_id(0),
- total_app_size(0) {
+ total_app_size(0),
+ app_flags(0) {
}
};
@@ -1233,7 +1235,8 @@
VT_APP_BINARY = 12,
VT_FRAGMENT_ID = 14,
VT_TOTAL_APP_SIZE = 16,
- VT_APP_BINARY_FILE_NAME = 18
+ VT_APP_BINARY_FILE_NAME = 18,
+ VT_APP_FLAGS = 20
};
uint32_t transaction_id() const {
return GetField<uint32_t>(VT_TRANSACTION_ID, 0);
@@ -1289,6 +1292,14 @@
flatbuffers::Vector<int8_t> *mutable_app_binary_file_name() {
return GetPointer<flatbuffers::Vector<int8_t> *>(VT_APP_BINARY_FILE_NAME);
}
+ /// The nanoapp flag values from the nanoapp header defined in
+ /// build/build_template.mk. Refer to that file for more details.
+ uint32_t app_flags() const {
+ return GetField<uint32_t>(VT_APP_FLAGS, 0);
+ }
+ bool mutate_app_flags(uint32_t _app_flags) {
+ return SetField<uint32_t>(VT_APP_FLAGS, _app_flags, 0);
+ }
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint32_t>(verifier, VT_TRANSACTION_ID) &&
@@ -1301,6 +1312,7 @@
VerifyField<uint32_t>(verifier, VT_TOTAL_APP_SIZE) &&
VerifyOffset(verifier, VT_APP_BINARY_FILE_NAME) &&
verifier.VerifyVector(app_binary_file_name()) &&
+ VerifyField<uint32_t>(verifier, VT_APP_FLAGS) &&
verifier.EndTable();
}
LoadNanoappRequestT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
@@ -1336,6 +1348,9 @@
void add_app_binary_file_name(flatbuffers::Offset<flatbuffers::Vector<int8_t>> app_binary_file_name) {
fbb_.AddOffset(LoadNanoappRequest::VT_APP_BINARY_FILE_NAME, app_binary_file_name);
}
+ void add_app_flags(uint32_t app_flags) {
+ fbb_.AddElement<uint32_t>(LoadNanoappRequest::VT_APP_FLAGS, app_flags, 0);
+ }
explicit LoadNanoappRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
@@ -1358,9 +1373,11 @@
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> app_binary = 0,
uint32_t fragment_id = 0,
uint32_t total_app_size = 0,
- flatbuffers::Offset<flatbuffers::Vector<int8_t>> app_binary_file_name = 0) {
+ flatbuffers::Offset<flatbuffers::Vector<int8_t>> app_binary_file_name = 0,
+ uint32_t app_flags = 0) {
LoadNanoappRequestBuilder builder_(_fbb);
builder_.add_app_id(app_id);
+ builder_.add_app_flags(app_flags);
builder_.add_app_binary_file_name(app_binary_file_name);
builder_.add_total_app_size(total_app_size);
builder_.add_fragment_id(fragment_id);
@@ -1380,7 +1397,8 @@
const std::vector<uint8_t> *app_binary = nullptr,
uint32_t fragment_id = 0,
uint32_t total_app_size = 0,
- const std::vector<int8_t> *app_binary_file_name = nullptr) {
+ const std::vector<int8_t> *app_binary_file_name = nullptr,
+ uint32_t app_flags = 0) {
auto app_binary__ = app_binary ? _fbb.CreateVector<uint8_t>(*app_binary) : 0;
auto app_binary_file_name__ = app_binary_file_name ? _fbb.CreateVector<int8_t>(*app_binary_file_name) : 0;
return chre::fbs::CreateLoadNanoappRequest(
@@ -1392,7 +1410,8 @@
app_binary__,
fragment_id,
total_app_size,
- app_binary_file_name__);
+ app_binary_file_name__,
+ app_flags);
}
flatbuffers::Offset<LoadNanoappRequest> CreateLoadNanoappRequest(flatbuffers::FlatBufferBuilder &_fbb, const LoadNanoappRequestT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
@@ -2640,6 +2659,7 @@
{ auto _e = fragment_id(); _o->fragment_id = _e; }
{ auto _e = total_app_size(); _o->total_app_size = _e; }
{ auto _e = app_binary_file_name(); if (_e) { _o->app_binary_file_name.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->app_binary_file_name[_i] = _e->Get(_i); } } }
+ { auto _e = app_flags(); _o->app_flags = _e; }
}
inline flatbuffers::Offset<LoadNanoappRequest> LoadNanoappRequest::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LoadNanoappRequestT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
@@ -2658,6 +2678,7 @@
auto _fragment_id = _o->fragment_id;
auto _total_app_size = _o->total_app_size;
auto _app_binary_file_name = _o->app_binary_file_name.size() ? _fbb.CreateVector(_o->app_binary_file_name) : 0;
+ auto _app_flags = _o->app_flags;
return chre::fbs::CreateLoadNanoappRequest(
_fbb,
_transaction_id,
@@ -2667,7 +2688,8 @@
_app_binary,
_fragment_id,
_total_app_size,
- _app_binary_file_name);
+ _app_binary_file_name,
+ _app_flags);
}
inline LoadNanoappResponseT *LoadNanoappResponse::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
diff --git a/host/common/include/chre_host/host_protocol_host.h b/host/common/include/chre_host/host_protocol_host.h
index ba04c14..107cb58 100644
--- a/host/common/include/chre_host/host_protocol_host.h
+++ b/host/common/include/chre_host/host_protocol_host.h
@@ -196,9 +196,9 @@
*/
static void encodeLoadNanoappRequestForBinary(
flatbuffers::FlatBufferBuilder &builder, uint32_t transactionId,
- uint64_t appId, uint32_t appVersion, uint32_t targetApiVersion,
- const std::vector<uint8_t> &nanoappBinary, uint32_t fragmentId,
- size_t appTotalSizeBytes);
+ uint64_t appId, uint32_t appVersion, uint32_t appFlags,
+ uint32_t targetApiVersion, const std::vector<uint8_t> &nanoappBinary,
+ uint32_t fragmentId, size_t appTotalSizeBytes);
/**
* Encodes a message requesting to load a nanoapp specified by the included
diff --git a/host/common/include/chre_host/log_message_parser_base.h b/host/common/include/chre_host/log_message_parser_base.h
index e949ce3..932c196 100644
--- a/host/common/include/chre_host/log_message_parser_base.h
+++ b/host/common/include/chre_host/log_message_parser_base.h
@@ -46,8 +46,7 @@
return true;
};
- virtual void log(const uint8_t * /* logBuffer */,
- size_t /* logBufferSize */) {}
+ virtual void log(const uint8_t *logBuffer, size_t logBufferSize);
/**
* With verbose logging enabled (via enableVerbose()), dump a
@@ -71,6 +70,21 @@
void emitLogMessage(uint8_t level, uint64_t timestampNanos,
const char *logMessage);
+
+ private:
+ enum LogLevel : uint8_t {
+ ERROR = 1,
+ WARNING = 2,
+ INFO = 3,
+ DEBUG = 4,
+ };
+
+ //! See host_messages.fbs for the definition of this struct.
+ struct LogMessage {
+ enum LogLevel logLevel;
+ uint64_t timestampNanos;
+ char logMessage[];
+ } __attribute__((packed));
};
} // namespace chre
diff --git a/host/common/include/chre_host/tokenized_log_message_parser.h b/host/common/include/chre_host/tokenized_log_message_parser.h
index d82140d..7677b38 100644
--- a/host/common/include/chre_host/tokenized_log_message_parser.h
+++ b/host/common/include/chre_host/tokenized_log_message_parser.h
@@ -18,6 +18,7 @@
#define CHRE_TOKENIZED_LOG_MESSAGE_PARSER_H_
#include <memory>
+#include "chre_host/daemon_base.h"
#include "chre_host/log_message_parser_base.h"
#include "pw_tokenizer/detokenize.h"
@@ -25,12 +26,14 @@
using pw::tokenizer::DetokenizedString;
using pw::tokenizer::Detokenizer;
+namespace android {
namespace chre {
class ChreTokenizedLogMessageParser : public ChreLogMessageParserBase {
public:
virtual bool init() override final {
mDetokenizer = logDetokenizerInit();
+ return mDetokenizer != nullptr;
}
virtual void log(const uint8_t *logBuffer,
@@ -55,7 +58,7 @@
constexpr const char kLogDatabaseFilePath[] =
"/vendor/etc/chre/libchre_log_database.bin";
std::vector<uint8_t> tokenData;
- if (readFileContents(kLogDatabaseFilePath, &tokenData)) {
+ if (ChreDaemonBase::readFileContents(kLogDatabaseFilePath, &tokenData)) {
pw::tokenizer::TokenDatabase database =
pw::tokenizer::TokenDatabase::Create(tokenData);
if (database.ok()) {
@@ -71,7 +74,7 @@
// Log messages are routed through ashLog if tokenized logging
// is disabled, so only parse tokenized log messages here.
- void parseAndEmitTokenizedLogMessages(unsigned char *message,
+ void parseAndEmitTokenizedLogMessages(const uint8_t *message,
unsigned int messageLen,
const Detokenizer *detokenizer) {
if (detokenizer != nullptr) {
@@ -80,24 +83,16 @@
// logs (b/148873804)
constexpr size_t kLogMessageHeaderSize =
1 /*logLevel*/ + sizeof(uint64_t) /*timestamp*/;
-
- const fbs::MessageContainer *container =
- fbs::GetMessageContainer(message);
- const auto *logMessage =
- static_cast<const fbs::LogMessage *>(container->message());
-
- const flatbuffers::Vector<int8_t> &logData = *logMessage->buffer();
- const uint8_t *log = reinterpret_cast<const uint8_t *>(logData.data());
- uint8_t level = *log;
- ++log;
+ uint8_t level = *message;
+ ++message;
uint64_t timestampNanos;
- memcpy(×tampNanos, log, sizeof(uint64_t));
+ memcpy(×tampNanos, message, sizeof(uint64_t));
timestampNanos = le64toh(timestampNanos);
- log += sizeof(uint64_t);
+ message += sizeof(uint64_t);
DetokenizedString detokenizedLog =
- detokenizer->Detokenize(log, messageLen - kLogMessageHeaderSize);
+ detokenizer->Detokenize(message, messageLen - kLogMessageHeaderSize);
std::string decodedLog = detokenizedLog.BestStringWithErrors();
emitLogMessage(level, timestampNanos, decodedLog.c_str());
} else {
@@ -107,5 +102,6 @@
};
} // namespace chre
+} // namespace android
#endif // CHRE_TOKENIZED_LOG_MESSAGE_PARSER_H_
\ No newline at end of file
diff --git a/host/common/log_message_parser_base.cc b/host/common/log_message_parser_base.cc
index 86d70d5..3de5002 100644
--- a/host/common/log_message_parser_base.cc
+++ b/host/common/log_message_parser_base.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <endian.h>
#include <cstdio>
#include "chre_host/log_message_parser_base.h"
@@ -27,14 +28,6 @@
#else
constexpr bool kVerboseLoggingEnabled = false;
#endif
-
-enum ChreLogLevel : uint8_t {
- LOG_LEVEL_ERROR = 0,
- LOG_LEVEL_WARN = 1,
- LOG_LEVEL_INFO = 2,
- LOG_LEVEL_DEBUG = 3
-};
-
} // anonymous namespace
ChreLogMessageParserBase::ChreLogMessageParserBase()
@@ -85,19 +78,33 @@
android_LogPriority ChreLogMessageParserBase::chreLogLevelToAndroidLogPriority(
uint8_t level) {
switch (level) {
- case LOG_LEVEL_ERROR:
+ case LogLevel::ERROR:
return ANDROID_LOG_ERROR;
- case LOG_LEVEL_WARN:
+ case LogLevel::WARNING:
return ANDROID_LOG_WARN;
- case LOG_LEVEL_INFO:
+ case LogLevel::INFO:
return ANDROID_LOG_INFO;
- case LOG_LEVEL_DEBUG:
+ case LogLevel::DEBUG:
return ANDROID_LOG_DEBUG;
default:
return ANDROID_LOG_SILENT;
}
}
+void ChreLogMessageParserBase::log(const uint8_t *logBuffer,
+ size_t logBufferSize) {
+ size_t bufferIndex = 0;
+ while (bufferIndex < logBufferSize) {
+ const LogMessage *message =
+ reinterpret_cast<const LogMessage *>(&logBuffer[bufferIndex]);
+ emitLogMessage(message->logLevel, le64toh(message->timestampNanos),
+ message->logMessage);
+ bufferIndex += sizeof(LogMessage) +
+ strnlen(message->logMessage, logBufferSize - bufferIndex) +
+ 1;
+ }
+}
+
void ChreLogMessageParserBase::emitLogMessage(uint8_t level,
uint64_t timestampNanos,
const char *logMessage) {
diff --git a/host/common/test/chre_test_client.cc b/host/common/test/chre_test_client.cc
index d651cb8..8e74657 100644
--- a/host/common/test/chre_test_client.cc
+++ b/host/common/test/chre_test_client.cc
@@ -15,6 +15,7 @@
*/
#include "chre/util/nanoapp/app_id.h"
+#include "chre/util/system/napp_header_utils.h"
#include "chre_host/host_protocol_host.h"
#include "chre_host/log.h"
#include "chre_host/socket_client.h"
@@ -37,7 +38,7 @@
*
* Usage:
* chre_test_client load <nanoapp-id> <nanoapp-path> \
- * [app-version] [api-version]
+ * [app-version] [api-version] [tcm-capable]
* chre_test_client unload <nanoapp-id>
*/
@@ -168,7 +169,7 @@
void sendLoadNanoappRequest(SocketClient &client, const char *filename,
uint64_t appId, uint32_t appVersion,
- uint32_t apiVersion) {
+ uint32_t apiVersion, bool tcmApp) {
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file) {
LOGE("Couldn't open file '%s': %s", filename, strerror(errno));
@@ -183,10 +184,16 @@
return;
}
+ // All loaded nanoapps must be signed currently.
+ uint32_t appFlags = CHRE_NAPP_HEADER_SIGNED;
+ if (tcmApp) {
+ appFlags |= CHRE_NAPP_HEADER_TCM_CAPABLE;
+ }
+
// Perform loading with 1 fragment for simplicity
FlatBufferBuilder builder(size + 128);
FragmentedLoadTransaction transaction = FragmentedLoadTransaction(
- 1 /* transactionId */, appId, appVersion, apiVersion, buffer,
+ 1 /* transactionId */, appId, appVersion, appFlags, apiVersion, buffer,
buffer.size() /* fragmentSize */);
HostProtocolHost::encodeFragmentedLoadNanoappRequest(
builder, transaction.getNextRequest());
@@ -237,6 +244,7 @@
const std::string path{argi < argc ? argv[argi++] : ""};
const std::string appVerStr{argi < argc ? argv[argi++] : ""};
const std::string apiVerStr{argi < argc ? argv[argi++] : ""};
+ const std::string tcmCapStr{argi < argc ? argv[argi++] : ""};
SocketClient client;
sp<SocketCallbacks> callbacks = new SocketCallbacks();
@@ -252,7 +260,8 @@
sendMessageToNanoapp(client);
sendLoadNanoappRequest(client, "/data/activity.so",
0x476f6f676c00100b /* appId */, 0 /* appVersion */,
- 0x01000000 /* targetApiVersion */);
+ 0x01000000 /* targetApiVersion */,
+ false /* tcmCapable */);
sendUnloadNanoappRequest(client, 0x476f6f676c00100b /* appId */);
LOGI("Sleeping, waiting on responses");
@@ -261,6 +270,7 @@
uint64_t id = 0;
uint32_t appVersion = kDefaultAppVersion;
uint32_t apiVersion = kDefaultApiVersion;
+ bool tcmApp = false;
if (idstr.empty() || path.empty()) {
LOGE("Arguments not provided!");
@@ -274,7 +284,11 @@
if (!apiVerStr.empty()) {
std::istringstream(apiVerStr) >> std::setbase(0) >> apiVersion;
}
- sendLoadNanoappRequest(client, path.c_str(), id, appVersion, apiVersion);
+ if (!tcmCapStr.empty()) {
+ std::istringstream(tcmCapStr) >> tcmApp;
+ }
+ sendLoadNanoappRequest(client, path.c_str(), id, appVersion, apiVersion,
+ tcmApp);
} else if (cmd == "unload") {
uint64_t id = 0;
diff --git a/host/common/test/power_test/chre_power_test_client.cc b/host/common/test/power_test/chre_power_test_client.cc
index c317dbb..1c0737e 100644
--- a/host/common/test/power_test/chre_power_test_client.cc
+++ b/host/common/test/power_test/chre_power_test_client.cc
@@ -32,6 +32,7 @@
#include <vector>
#include "chre/util/nanoapp/app_id.h"
+#include "chre/util/system/napp_header_utils.h"
#include "chre/version.h"
#include "chre_host/host_protocol_host.h"
#include "chre_host/log.h"
@@ -281,7 +282,7 @@
bool sendLoadNanoappRequest(SocketClient &client, const char *filename,
uint64_t appId, uint32_t appVersion,
- uint32_t apiVersion) {
+ uint32_t apiVersion, bool tcmApp) {
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file) {
LOGE("Couldn't open file '%s': %s", filename, strerror(errno));
@@ -297,10 +298,16 @@
return false;
}
+ // All loaded nanoapps must be signed currently.
+ uint32_t appFlags = CHRE_NAPP_HEADER_SIGNED;
+ if (tcmApp) {
+ appFlags |= CHRE_NAPP_HEADER_TCM_CAPABLE;
+ }
+
// Perform loading with 1 fragment for simplicity
FlatBufferBuilder builder(size + 128);
FragmentedLoadTransaction transaction = FragmentedLoadTransaction(
- 1 /* transactionId */, appId, appVersion, apiVersion, buffer,
+ 1 /* transactionId */, appId, appVersion, appFlags, apiVersion, buffer,
buffer.size() /* fragmentSize */);
HostProtocolHost::encodeFragmentedLoadNanoappRequest(
builder, transaction.getNextRequest());
@@ -318,9 +325,9 @@
bool loadNanoapp(SocketClient &client, sp<SocketCallbacks> callbacks,
const char *filename, uint64_t appId, uint32_t appVersion,
- uint32_t apiVersion) {
- if (!sendLoadNanoappRequest(client, filename, appId, appVersion,
- apiVersion)) {
+ uint32_t apiVersion, bool tcmApp) {
+ if (!sendLoadNanoappRequest(client, filename, appId, appVersion, apiVersion,
+ tcmApp)) {
return false;
}
auto status = kReadyCond.wait_for(kReadyCondLock, kTimeout);
@@ -384,11 +391,12 @@
return true;
}
+bool isTcmArgSpecified(std::vector<string> &args) {
+ return !args.empty() && args[0] == "tcm";
+}
+
inline uint64_t getId(std::vector<string> &args) {
- if (!args.empty() && args[0] == "tcm") {
- return kPowerTestTcmAppId;
- }
- return kPowerTestAppId;
+ return isTcmArgSpecified(args) ? kPowerTestTcmAppId : kPowerTestAppId;
}
/**
@@ -761,7 +769,7 @@
}
case Command::kLoad: {
success = loadNanoapp(client, callbacks, getPath(args), getId(args),
- kAppVersion, kApiVersion);
+ kAppVersion, kApiVersion, isTcmArgSpecified(args));
break;
}
default: {
diff --git a/host/hal_generic/common/generic_context_hub_base.h b/host/hal_generic/common/generic_context_hub_base.h
index 81514e5..6f0f47f 100644
--- a/host/hal_generic/common/generic_context_hub_base.h
+++ b/host/hal_generic/common/generic_context_hub_base.h
@@ -239,7 +239,7 @@
uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
(appBinary.targetChreApiMinorVersion << 16);
mPendingLoadTransaction = FragmentedLoadTransaction(
- transactionId, appBinary.appId, appBinary.appVersion,
+ transactionId, appBinary.appId, appBinary.appVersion, appBinary.flags,
targetApiVersion, appBinary.customBinary, kLoadFragmentSizeBytes);
result =
diff --git a/host/msm/daemon/fastrpc_daemon.cc b/host/msm/daemon/fastrpc_daemon.cc
index 5ba4ccc..7ffe5c0 100644
--- a/host/msm/daemon/fastrpc_daemon.cc
+++ b/host/msm/daemon/fastrpc_daemon.cc
@@ -147,7 +147,13 @@
}
if (messageType == fbs::ChreMessage::LogMessage) {
- mLogger.log(messageBuffer, messageLen);
+ std::unique_ptr<fbs::MessageContainerT> container =
+ fbs::UnPackMessageContainer(messageBuffer);
+ const auto *logMessage = container->message.AsLogMessage();
+ const std::vector<int8_t> &logData = logMessage->buffer;
+
+ mLogger.log(reinterpret_cast<const uint8_t *>(logData.data()),
+ logData.size());
} else if (messageType == fbs::ChreMessage::TimeSyncRequest) {
sendTimeSync(true /* logOnError */);
} else if (messageType == fbs::ChreMessage::LowPowerMicAccessRequest) {
diff --git a/host/usf_daemon/main.cc b/host/usf_daemon/main.cc
new file mode 100644
index 0000000..0911d1f
--- /dev/null
+++ b/host/usf_daemon/main.cc
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#include "usf_daemon.h"
+
+#include "chre_host/log.h"
+
+int main() {
+ LOGI("CHRE Starting!");
+
+ android::chre::UsfChreDaemon daemon;
+
+ if (!daemon.init()) {
+ LOGE("Failed to initialize the CHRE daemon..");
+ } else {
+ daemon.run();
+ }
+
+ return 0;
+}
diff --git a/host/usf_daemon/usf_daemon.cc b/host/usf_daemon/usf_daemon.cc
new file mode 100644
index 0000000..e5b21c6
--- /dev/null
+++ b/host/usf_daemon/usf_daemon.cc
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+#include "usf_daemon.h"
+
+#include <unistd.h>
+#include <chrono>
+#include <fstream>
+#include <thread>
+
+#include "usf/usf_android_time.h"
+#include "usf/usf_client.h"
+
+// Aliased for consistency with the way these symbols are referenced in
+// CHRE-side code
+namespace fbs = ::chre::fbs;
+
+namespace android {
+namespace chre {
+
+namespace {
+
+using ReffedTransportClientPtr = refcount::reffed_ptr<usf::UsfTransportClient>;
+
+constexpr uint8_t kConnectRetries = 5;
+constexpr int kRetryDelayMilliSeconds = 100;
+
+bool connectToUsfWithRetry(ReffedTransportClientPtr &client) {
+ // client has already been nullptr-checked
+ uint8_t retryCount = 0;
+ bool success = false;
+ usf::UsfErr err;
+
+ while (retryCount < kConnectRetries) {
+ if ((err = client->Connect()) == usf::kErrNone) {
+ success = true;
+ break;
+ }
+ ++retryCount;
+ std::this_thread::sleep_for(
+ std::chrono::milliseconds(kRetryDelayMilliSeconds));
+ }
+
+ if (success) {
+ LOGD("Connected to USF Transport server (retry count: %u)", retryCount + 1);
+ }
+
+ return success;
+}
+
+} // namespace
+
+bool UsfChreDaemon::init() {
+ constexpr size_t kMaxTimeSyncRetries = 5;
+ constexpr useconds_t kTimeSyncRetryDelayUs = 50000; // 50 ms
+ bool success = false;
+ usf::UsfErr err;
+
+ if ((err = usf::UsfClientMgr::Init()) != usf::kErrNone) {
+ LOGE("Failed to initialize usf client mgr: (err %d)",
+ static_cast<int>(err));
+ } else if ((err = usf::UsfTransportClient::Create(
+ &mConnection.transportClient)) != usf::kErrNone) {
+ LOGE("Failed to create a USF transport client: (err %d)",
+ static_cast<int>(err));
+ } else if (mConnection.transportClient == nullptr) {
+ LOGE("Init Failed: Created a NULL transport client!");
+ } else if (!connectToUsfWithRetry(mConnection.transportClient)) {
+ LOGE("Failed to connect to USF transport server on %u retries",
+ kConnectRetries);
+ } else if ((err = usf::UsfTransportMgr::SetMsgHandler(
+ usf::UsfMsgType_CHRE, UsfChreDaemon::usfMessageHandler,
+ this)) != usf::kErrNone) {
+ LOGE("Failed to set Usf Message Handler");
+ } else if (!sendTimeSyncWithRetry(kMaxTimeSyncRetries, kTimeSyncRetryDelayUs,
+ true /* logOnError */)) {
+ LOGE("Failed to send initial time sync message");
+ } else {
+ mLogger = getLogMessageParser();
+ loadPreloadedNanoapps();
+ success = true;
+ LOGD("CHRE daemon initialized successfully");
+ }
+
+ return success;
+}
+
+void UsfChreDaemon::deinit() {
+ setShutdownRequested(true);
+ usf::UsfClientMgr::Deinit();
+}
+
+void UsfChreDaemon::run() {
+ constexpr char kChreSocketName[] = "chre";
+ auto serverCb = [&](uint16_t clientId, void *data, size_t len) {
+ sendMessageToChre(clientId, data, len);
+ };
+
+ mSocketServer.run(kChreSocketName, true /* allowSocketCreation */, serverCb);
+}
+
+usf::UsfErr UsfChreDaemon::sendUsfMessage(const uint8_t *data, size_t dataLen) {
+ usf::UsfTxMsg msg;
+
+ msg.SetMsgType(usf::UsfMsgType_CHRE);
+ msg.SetData(data, dataLen);
+
+ return mConnection.transportClient->SendMsg(&msg);
+}
+
+bool UsfChreDaemon::sendMessageToChre(uint16_t clientId, void *data,
+ size_t length) {
+ bool success = false;
+ usf::UsfErr err;
+
+ if (!HostProtocolHost::mutateHostClientId(data, length, clientId)) {
+ LOGE("Couldn't set host client ID in message container!");
+ } else {
+ LOGV("Delivering message from host (size %zu)", length);
+ if ((err = sendUsfMessage(static_cast<const uint8_t *>(data), length)) !=
+ usf::kErrNone) {
+ LOGE("Failed to deliver message from host to CHRE: %d",
+ static_cast<int>(err));
+ } else {
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+// TODO(karthikmb): Consider moving platform independent parts of this
+// function to the base class, revisit when implementing the daemon for
+// another platform.
+void UsfChreDaemon::onMessageReceived(const unsigned char *messageBuffer,
+ size_t messageLen) {
+ mLogger.dump(messageBuffer, messageLen);
+
+ uint16_t hostClientId;
+ fbs::ChreMessage messageType;
+ if (!HostProtocolHost::extractHostClientIdAndType(
+ messageBuffer, messageLen, &hostClientId, &messageType)) {
+ LOGW(
+ "Failed to extract host client ID from message - sending "
+ "broadcast");
+ hostClientId = ::chre::kHostClientIdUnspecified;
+ }
+
+ if (messageType == fbs::ChreMessage::LogMessage) {
+ std::unique_ptr<fbs::MessageContainerT> container =
+ fbs::UnPackMessageContainer(messageBuffer);
+ const auto *logMessage = container->message.AsLogMessage();
+ const std::vector<int8_t> &logData = logMessage->buffer;
+
+ mLogger.log(reinterpret_cast<const uint8_t *>(logData.data()),
+ logData.size());
+ } else if (messageType == fbs::ChreMessage::TimeSyncRequest) {
+ sendTimeSync(true /* logOnError */);
+ } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRequest) {
+ LOGD("LpmaRequest unsupported");
+ } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRelease) {
+ LOGD("LpmaRelease unsupported");
+ } else if (hostClientId == kHostClientIdDaemon) {
+ handleDaemonMessage(messageBuffer);
+ } else if (hostClientId == ::chre::kHostClientIdUnspecified) {
+ mSocketServer.sendToAllClients(messageBuffer,
+ static_cast<size_t>(messageLen));
+ } else {
+ mSocketServer.sendToClientById(
+ messageBuffer, static_cast<size_t>(messageLen), hostClientId);
+ }
+}
+
+void UsfChreDaemon::handleDaemonMessage(const uint8_t *message) {
+ std::unique_ptr<fbs::MessageContainerT> container =
+ fbs::UnPackMessageContainer(message);
+ if (container->message.type != fbs::ChreMessage::LoadNanoappResponse) {
+ LOGE("Invalid message from CHRE directed to daemon");
+ } else {
+ const auto *response = container->message.AsLoadNanoappResponse();
+ std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
+
+ if (!mPreloadedNanoappPending) {
+ LOGE("Received nanoapp load response with no pending load");
+ } else if (mPreloadedNanoappPendingTransactionId !=
+ response->transaction_id) {
+ LOGE("Received nanoapp load response with invalid transaction id");
+ } else {
+ mPreloadedNanoappPending = false;
+ }
+
+ mPreloadedNanoappsCond.notify_all();
+ }
+}
+
+void UsfChreDaemon::loadPreloadedNanoapp(const std::string &directory,
+ const std::string &name,
+ uint32_t transactionId) {
+ std::vector<uint8_t> headerBuffer;
+ std::vector<uint8_t> nanoappBuffer;
+
+ std::string headerFilename = directory + "/" + name + ".napp_header";
+ std::string nanoappFilename = directory + "/" + name + ".so";
+
+ if (readFileContents(headerFilename, headerBuffer) &&
+ readFileContents(nanoappFilename, nanoappBuffer) &&
+ !loadNanoapp(headerBuffer, nanoappBuffer, transactionId)) {
+ LOGE("Failed to load nanoapp: '%s'", name.c_str());
+ }
+}
+
+bool UsfChreDaemon::readFileContents(const std::string &filename,
+ std::vector<uint8_t> &buffer) {
+ bool success = false;
+ std::ifstream file(filename.c_str(), std::ios::binary | std::ios::ate);
+ if (!file) {
+ LOGE("Couldn't open file '%s': %d (%s)", filename.c_str(), errno,
+ strerror(errno));
+ } else {
+ ssize_t size = file.tellg();
+ file.seekg(0, std::ios::beg);
+
+ buffer.resize(size);
+ if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
+ LOGE("Couldn't read from file '%s': %d (%s)", filename.c_str(), errno,
+ strerror(errno));
+ } else {
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool UsfChreDaemon::loadNanoapp(const std::vector<uint8_t> &header,
+ const std::vector<uint8_t> &nanoapp,
+ uint32_t transactionId) {
+ // This struct comes from build/build_template.mk and must not be modified.
+ // Refer to that file for more details.
+ struct NanoAppBinaryHeader {
+ uint32_t headerVersion;
+ uint32_t magic;
+ uint64_t appId;
+ uint32_t appVersion;
+ uint32_t flags;
+ uint64_t hwHubType;
+ uint8_t targetChreApiMajorVersion;
+ uint8_t targetChreApiMinorVersion;
+ uint8_t reserved[6];
+ } __attribute__((packed));
+
+ bool success = false;
+ if (header.size() != sizeof(NanoAppBinaryHeader)) {
+ LOGE("Header size mismatch");
+ } else {
+ // The header blob contains the struct above.
+ const auto *appHeader =
+ reinterpret_cast<const NanoAppBinaryHeader *>(header.data());
+
+ // Build the target API version from major and minor.
+ uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) |
+ (appHeader->targetChreApiMinorVersion << 16);
+
+ success = sendFragmentedNanoappLoad(
+ appHeader->appId, appHeader->appVersion, appHeader->flags,
+ targetApiVersion, nanoapp.data(), nanoapp.size(), transactionId);
+ }
+
+ return success;
+}
+
+bool UsfChreDaemon::sendFragmentAndWaitOnResponse(
+ uint32_t transactionId, flatbuffers::FlatBufferBuilder &builder) {
+ bool success = true;
+ std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
+
+ mPreloadedNanoappPendingTransactionId = transactionId;
+ mPreloadedNanoappPending = sendMessageToChre(
+ kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
+ if (!mPreloadedNanoappPending) {
+ LOGE("Failed to send nanoapp fragment");
+ success = false;
+ } else {
+ // 60s timeout for fragments. Set high for busy first bootups where the
+ // PALs can block CHRE initialization while other subsystems come up.
+ std::chrono::seconds timeout(60);
+ bool signaled = mPreloadedNanoappsCond.wait_for(
+ lock, timeout, [this] { return !mPreloadedNanoappPending; });
+
+ if (!signaled) {
+ LOGE("Nanoapp fragment load timed out");
+ success = false;
+ }
+ }
+ return success;
+}
+
+bool UsfChreDaemon::sendFragmentedNanoappLoad(
+ uint64_t appId, uint32_t appVersion, uint32_t appFlags,
+ uint32_t appTargetApiVersion, const uint8_t *appBinary, size_t appSize,
+ uint32_t transactionId) {
+ // TODO: This is currently limited by the USF Message size, revisit
+ // and increase this when the USF message size increases.
+ constexpr size_t kFragmentSize = 512;
+ std::vector<uint8_t> binary(appSize);
+ std::copy(appBinary, appBinary + appSize, binary.begin());
+
+ FragmentedLoadTransaction transaction(transactionId, appId, appVersion,
+ appFlags, appTargetApiVersion, binary,
+ kFragmentSize);
+
+ bool success = true;
+
+ while (success && !transaction.isComplete()) {
+ // Pad the builder to avoid allocation churn.
+ const auto &fragment = transaction.getNextRequest();
+ flatbuffers::FlatBufferBuilder builder(fragment.binary.size() + 128);
+ HostProtocolHost::encodeFragmentedLoadNanoappRequest(builder, fragment);
+ success = sendFragmentAndWaitOnResponse(transactionId, builder);
+ }
+
+ return success;
+}
+
+int64_t UsfChreDaemon::getTimeOffset(bool *success) {
+ int64_t offset = 0;
+ uint64_t androidTimeNs;
+ uint64_t sensorCoreTimeNs;
+ usf::UsfErr err =
+ usf::UsfGetAndroidAndSensorCoreTime(&androidTimeNs, &sensorCoreTimeNs);
+ if (err != usf::kErrNone) {
+ LOGE("Get Android and sensor core time failed.");
+ } else {
+ offset = static_cast<int64_t>(androidTimeNs) -
+ static_cast<int64_t>(sensorCoreTimeNs);
+ }
+ *success = (err == usf::kErrNone);
+ return offset;
+}
+
+ChreLogMessageParserBase UsfChreDaemon::getLogMessageParser() {
+#ifdef CHRE_USE_TOKENIZED_LOGGING
+ return ChreTokenizedLogMessageParser();
+#else
+ return ChreLogMessageParserBase();
+#endif
+}
+
+usf::UsfErr UsfChreDaemon::usfMessageHandler(
+ usf::UsfTransport * /* transport */, const usf::UsfMsg *usfMsg, void *arg) {
+ usf::UsfErr err = usf::kErrInvalid;
+ auto *daemon = static_cast<UsfChreDaemon *>(arg);
+ if ((daemon != nullptr) && (usfMsg != nullptr)) {
+ const uint8_t *msgData = usfMsg->data()->data();
+ size_t msgLen = usfMsg->data()->size();
+ daemon->onMessageReceived(msgData, msgLen);
+ } else {
+ LOGE("Error Handling Usf Message");
+ }
+ return err;
+}
+
+} // namespace chre
+} // namespace android
diff --git a/host/usf_daemon/usf_daemon.h b/host/usf_daemon/usf_daemon.h
new file mode 100644
index 0000000..194d719
--- /dev/null
+++ b/host/usf_daemon/usf_daemon.h
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_USF_DAEMON_H_
+#define CHRE_USF_DAEMON_H_
+
+#include "chre_host/daemon_base.h"
+#include "chre_host/socket_server.h"
+#ifdef CHRE_USE_TOKENIZED_LOGGING
+#include "chre_host/tokenized_log_message_parser.h"
+#else
+#include "chre_host/log_message_parser_base.h"
+#endif
+
+#include "usf/usf_transport_client.h"
+
+#include <utils/SystemClock.h>
+
+// Disable verbose logging
+// TODO: use property_get_bool to make verbose logging runtime configurable
+// #define LOG_NDEBUG 0
+
+namespace android {
+namespace chre {
+
+class UsfChreDaemon : public ChreDaemonBase {
+ public:
+ struct UsfServerConnection {
+ refcount::reffed_ptr<usf::UsfTransportClient> transportClient;
+ usf::UsfTransport *transport;
+ usf::UsfServerHandle serverHandle;
+ };
+
+ ~UsfChreDaemon() {
+ deinit();
+ }
+
+ /**
+ * Initializes the CHRE daemon, and creates a USF transport for
+ * communicating with CHRE
+ *
+ * @return true on successful init
+ */
+ bool init();
+
+ /**
+ * Starts a socket server receive loop for inbound messages
+ */
+ void run();
+
+ bool sendMessageToChre(uint16_t clientId, void *data, size_t length);
+
+ void onMessageReceived(const unsigned char *messageBuffer, size_t messageLen);
+
+ UsfServerConnection &getMutableServerConnection() {
+ return mConnection;
+ }
+
+ protected:
+ void loadPreloadedNanoapp(const std::string &directory,
+ const std::string &name,
+ uint32_t transactionId) override;
+
+ void handleDaemonMessage(const uint8_t *message) override;
+
+ private:
+ SocketServer mSocketServer;
+ ChreLogMessageParserBase mLogger;
+ UsfServerConnection mConnection;
+
+ //! The mutex used to guard state between the nanoapp messaging thread
+ //! and loading preloaded nanoapps.
+ std::mutex mPreloadedNanoappsMutex;
+
+ //! The condition variable used to wait for a nanoapp to finish loading.
+ std::condition_variable mPreloadedNanoappsCond;
+
+ //! Set to true when a preloaded nanoapp is pending load.
+ bool mPreloadedNanoappPending;
+
+ //! Set to the expected transaction ID for loading a nanoapp.
+ uint32_t mPreloadedNanoappPendingTransactionId;
+
+ /**
+ * Perform a graceful shutdown of the daemon
+ */
+ void deinit();
+
+ /**
+ * Platform specific getTimeOffset for the WHI Daemon
+ *
+ * @return clock drift offset in nanoseconds
+ */
+ int64_t getTimeOffset(bool *success);
+
+ /**
+ * Get the Log Message Parser configured for this platform
+ *
+ * @return An instance of a log message parser
+ */
+ ChreLogMessageParserBase getLogMessageParser();
+
+ /**
+ * Loads the supplied file into the provided buffer.
+ *
+ * @param filename The name of the file to load.
+ * @param buffer The buffer to load into.
+ * @return true if successful, false otherwise.
+ */
+ bool readFileContents(const std::string &filename,
+ std::vector<uint8_t> &buffer);
+
+ /**
+ * Sends a preloaded nanoapp to CHRE.
+ *
+ * @param header The nanoapp header binary blob.
+ * @param nanoapp The nanoapp binary blob.
+ * @param transactionId The transaction ID to use when loading the app.
+ * @return true if succssful, false otherwise.
+ */
+ bool loadNanoapp(const std::vector<uint8_t> &header,
+ const std::vector<uint8_t> &nanoapp, uint32_t transactionId);
+
+ /**
+ * Loads a nanoapp using fragments.
+ *
+ * @param appId The ID of the nanoapp to load.
+ * @param appVersion The version of the nanoapp to load.
+ * @param appFlags The flags specified by the nanoapp to be loaded.
+ * @param appTargetApiVersion The version of the CHRE API that the app
+ * targets.
+ * @param appBinary The application binary code.
+ * @param appSize The size of the appBinary.
+ * @param transactionId The transaction ID to use when loading.
+ * @return true if successful, false otherwise.
+ */
+ bool sendFragmentedNanoappLoad(uint64_t appId, uint32_t appVersion,
+ uint32_t appFlags,
+ uint32_t appTargetApiVersion,
+ const uint8_t *appBinary, size_t appSize,
+ uint32_t transactionId);
+
+ static usf::UsfErr usfMessageHandler(usf::UsfTransport *transport,
+ const usf::UsfMsg *usfMsg, void *arg);
+
+ usf::UsfErr sendUsfMessage(const uint8_t *data, size_t dataLen);
+
+ bool sendFragmentAndWaitOnResponse(uint32_t transactionId,
+ flatbuffers::FlatBufferBuilder &builder);
+};
+
+} // namespace chre
+} // namespace android
+
+#endif // CHRE_USF_DAEMON_H_
diff --git a/platform/aoc/audio_controller.cc b/platform/aoc/audio_controller.cc
new file mode 100644
index 0000000..2986ad3
--- /dev/null
+++ b/platform/aoc/audio_controller.cc
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/aoc/audio_controller.h"
+#include "chre/core/event_loop_manager.h"
+#include "chre/platform/assert.h"
+
+#include "aoc/ff1/audio/include/controller_audio_input.h" // For command IDs
+#include "aoc/include/processor_aoc.h"
+#include "efw/include/environment_ipc.h"
+#include "efw/include/ring_buffer_ipc.h"
+
+namespace chre {
+
+namespace {
+
+// Helper function to get the IPC Array parameter for the conroller
+// constructor. The function exists because of a current limitation in
+// the EFW Controller class constructor which requires an IPC array
+// variable to be passed in.
+inline IPCommunication **GetIpcArray() {
+ static IPCommunication *arr =
+ EnvironmentIpc::Instance()->IpcByID(IPC_CHANNEL_CMD_CHRE_AUDIO_NOTIF_ID);
+ return &arr;
+}
+
+} // namespace
+
+AudioController::AudioController()
+ : Controller(
+ "CHRE_CTR" /* name */, EFWObject::Root(),
+ tskIDLE_PRIORITY + 5 /* priority */, 1024 /* stack size in words */,
+ GetIpcArray() /* ipc array */, 1 /*ipc descriptor array size */,
+ 5 /* max outstanding ipc notifications */,
+ 10 /* max total notifications */, 1 /* num IPC ring buffers */, 10),
+ mControllerIpcAoc("CHRE_CTR_IPC" /* name */, EFWObject::Root(),
+ tskIDLE_PRIORITY + 5 /* priority */,
+ ProcessorAoC::FF1 /* remote core */,
+ EnvironmentIpc::Instance()->IpcByID(
+ IPC_CHANNEL_CMD_CHRE_AUDIO_INPUT_ID),
+ 1 /* notifDepth */, 1 /* NumIpcRingBuffers */),
+ mPipeIpcReceiverAoc(
+ "CHRE_PIPE" /* name */,
+ EnvironmentIpc::Instance()->IpcByID(IPC_CHANNEL_DTA_CHRE_AUDIO_ID),
+ ProcessorAoC::A32 /* Primary core */, EFWObject::Root()) {
+ LOGV("MainController created");
+
+ TaskSpawn();
+}
+
+void AudioController::SetUp() {
+ Controller::SetUp();
+ mControllerIpcAoc.Start();
+}
+
+void AudioController::SetUpRemoteCore() {
+ if (!mDspCoreInitDone) {
+ int rc = -1;
+ struct CMD_HDR cmd;
+ // See controller_audio_input.h in EFW for command ID definitions.
+ uint16_t cmdId = ControllerAudioInput::CMD_AUDIO_INPUT_CHRE_SETUP_ID;
+ if ((rc = mControllerIpcAoc.CmdRelay(&cmd, cmdId, sizeof(CMD_HDR),
+ portMAX_DELAY)) != 0) {
+ LOGE("Failed to send setup cmd to DSP rc: %d", rc);
+ }
+ }
+}
+
+void AudioController::TearDown() {
+ Controller::TearDown();
+
+ mFilter.Stop();
+ mFilter.RingUnbind(AudioFilter::kRingIndex);
+}
+
+bool AudioController::CmdProcessor(struct CMD_HDR *cmd) {
+ switch (cmd->id) {
+ case ControllerAudioInput::CMD_AUDIO_INPUT_CHRE_F1_CTR_READY_ID:
+ cmd->reply = OnDspReady();
+ break;
+
+ default:
+ LOGD("Unknown cmd for CHRE controller");
+ cmd->reply = -1;
+ break;
+ }
+
+ return true;
+}
+
+int AudioController::OnDspReady() {
+ int rc = -1;
+
+ mDspCoreInitDone = true;
+
+ if ((rc = mFilter.InputBind(AudioFilter::kPipeIndex, &mPipeIpcReceiverAoc)) !=
+ 0) {
+ LOGE("Failed to bind pipe to filter input");
+ } else if ((rc = mFilter.RingBind(
+ AudioFilter::kRingIndex,
+ ring_buffer_ipc_[AudioFilter::kRingIndex])) != 0) {
+ LOGE("Failed to bind ipc ring buffer to filter");
+ } else if ((rc = mFilter.Start()) != 0) {
+ LOGE("Failed to start audio filter");
+ } else {
+ EventLoopManagerSingleton::get()
+ ->getAudioRequestManager()
+ .handleAudioAvailability(0 /*handle*/, true /*available*/);
+ }
+
+ return rc;
+}
+
+const chreAudioSource *AudioController::GetSource(uint32_t /*handle*/) const {
+ return &kAudioSource;
+}
+
+void AudioController::SetEnabled(uint32_t /*handle*/, bool enabled) {
+ mSourceStatus.enabled = enabled;
+}
+
+bool AudioController::UpdateStream(uint32_t /*handle*/, bool start) {
+ bool success = false;
+ // TODO: handle and other params
+ if (mDspCoreInitDone && mSourceStatus.enabled) {
+ if (mRequestStarted != start) {
+ mRequestStarted = start;
+ CMD_HDR cmd;
+ cmd.reply = -1; // negative value to test
+ // see controller_audio_input.h in EFW for command ID definitions.
+ uint16_t cmdId =
+ start ? ControllerAudioInput::CMD_AUDIO_INPUT_MIC_CHRE_START_ID
+ : ControllerAudioInput::CMD_AUDIO_INPUT_MIC_CHRE_STOP_ID;
+
+ int rc = mControllerIpcAoc.CmdRelay(&cmd, cmdId, sizeof(CMD_HDR),
+ portMAX_DELAY);
+ LOGV("request audio send: %d", rc);
+ success = (rc == 0);
+ } else {
+ // streaming already in the correct state for this handle
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool AudioController::RequestAudio(uint32_t handle) {
+ return UpdateStream(handle, true /*start*/);
+}
+
+bool AudioController::ReleaseAudio(uint32_t handle) {
+ return UpdateStream(handle, false /*start*/);
+}
+
+void AudioController::OnBufferReleased() {
+ mFilter.Signal(AudioFilter::kBufferReleasedSignalIndex);
+}
+
+} // namespace chre
diff --git a/platform/aoc/audio_filter.cc b/platform/aoc/audio_filter.cc
new file mode 100644
index 0000000..ac66e6a
--- /dev/null
+++ b/platform/aoc/audio_filter.cc
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+#include "chre/platform/aoc/audio_filter.h"
+#include "chre/core/event_loop_manager.h"
+#include "chre/platform/aoc/memory.h"
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+#include "chre/platform/system_time.h"
+#include "chre/util/time.h"
+#include "efw/include/ring_buffer_ipc.h"
+
+namespace chre {
+
+AudioFilter::AudioFilter()
+ : Filter("CHRE_FLT" /* name */, EFWObject::Root() /* parent */,
+ tskIDLE_PRIORITY + 5 /* priority */, 512 /* stack size words */,
+ 1 /* command depth */, 1 /* Num Filter inputs */,
+ 1 /* Num Filter Outputs */, 1 /* Num Ring Buffers */) {
+ LOGV("Filter Created");
+
+ TaskSpawn();
+}
+
+int AudioFilter::StartCallback() {
+ mRingBufferHandle = ring_[kRingIndex]->ReaderRegister();
+ CHRE_ASSERT(mRingBufferHandle != nullptr);
+
+ // TODO (b/168330113): The sample buffer is a fairly large chunk of
+ // contiguous memory that may be prone to allocation failures - reserve
+ // this memory statically.
+ forceDramAccess();
+ mSampleBufferDram =
+ static_cast<int16_t *>(memoryAllocDram(sizeof(mSampleBufferSram)));
+ CHRE_ASSERT(mSampleBufferDram != nullptr);
+
+ return 0;
+}
+
+bool AudioFilter::StopCallback() {
+ ring_[kRingIndex]->ReaderUnregister(mRingBufferHandle);
+ mRingBufferHandle = nullptr;
+
+ forceDramAccess();
+ memoryFreeDram(mSampleBufferDram);
+ mSampleBufferDram = nullptr;
+
+ return true;
+}
+
+bool AudioFilter::SignalProcessor(int index) {
+ switch (index) {
+ case kBufferReleasedSignalIndex:
+ OnBufferReleased();
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+void AudioFilter::InputProcessor(int /*pin*/, void *message, size_t size) {
+ CHRE_ASSERT(message != nullptr);
+
+ // TODO resync and synchronization, check metadata format etc.
+ auto *metadata = static_cast<struct AudioInputMetadata *>(message);
+
+ if (metadata->need_resync) {
+ LOGW("Resync request received, logic not implemented yet");
+ }
+
+ constexpr size_t kBytesPerAocSample = sizeof(uint32_t);
+ constexpr size_t kNumSamplesAoc = 160;
+ constexpr size_t kRingSize = kNumSamplesAoc * kBytesPerAocSample *
+ 1; // 1 channel of u32 samples per 10 ms
+ uint32_t buffer[kNumSamplesAoc];
+ uint32_t nBytes =
+ ring_[kRingIndex]->Read(mRingBufferHandle, &buffer, kRingSize);
+ CHRE_ASSERT_LOG(nBytes != 0, "Got data pipe notif, but no data in ring");
+ size_t nSamples = nBytes / kBytesPerAocSample;
+
+ if (mSampleCount == 0) {
+ // TODO: Get a better estimate of the timestamp of the first sample in
+ // the frame. Since the pipe notification arrives every 10ms, we could
+ // subtract 10ms from now(), and maybe even keep track of the embedded
+ // timestamp of the first sample of the current frame, and the last
+ // sample of the previous frame.
+ mDataEvent.timestamp = SystemTime::getMonotonicTime().toRawNanoseconds();
+ }
+
+ // TODO: For the initial implementation/testing, we assume that the
+ // frame might not fit to a T in our current buffer, but that is the
+ // expectation. With the current logic, we could end up dropping a frame,
+ // revisit this to make sure we capture the exact number of samples,
+ // while continuing to buffer samples either via a ping-pong scheme, or
+ // a small extra scratch space.
+ if ((nSamples + mSampleCount) > kSamplesPer2Sec) {
+ if (!mDramBufferInUse) {
+ forceDramAccess();
+ memcpy(mSampleBufferDram, mSampleBufferSram, sizeof(mSampleBufferSram));
+ mDataEvent.sampleCount = mSampleCount;
+ mDataEvent.samplesS16 = mSampleBufferDram;
+ mDramBufferInUse = true;
+
+ EventLoopManagerSingleton::get()
+ ->getAudioRequestManager()
+ .handleAudioDataEvent(&mDataEvent);
+ } else {
+ LOGW("SRAM audio buffer full while DRAM audio buffer not released!");
+ }
+ mSampleCount = 0;
+ }
+ for (size_t i = 0; i < nSamples; ++i, ++mSampleCount) {
+ // The zeroth byte of the received sample is a timestamp, which we
+ // strip out.
+ // TODO: Verify that there's no scaling on the remaining data, in
+ // which case casting without descaling won't be the right thing to
+ // do.
+ mSampleBufferSram[mSampleCount] =
+ static_cast<int16_t>((buffer[i] >> 8) & 0xffff);
+ }
+}
+
+void AudioFilter::OnBufferReleased() {
+ mDramBufferInUse = false;
+}
+
+} // namespace chre
diff --git a/platform/aoc/chre_api_re.cc b/platform/aoc/chre_api_re.cc
new file mode 100644
index 0000000..00fa668
--- /dev/null
+++ b/platform/aoc/chre_api_re.cc
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+#include <cinttypes>
+
+#include "chre/platform/log.h"
+#include "chre/util/macros.h"
+#include "chre_api/chre/re.h"
+
+void chreLog(enum chreLogLevel level, const char *formatStr, ...) {
+ va_list args;
+ va_start(args, formatStr);
+
+ // TODO(karthikmb): Remove this once log buffering is implemented.
+ char logBuf[500];
+ va_list vsnargs;
+ va_copy(vsnargs, args);
+ vsnprintf(logBuf, sizeof(logBuf), formatStr, args);
+ va_end(vsnargs);
+ printf("CHRE: %s\n", logBuf);
+
+ chre::vaLog(level, formatStr, args);
+ va_end(args);
+}
diff --git a/platform/aoc/dram_vote_client.cc b/platform/aoc/dram_vote_client.cc
new file mode 100644
index 0000000..0977560
--- /dev/null
+++ b/platform/aoc/dram_vote_client.cc
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/shared/dram_vote_client.h"
+
+#include "chre/platform/assert.h"
+
+#include "sysmem.h"
+
+namespace chre {
+
+void DramVoteClient::issueDramVote(bool enabled) {
+ if (mLastDramVote != enabled) {
+ int rc;
+ if (enabled) {
+ rc = SysMem::Instance()->MemoryRequest(SysMem::MIF, true);
+ } else {
+ rc = SysMem::Instance()->MemoryRelease(SysMem::MIF);
+ }
+
+ CHRE_ASSERT_LOG(rc == 0,
+ "Unable to change DRAM access to %d with error code %d",
+ enabled, rc);
+ }
+}
+
+} // namespace chre
diff --git a/platform/aoc/fatal_error.cc b/platform/aoc/fatal_error.cc
new file mode 100644
index 0000000..ae3903d
--- /dev/null
+++ b/platform/aoc/fatal_error.cc
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#include "chre/target_platform/fatal_error.h"
+
+#include "chre/platform/shared/platform_log.h"
+
+namespace chre {
+
+void preFatalError() {
+ // TODO: stubbed out, Implement this.
+}
+
+} // namespace chre
diff --git a/platform/aoc/host_link.cc b/platform/aoc/host_link.cc
new file mode 100644
index 0000000..2c0d638
--- /dev/null
+++ b/platform/aoc/host_link.cc
@@ -0,0 +1,541 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/host_link.h"
+
+#include "chre/core/event_loop_manager.h"
+#include "chre/core/host_comms_manager.h"
+#include "chre/core/settings.h"
+#include "chre/platform/aoc/system_time.h"
+#include "chre/platform/shared/host_protocol_chre.h"
+#include "chre/platform/shared/nanoapp_load_manager.h"
+#include "chre/util/fixed_size_blocking_queue.h"
+#include "chre/util/flatbuffers/helpers.h"
+#include "chre_api/chre/version.h"
+
+namespace chre {
+
+Nanoseconds HostLinkBase::mLastTimeSyncRequestNanos(0);
+
+namespace {
+
+//! Used to pass the client ID through the user data pointer in deferCallback
+union HostClientIdCallbackData {
+ uint16_t hostClientId;
+ void *ptr;
+};
+static_assert(sizeof(uint16_t) <= sizeof(void *),
+ "Pointer must at least fit a u16 for passing the host client ID");
+
+struct LoadNanoappCallbackData {
+ uint64_t appId;
+ uint32_t transactionId;
+ uint16_t hostClientId;
+ UniquePtr<Nanoapp> nanoapp;
+ uint32_t fragmentId;
+};
+
+struct UnloadNanoappCallbackData {
+ uint64_t appId;
+ uint32_t transactionId;
+ uint16_t hostClientId;
+ bool allowSystemNanoappUnload;
+};
+
+inline HostCommsManager &getHostCommsManager() {
+ return EventLoopManagerSingleton::get()->getHostCommsManager();
+}
+
+void setTimeSyncRequestTimer(Nanoseconds delay) {
+ static TimerHandle sHandle;
+ static bool sHandleInitialized;
+
+ if (sHandleInitialized) {
+ EventLoopManagerSingleton::get()->cancelDelayedCallback(sHandle);
+ }
+
+ auto callback = [](uint16_t /* eventType */, void * /* data */) {
+ HostLinkBase::sendTimeSyncRequest();
+ };
+ sHandle = EventLoopManagerSingleton::get()->setDelayedCallback(
+ SystemCallbackType::TimerSyncRequest, nullptr /* data */, callback,
+ delay);
+ sHandleInitialized = true;
+}
+
+bool getSettingFromFbs(fbs::Setting setting, Setting *chreSetting) {
+ bool success = true;
+ switch (setting) {
+ case fbs::Setting::LOCATION:
+ *chreSetting = Setting::LOCATION;
+ break;
+ default:
+ LOGE("Unknown setting %" PRIu8, setting);
+ success = false;
+ }
+
+ return success;
+}
+
+bool getSettingStateFromFbs(fbs::SettingState state,
+ SettingState *chreSettingState) {
+ bool success = true;
+ switch (state) {
+ case fbs::SettingState::DISABLED:
+ *chreSettingState = SettingState::DISABLED;
+ break;
+ case fbs::SettingState::ENABLED:
+ *chreSettingState = SettingState::ENABLED;
+ break;
+ default:
+ LOGE("Unknown state %" PRIu8, state);
+ success = false;
+ }
+
+ return success;
+}
+
+void sendDebugDumpData(uint16_t hostClientId, const char *debugStr,
+ size_t debugStrSize) {
+ constexpr size_t kFixedSizePortion = 52;
+ ChreFlatBufferBuilder builder(kFixedSizePortion + debugStrSize);
+ HostProtocolChre::encodeDebugDumpData(builder, hostClientId, debugStr,
+ debugStrSize);
+
+ getHostCommsManager().send(builder.GetBufferPointer(), builder.GetSize());
+}
+
+void sendDebugDumpResponse(uint16_t hostClientId, bool success,
+ uint32_t dataCount) {
+ constexpr size_t kFixedSizePortion = 52;
+ ChreFlatBufferBuilder builder(kFixedSizePortion);
+ HostProtocolChre::encodeDebugDumpResponse(builder, hostClientId, success,
+ dataCount);
+ getHostCommsManager().send(builder.GetBufferPointer(), builder.GetSize());
+}
+
+void constructNanoappListCallback(uint16_t /*eventType*/, void *deferCbData) {
+ HostClientIdCallbackData clientIdCbData;
+ clientIdCbData.ptr = deferCbData;
+
+ struct NanoappListData {
+ ChreFlatBufferBuilder *builder;
+ DynamicVector<NanoappListEntryOffset> nanoappEntries;
+ };
+ NanoappListData cbData;
+
+ EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop();
+ size_t expectedNanoappCount = eventLoop.getNanoappCount();
+ if (!cbData.nanoappEntries.reserve(expectedNanoappCount)) {
+ LOG_OOM();
+ } else {
+ constexpr size_t kFixedOverhead = 48;
+ constexpr size_t kPerNanoappSize = 32;
+ size_t initialBufferSize =
+ (kFixedOverhead + expectedNanoappCount * kPerNanoappSize);
+
+ ChreFlatBufferBuilder builder(initialBufferSize);
+ cbData.builder = &builder;
+
+ auto nanoappAdderCallback = [](const Nanoapp *nanoapp, void *data) {
+ auto *listData = static_cast<NanoappListData *>(data);
+ HostProtocolChre::addNanoappListEntry(
+ *(listData->builder), listData->nanoappEntries, nanoapp->getAppId(),
+ nanoapp->getAppVersion(), true /*enabled*/,
+ nanoapp->isSystemNanoapp());
+ };
+ eventLoop.forEachNanoapp(nanoappAdderCallback, &cbData);
+ HostProtocolChre::finishNanoappListResponse(builder, cbData.nanoappEntries,
+ clientIdCbData.hostClientId);
+ getHostCommsManager().send(builder.GetBufferPointer(), builder.GetSize());
+ }
+}
+
+void sendFragmentResponse(uint16_t hostClientId, uint32_t transactionId,
+ uint32_t fragmentId, bool success) {
+ constexpr size_t kInitialBufferSize = 52;
+ ChreFlatBufferBuilder builder(kInitialBufferSize);
+ HostProtocolChre::encodeLoadNanoappResponse(
+ builder, hostClientId, transactionId, success, fragmentId);
+
+ if (!getHostCommsManager().send(builder.GetBufferPointer(),
+ builder.GetSize())) {
+ LOGE(
+ "Failed to send fragment response for HostClientID: %x FragmentID: %x"
+ "transactionID: 0x%x",
+ hostClientId, fragmentId, transactionId);
+ }
+}
+
+void finishLoadingNanoappCallback(uint16_t /*eventType*/, void *data) {
+ UniquePtr<LoadNanoappCallbackData> cbData(
+ static_cast<LoadNanoappCallbackData *>(data));
+ constexpr size_t kInitialBufferSize = 48;
+ ChreFlatBufferBuilder builder(kInitialBufferSize);
+
+ CHRE_ASSERT(cbData != nullptr);
+ LOGD("Finished loading nanoapp cb on fragment ID: %u", cbData->fragmentId);
+
+ EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop();
+ bool success =
+ cbData->nanoapp->isLoaded() && eventLoop.startNanoapp(cbData->nanoapp);
+
+ sendFragmentResponse(cbData->hostClientId, cbData->transactionId,
+ cbData->fragmentId, success);
+}
+
+void handleUnloadNanoappCallback(uint16_t /*eventType*/, void *data) {
+ auto *cbData = static_cast<UnloadNanoappCallbackData *>(data);
+ bool success = false;
+ uint32_t instanceId;
+ EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop();
+ if (!eventLoop.findNanoappInstanceIdByAppId(cbData->appId, &instanceId)) {
+ LOGE("Couldn't unload app ID 0x%016" PRIx64 ": not found", cbData->appId);
+ } else {
+ success =
+ eventLoop.unloadNanoapp(instanceId, cbData->allowSystemNanoappUnload);
+ }
+
+ constexpr size_t kInitialBufferSize = 52;
+ ChreFlatBufferBuilder builder(kInitialBufferSize);
+ HostProtocolChre::encodeUnloadNanoappResponse(builder, cbData->hostClientId,
+ cbData->transactionId, success);
+
+ if (!getHostCommsManager().send(builder.GetBufferPointer(),
+ builder.GetSize())) {
+ LOGE("Failed to send unload response to host: %x transactionID: 0x%x",
+ cbData->hostClientId, cbData->transactionId);
+ }
+
+ memoryFree(data);
+}
+
+} // anonymous namespace
+
+void sendDebugDumpResultToHost(uint16_t hostClientId, const char *debugStr,
+ size_t debugStrSize, bool complete,
+ uint32_t dataCount) {
+ if (debugStrSize > 0) {
+ sendDebugDumpData(hostClientId, debugStr, debugStrSize);
+ }
+
+ if (complete) {
+ sendDebugDumpResponse(hostClientId, true /*success*/, dataCount);
+ }
+}
+
+void HostLink::flushMessagesSentByNanoapp(uint64_t /* appId */) {
+ // TODO: Implement this once USF supports this (b/149318001)
+}
+
+bool HostLink::sendMessage(const MessageToHost *message) {
+ constexpr size_t kFixedReserveSize = 80;
+ ChreFlatBufferBuilder builder(message->message.size() + kFixedReserveSize);
+ HostProtocolChre::encodeNanoappMessage(
+ builder, message->appId, message->toHostData.messageType,
+ message->toHostData.hostEndpoint, message->message.data(),
+ message->message.size());
+
+ bool success =
+ getHostCommsManager().send(builder.GetBufferPointer(), builder.GetSize());
+
+ getHostCommsManager().onMessageToHostComplete(message);
+
+ return success;
+}
+
+HostLinkBase::HostLinkBase() {
+ usf::UsfErr err = usf::UsfTransportMgr::SetMsgHandler(
+ usf::UsfMsgType::UsfMsgType_CHRE, handleUsfMessage, nullptr);
+ if (err == usf::kErrNone) {
+ err = usf::UsfWorkMgr::CreateWorker(&mWorker);
+ }
+
+ if (err != usf::kErrNone) {
+ FATAL_ERROR("Failed to initialize CHRE transport");
+ }
+}
+
+HostLinkBase::~HostLinkBase() {
+ mWorker->Stop();
+ mWorker.reset();
+}
+
+void HostLinkBase::init(usf::UsfTransport *transport) {
+ if (mTransportHandle == nullptr) {
+ if (transport == nullptr) {
+ FATAL_ERROR("Null transport at init, cannot send out messages!");
+ }
+ mTransportHandle = transport;
+ }
+}
+
+bool HostLinkBase::send(uint8_t *data, size_t dataLen) {
+ usf::UsfErr err = usf::kErrFailed;
+
+ if (mTransportHandle != nullptr) {
+ usf::UsfTxMsg msg;
+ msg.SetMsgType(usf::UsfMsgType_CHRE);
+ msg.SetData(data, dataLen);
+ err = mTransportHandle->SendMsg(&msg);
+ }
+
+ return (usf::kErrNone == err);
+}
+
+usf::UsfErr HostLinkBase::handleUsfMessage(usf::UsfTransport *transport,
+ const usf::UsfMsg *msg,
+ void * /* arg */) {
+ usf::UsfErr rc = usf::kErrFailed;
+ getHostCommsManager().init(transport);
+
+ if (msg == nullptr) {
+ rc = usf::kErrInvalid;
+ } else {
+ usf::UsfMsgType msgType = msg->msg_type();
+
+ if (msgType == usf::UsfMsgType_CHRE) {
+ auto *dataVector = msg->data();
+ if (dataVector != nullptr) {
+ size_t dataLen = dataVector->size();
+ struct MessageData {
+ uint64_t dataLen;
+ void *data;
+ };
+
+ // handleUsfMessage isn't single-threaded. Make a copy and process
+ // on a worker thread to ensure CHRE only processes incoming messages
+ // on a single thread. CHRE's thread isn't used since several messages
+ // can be processed without posting to it.
+ //
+ // TODO(b/163592230): Improve USF messaging interface to remove extra
+ // data copies and keep work single-threaded.
+ MessageData data;
+ data.dataLen = dataLen;
+ data.data = memoryAlloc(dataLen);
+ if (data.data == nullptr) {
+ LOG_OOM();
+ rc = usf::kErrAllocation;
+ } else {
+ memcpy(data.data, dataVector->data(), dataLen);
+
+ auto callback = [](void *data) {
+ MessageData *dataInfo = static_cast<MessageData *>(data);
+ HostProtocolChre::decodeMessageFromHost(dataInfo->data,
+ dataInfo->dataLen);
+ memoryFree(dataInfo->data);
+ // USF owns the passed in data pointer and will free it after the
+ // callback returns.
+ };
+ rc = getHostCommsManager().getWorker()->Enqueue(callback, &data,
+ sizeof(data));
+ }
+ }
+ } else {
+ rc = usf::kErrInvalid;
+ }
+ }
+
+ // Opportunistically send a time sync message (1 hour period threshold)
+ constexpr Seconds kOpportunisticTimeSyncPeriod = Seconds(60 * 60 * 1);
+ if (SystemTime::getMonotonicTime() >
+ mLastTimeSyncRequestNanos + kOpportunisticTimeSyncPeriod) {
+ sendTimeSyncRequest();
+ }
+
+ return rc;
+}
+
+void HostLinkBase::sendLogMessage(const uint8_t *logMessage,
+ size_t logMessageSize) {
+ constexpr size_t kInitialSize = 128;
+ ChreFlatBufferBuilder builder(logMessageSize + kInitialSize);
+ HostProtocolChre::encodeLogMessages(builder, logMessage, logMessageSize);
+
+ getHostCommsManager().send(builder.GetBufferPointer(), builder.GetSize());
+}
+
+void HostLinkBase::sendTimeSyncRequest() {
+ constexpr size_t kInitialSize = 52;
+ ChreFlatBufferBuilder builder(kInitialSize);
+ HostProtocolChre::encodeTimeSyncRequest(builder);
+
+ getHostCommsManager().send(builder.GetBufferPointer(), builder.GetSize());
+
+ mLastTimeSyncRequestNanos = SystemTime::getMonotonicTime();
+}
+
+void HostMessageHandlers::handleDebugDumpRequest(uint16_t hostClientId) {
+ chre::EventLoopManagerSingleton::get()
+ ->getDebugDumpManager()
+ .onDebugDumpRequested(hostClientId);
+}
+
+void HostMessageHandlers::handleHubInfoRequest(uint16_t hostClientId) {
+ constexpr size_t kInitialBufferSize = 192;
+
+ constexpr char kHubName[] = "CHRE on AoC";
+ constexpr char kVendor[] = "Google";
+ constexpr char kToolchain[] =
+ "Clang " STRINGIFY(__clang_major__) "." STRINGIFY(
+ __clang_minor__) "." STRINGIFY(__clang_patchlevel__) ")";
+ constexpr uint32_t kLegacyPlatformVersion = 0;
+ constexpr uint32_t kLegacyToolchainVersion =
+ ((__clang_major__ & 0xFF) << 24) | ((__clang_minor__ & 0xFF) << 16) |
+ (__clang_patchlevel__ & 0xFFFF);
+ constexpr float kPeakMips = 800;
+ // TODO: The following need to be updated when we've measured the
+ // indicated power levels on the AoC
+ constexpr float kStoppedPower = 0;
+ constexpr float kSleepPower = 0;
+ constexpr float kPeakPower = 0;
+
+ ChreFlatBufferBuilder builder(kInitialBufferSize);
+ HostProtocolChre::encodeHubInfoResponse(
+ builder, kHubName, kVendor, kToolchain, kLegacyPlatformVersion,
+ kLegacyToolchainVersion, kPeakMips, kStoppedPower, kSleepPower,
+ kPeakPower, CHRE_MESSAGE_TO_HOST_MAX_SIZE, chreGetPlatformId(),
+ chreGetVersion(), hostClientId);
+
+ if (!getHostCommsManager().send(builder.GetBufferPointer(),
+ builder.GetSize())) {
+ LOGE("Failed to send Hub Info Response message");
+ }
+}
+
+void HostMessageHandlers::handleLoadNanoappRequest(
+ uint16_t hostClientId, uint32_t transactionId, uint64_t appId,
+ uint32_t appVersion, uint32_t appFlags, uint32_t targetApiVersion,
+ const void *buffer, size_t bufferLen, const char *appFileName,
+ uint32_t fragmentId, size_t appBinaryLen) {
+ bool success = true;
+ static NanoappLoadManager sLoadManager;
+
+ if (fragmentId == 0 || fragmentId == 1) {
+ size_t totalAppBinaryLen = (fragmentId == 0) ? bufferLen : appBinaryLen;
+ LOGD("Load nanoapp request for app ID 0x%016" PRIx64 " ver 0x%" PRIx32
+ " flags 0x%" PRIx32 " target API 0x%08" PRIx32
+ " size %zu (txnId %" PRIu32 " client %" PRIu16 ")",
+ appId, appVersion, appFlags, targetApiVersion, totalAppBinaryLen,
+ transactionId, hostClientId);
+
+ if (sLoadManager.hasPendingLoadTransaction()) {
+ FragmentedLoadInfo info = sLoadManager.getTransactionInfo();
+ sendFragmentResponse(info.hostClientId, info.transactionId,
+ 0 /* fragmentId */, false /* success */);
+ sLoadManager.markFailure();
+ }
+
+ success =
+ sLoadManager.prepareForLoad(hostClientId, transactionId, appId,
+ appVersion, appFlags, totalAppBinaryLen);
+ }
+
+ if (success) {
+ success = sLoadManager.copyNanoappFragment(
+ hostClientId, transactionId, (fragmentId == 0) ? 1 : fragmentId, buffer,
+ bufferLen);
+ } else {
+ LOGE("Failed to prepare for load");
+ }
+
+ if (sLoadManager.isLoadComplete()) {
+ LOGD("Load manager load complete...");
+ auto cbData = MakeUnique<LoadNanoappCallbackData>();
+ if (cbData.isNull()) {
+ LOG_OOM();
+ } else {
+ cbData->transactionId = transactionId;
+ cbData->hostClientId = hostClientId;
+ cbData->appId = appId;
+ cbData->fragmentId = fragmentId;
+ cbData->nanoapp = sLoadManager.releaseNanoapp();
+
+ // Note that if this fails, we'll generate the error response in
+ // the normal deferred callback
+ EventLoopManagerSingleton::get()->deferCallback(
+ SystemCallbackType::FinishLoadingNanoapp, cbData.release(),
+ finishLoadingNanoappCallback);
+ }
+ } else {
+ // send a response for this fragment
+ sendFragmentResponse(hostClientId, transactionId, fragmentId, success);
+ }
+}
+
+void HostMessageHandlers::handleNanoappListRequest(uint16_t hostClientId) {
+ LOGD("Nanoapp list request from client ID %" PRIu16, hostClientId);
+ HostClientIdCallbackData cbData = {};
+ cbData.hostClientId = hostClientId;
+ EventLoopManagerSingleton::get()->deferCallback(
+ SystemCallbackType::NanoappListResponse, cbData.ptr,
+ constructNanoappListCallback);
+}
+
+void HostMessageHandlers::handleNanoappMessage(uint64_t appId,
+ uint32_t messageType,
+ uint16_t hostEndpoint,
+ const void *messageData,
+ size_t messageDataLen) {
+ LOGD("Parsed nanoapp message from host: app ID 0x%016" PRIx64
+ " endpoint 0x%" PRIx16 " msgType %" PRIu32 " payload size %zu",
+ appId, hostEndpoint, messageType, messageDataLen);
+
+ getHostCommsManager().sendMessageToNanoappFromHost(
+ appId, messageType, hostEndpoint, messageData, messageDataLen);
+}
+
+void HostMessageHandlers::handleSettingChangeMessage(fbs::Setting setting,
+ fbs::SettingState state) {
+ Setting chreSetting;
+ SettingState chreSettingState;
+ if (getSettingFromFbs(setting, &chreSetting) &&
+ getSettingStateFromFbs(state, &chreSettingState)) {
+ postSettingChange(chreSetting, chreSettingState);
+ }
+}
+
+void HostMessageHandlers::handleTimeSyncMessage(int64_t offset) {
+ setEstimatedHostTimeOffset(offset);
+
+ // Schedule a time sync request since offset may drift
+ constexpr Seconds kClockDriftTimeSyncPeriod =
+ Seconds(60 * 60 * 6); // 6 hours
+ setTimeSyncRequestTimer(kClockDriftTimeSyncPeriod);
+}
+
+void HostMessageHandlers::handleUnloadNanoappRequest(
+ uint16_t hostClientId, uint32_t transactionId, uint64_t appId,
+ bool allowSystemNanoappUnload) {
+ LOGD("Unload nanoapp request (txnID %" PRIu32 ") for appId 0x%016" PRIx64
+ " system %d",
+ transactionId, appId, allowSystemNanoappUnload);
+ auto *cbData = memoryAlloc<UnloadNanoappCallbackData>();
+ if (cbData == nullptr) {
+ LOG_OOM();
+ } else {
+ cbData->appId = appId;
+ cbData->transactionId = transactionId;
+ cbData->hostClientId = hostClientId;
+ cbData->allowSystemNanoappUnload = allowSystemNanoappUnload;
+
+ EventLoopManagerSingleton::get()->deferCallback(
+ SystemCallbackType::HandleUnloadNanoapp, cbData,
+ handleUnloadNanoappCallback);
+ }
+}
+
+} // namespace chre
diff --git a/platform/aoc/include/chre/platform/aoc/audio_controller.h b/platform/aoc/include/chre/platform/aoc/audio_controller.h
new file mode 100644
index 0000000..76d19e3
--- /dev/null
+++ b/platform/aoc/include/chre/platform/aoc/audio_controller.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_AUDIO_CONTROLLER_H_
+#define CHRE_PLATFORM_AOC_AUDIO_CONTROLLER_H_
+
+#include "chre/platform/log.h"
+#include "chre/platform/system_time.h"
+#include "chre/target_platform/init.h"
+#include "chre/util/time.h"
+#include "chre_api/chre/audio.h"
+
+#include "aoc/common/include/controller_ipc_aoc.h"
+#include "aoc/common/include/pipe_ipc_receiver_aoc.h"
+
+#include "efw/include/controller.h"
+
+#include "chre/platform/aoc/audio_filter.h"
+
+namespace chre {
+
+/**
+ * This class implements an audio controller, which sets up a filter object
+ * for buffering audio (see audio_filter.h), and manages both local and
+ * remote core commands to route audio data from the microphone to CHRE.
+ * To ensure proper timing of operation, commands are processed in a request-
+ * response pattern. The controller handles messages inbound from the remote
+ * core directly, via a PipeIPCReceiver object that it instantiates and binds
+ * to, while delegating outbound messages to a ControllerIPC object
+ * that it also instantiates. The controller handles all CHRE platform audio
+ * calls, relaying and collecting relevant information and data from the
+ * audio filter.
+ *
+ * Limitations:
+ * - Only a single microphone channel is currently supported
+ */
+class AudioController : public Controller {
+ public:
+ static constexpr size_t kMaxAocMicrophones = 1;
+
+ AudioController();
+
+ /**
+ * Calls the base class SetUp method, initializes audio source data,
+ * and initializes cross core communication.
+ */
+ void SetUp() override;
+
+ /**
+ * Sends a command to the remote core indicating that the A32 Audio
+ * Controller is fully up and ready to process data. This prompts
+ * the DSP to setup its own CHRE Audio Controller.
+ */
+ void SetUpRemoteCore();
+
+ /**
+ * Calls the base class TearDown method, and unbinds the shared ring
+ * buffer from the audio filter, and shuts the filter down.
+ */
+ void TearDown() override;
+
+ /**
+ * Handles commands inbound to the filter. This method is only
+ * invoked if the IPC channel for this controller and its cross
+ * core peer has been configured and setup correctly, else all
+ * messages are dropped by default by the worker thread.
+ *
+ * @return We always return true from this function to not trigger
+ * assertions in the underlying CmdThread, regardless of whether
+ * we received a valid command or not. On receiving a command not
+ * recognized by the controller, we simply set the command reply to
+ * an error value (-1).
+ */
+ bool CmdProcessor(struct CMD_HDR *cmd) override;
+
+ /**
+ * Get audio source information
+ *
+ * @return Pointer to chreAudioSource
+ */
+ const chreAudioSource *GetSource(uint32_t handle) const;
+
+ /**
+ * Enables audio requests on a handle
+ *
+ * @param handle Handle to be enabled
+ *
+ * @param enabled Handle is enabled if true, disabled otherwise
+ */
+ void SetEnabled(uint32_t handle, bool enabled);
+
+ /**
+ * Request audio data from given handle/source.
+ *
+ * @param handle Handle to an audio source to request data from.
+ *
+ * @return true on success
+ */
+ bool RequestAudio(uint32_t handle);
+
+ /**
+ * Cancel/Release audio data events from given handle.
+ *
+ * @param handle Handle to audio source to cancel data from
+ *
+ * @return true on success
+ */
+ bool ReleaseAudio(uint32_t handle);
+
+ /**
+ * Called by the CHRE release audio data event callback, this function
+ * lets the filter know that the current audio buffer was processed
+ * by all clients.
+ */
+ void OnBufferReleased();
+
+ private:
+ static constexpr uint64_t kSupportedDurationNs =
+ Seconds(AudioFilter::kSupportedDurationSeconds).toRawNanoseconds();
+ static constexpr const char *kMicrophoneName = "MIC_0";
+
+ bool mDspCoreInitDone = false;
+ bool mRequestStarted = false;
+
+ const chreAudioSource kAudioSource = {
+ .name = kMicrophoneName,
+ .sampleRate = AudioFilter::kSupportedSampleRate,
+ .minBufferDuration = kSupportedDurationNs,
+ .maxBufferDuration = kSupportedDurationNs,
+ .format = CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM};
+
+ chreAudioSourceStatus mSourceStatus = {.enabled = false, .suspended = false};
+
+ AudioFilter mFilter;
+
+ // The peer Controller IPC to the CHRE controller on the DSP,
+ // this class handles outbound messages from the audio controller.
+ ControllerIpcAoC mControllerIpcAoc;
+
+ // The peer Pipe Receiver to the CHRE pipe instance on the DSP,
+ // this class receives IPC pipe notifications sent by the said
+ // remote pipe.
+ PipeIpcReceiverAoC mPipeIpcReceiverAoc;
+
+ /**
+ * Invoked by the command processor when it gets a 'Ready'
+ * message from the DSP. Binds the Pipe object to the audio filter's
+ * input, and binds the ipc ring buffer to the audio filter.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+ int OnDspReady();
+
+ /**
+ * Checks the current streaming state of a handle/channel, sends
+ * a command to the remote core to start/stop streaming if they
+ * don't match, and updates the current state on the handle.
+ * Note that there's an ~50ms latency for a new 'start' on a handle.
+ *
+ * @param start Start streaming if enabled, stop otherwise.
+ *
+ * @return true on success.
+ *
+ * TODO: Handle the 'handle' parameter when multiple channels are supported,
+ * and update the state tracking logic accordingly.
+ */
+ bool UpdateStream(uint32_t handle, bool start);
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_AUDIO_CONTROLLER_H_
diff --git a/platform/aoc/include/chre/platform/aoc/audio_filter.h b/platform/aoc/include/chre/platform/aoc/audio_filter.h
new file mode 100644
index 0000000..260e6fa
--- /dev/null
+++ b/platform/aoc/include/chre/platform/aoc/audio_filter.h
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_AUDIO_FILTER_H_
+#define CHRE_PLATFORM_AOC_AUDIO_FILTER_H_
+
+#include "chre_api/chre/audio.h"
+#include "efw/include/filter.h"
+#include "filters/audio/common/include/audio_metadata.h"
+
+namespace chre {
+
+/**
+ * This class implements an audio filter, whose primary purpose is to
+ * read audio samples from a shared ring buffer, process it, and route
+ * it to registered audio clients. 'Filter', in this context, means a
+ * data processing unit (audio data itself is raw, with no actual filtering
+ * done on it). The audio filter is fully controlled by an instance of an
+ * audio controller object (see audio_controller.h). The interaction occurs
+ * as follows:
+ * - The controller instantiates a filter, and upon notification from the
+ * remote core that a ring buffer is available, binds it to the filter.
+ * - The controller also binds an IPC Pipe to the filter's input. The pipe's
+ * primary (and only) purpose is to notify the filter that there's fresh
+ * data in the ring buffer.
+ * - The filter then reads the ring buffer, does some processing (strip
+ * timestamps, format data, etc.), and sends said data out to clients via
+ * the CHRE AudioRequestManager.
+ * The filter instantiates a worker thread, which is woken up by any one of
+ * the following two mechanisms (outside of the automatic watchdog pet wakeup):
+ * - 'Signal': A signal is an internal (to the core) command, sent by the main
+ * audio controller, to notify the filter of local (to the core) conditions.
+ * For example, CHRE called a free data event, which the controller receives,
+ * and notifies the filter. Signals are processed by overloading the
+ * SignalProcessor method of the filter base class.
+ * - 'Input': An input is a cross core notification that the filter registers
+ * to, via the pipe that's bound to it. On a pipe notification, the thread
+ * wakes up to read/process data, which is done by overloading the
+ * InputProcessor method of the filter base class.
+ *
+ * TODO:
+ * 1 - Audio samples are currently collected and processed in SRAM, need to
+ * copy out to DRAM before passing to nanoapps, to free up the 2-sec
+ * buffer to resume collecting samples.
+ * 2 - Only a single channel (of the 4 available) is currently supported
+ * (blocked by 1)..
+ * 3 - The Input processor drops audio samples while the current buffer
+ * is being serviced by clients (blocked by 1)
+ */
+
+class AudioFilter : public Filter {
+ public:
+ // Defines the index of a signal sent by the controller
+ // on receiving CHRE's freeAudioDataEvent.
+ static constexpr size_t kBufferReleasedSignalIndex = 0;
+
+ // Defines the index at which the audio controller's IPC pipe binds to
+ // the filter input. Note that this is dictated by the 'Num Inputs'
+ // parameter to the filter constructor.
+ static constexpr size_t kPipeIndex = 0;
+
+ // Defines the index at which the audio controller's IPC ring buffer
+ // binds to the filter's ring buffer array. Note that this is dictated
+ // by the 'Num Ring Buffers' parameter to the filter constructor.
+ static constexpr size_t kRingIndex = 0;
+
+ static constexpr uint32_t kSupportedSampleRate = 16000;
+ static constexpr uint32_t kSupportedDurationSeconds = 2;
+
+ AudioFilter();
+
+ /**
+ * Callback that's called at the start of the 'Start' method,
+ * and before the worker thread has been instantiated.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+ int StartCallback() override final;
+
+ /**
+ * Callback that gets called at the end of the 'Stop' method,
+ * and after the worker thread has been shutdown.
+ *
+ * @return true on success
+ */
+ bool StopCallback() override final;
+
+ /**
+ * Method that handles commands that are internal to the core,
+ * typically sent by the audio controller. The argument to the
+ * function is predicated by the 'Command Depth' argument to the
+ * constructor. If the filter is expected to process a new signal,
+ * then the constructor argument needs to be incremented as well,
+ * as any index exceeding this is automatically dropped.
+ *
+ * @param index Signal ID (less than command depth) sent by the
+ * controller
+ *
+ * @return true on valid command and processing success
+ */
+ bool SignalProcessor(int index) override final;
+
+ /**
+ * Method that handles cross core pipe notifications, if one was bound
+ * to the filter's input. The 'pin' argument to the function is predicated
+ * by the 'Num Filter Inputs' argument to the constructor. If the filter is
+ * expected to process a new input (via a new pipe being bound to its
+ * input), then the constructor argument needs to be incremented as well,
+ * as any index exceeding this is automatically dropped. The pipe index
+ * depends on the order that InputBind was called. No lookup is provided,
+ * and the application is expected to keep track of this (maybe via an
+ * external enum).
+ *
+ * @param pin Index of the input pipe that was bound to the filter.
+ *
+ * @param message Input pipe notification to be processed. This can be
+ * a nullptr - which just means that the pipe wanted to notify a certain
+ * condition, but had no metadata to provide.
+ *
+ * @param size Input message size. Can be zero, if the pipe had no
+ * metadata to send.
+ */
+ void InputProcessor(int pin, void *message, size_t size) override final;
+
+ /**
+ * Callback that's invoked on CHRE's release audio data event.
+ *
+ * TODO: This needs to be refactored when we store audio samples in DRAM.
+ */
+ void OnBufferReleased();
+
+ private:
+ static constexpr size_t kSamplesPer2Sec =
+ kSupportedSampleRate * kSupportedDurationSeconds;
+
+ int16_t mSampleBufferSram[kSamplesPer2Sec];
+ int16_t *mSampleBufferDram = nullptr;
+ void *mRingBufferHandle = nullptr;
+ size_t mSampleCount = 0;
+ bool mDramBufferInUse = false;
+
+ struct chreAudioDataEvent mDataEvent = {
+ .version = CHRE_AUDIO_DATA_EVENT_VERSION,
+ .reserved = {0, 0, 0},
+ .handle = 0,
+ .timestamp = 0,
+ .sampleRate = kSupportedSampleRate,
+ .sampleCount = 0,
+ .format = CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM,
+ .samplesS16 = nullptr};
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_AUDIO_FILTER_H_
diff --git a/platform/aoc/include/chre/platform/aoc/memory.h b/platform/aoc/include/chre/platform/aoc/memory.h
new file mode 100644
index 0000000..38b6928
--- /dev/null
+++ b/platform/aoc/include/chre/platform/aoc/memory.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_MEMORY_H_
+#define CHRE_PLATFORM_AOC_MEMORY_H_
+
+#include <cstddef>
+
+#include "chre/platform/shared/memory.h"
+
+namespace chre {
+
+/**
+ * Memory allocation specifically using the DRAM heap. The semantics are the
+ * same as malloc.
+ */
+void *memoryAllocDram(size_t size);
+
+/**
+ * Memory free from memory allocated using the DRAM heap. The semantics are the
+ * same as free.
+ */
+void memoryFreeDram(void *pointer);
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_MEMORY_H_
diff --git a/platform/aoc/include/chre/platform/aoc/system_time.h b/platform/aoc/include/chre/platform/aoc/system_time.h
new file mode 100644
index 0000000..26c4fca
--- /dev/null
+++ b/platform/aoc/include/chre/platform/aoc/system_time.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_SYSTEM_TIME_H_
+#define CHRE_PLATFORM_AOC_SYSTEM_TIME_H_
+
+#include <cstdint>
+
+namespace chre {
+
+/**
+ * Sets the estimated offset between the host and AoC clock.
+ *
+ * @param offset The current estimated offset in nanoseconds.
+ */
+void setEstimatedHostTimeOffset(int64_t offset);
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_SYSTEM_TIME_H_
diff --git a/platform/aoc/include/chre/target_platform/assert.h b/platform/aoc/include/chre/target_platform/assert.h
new file mode 100644
index 0000000..aec4e33
--- /dev/null
+++ b/platform/aoc/include/chre/target_platform/assert.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_ASSERT_H_
+#define CHRE_PLATFORM_AOC_ASSERT_H_
+
+#include "chre/platform/fatal_error.h"
+
+#define CHRE_ASSERT(condition) \
+ do { \
+ if (!(condition)) { \
+ FATAL_ERROR("Assertion failure at %s:%d", CHRE_FILENAME, __LINE__); \
+ } \
+ } while (0)
+
+#endif // CHRE_PLATFORM_AOC_ASSERT_H_
diff --git a/platform/aoc/include/chre/target_platform/fatal_error.h b/platform/aoc/include/chre/target_platform/fatal_error.h
new file mode 100644
index 0000000..8a5c5c8
--- /dev/null
+++ b/platform/aoc/include/chre/target_platform/fatal_error.h
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2019 Google LLC. All Rights Reserved
+ *
+ * @file fatal_error.h
+ *
+ * @brief Fatal Error Handling
+ */
+
+#ifndef CHRE_PLATFORM_AOC_FATAL_ERROR_H_
+#define CHRE_PLATFORM_AOC_FATAL_ERROR_H_
+
+#include <cstdlib>
+
+#define FATAL_ERROR_QUIT() \
+ do { \
+ chre::preFatalError(); \
+ abort(); \
+ } while (0)
+
+namespace chre {
+
+/**
+ * Do preparation for an impending fatal error including flushing logs.
+ *
+ * It must not be possible for FATAL_ERROR() to be called by this function or
+ * any of its callees.
+ */
+void preFatalError();
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_FATAL_ERROR_H_
diff --git a/platform/aoc/include/chre/target_platform/host_link_base.h b/platform/aoc/include/chre/target_platform/host_link_base.h
new file mode 100644
index 0000000..638866e
--- /dev/null
+++ b/platform/aoc/include/chre/target_platform/host_link_base.h
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_HOST_LINK_BASE_H_
+#define CHRE_PLATFORM_AOC_HOST_LINK_BASE_H_
+
+#include "chre/util/time.h"
+
+#include "usf/error.h"
+#include "usf/usf_transport.h"
+#include "usf/usf_work.h"
+
+namespace chre {
+
+/**
+ * Helper function to send debug dump result to host.
+ */
+void sendDebugDumpResultToHost(uint16_t hostClientId, const char *debugStr,
+ size_t debugStrSize, bool complete,
+ uint32_t dataCount);
+
+/**
+ * This class implements a basic wrapper for sending/receiving messages over USF
+ * transports. It sets up a callback into the USF transport layer for receiving
+ * messages on instantiation. The first message that we receive from the USF
+ * transport layer includes a pointer to a 'transport' abstraction, that can be
+ * used to send messages out of CHRE.
+ *
+ * TODO: Revisit this implementation after the USF feature requests in
+ * b/149318001 and b/149317051 are fulfilled
+ */
+class HostLinkBase {
+ public:
+ HostLinkBase();
+ ~HostLinkBase();
+
+ /**
+ * Initializes the host link with the transport provided when the CHRE daemon
+ * sends its first message to AoC. If called multiple times, only the first
+ * transport is used.
+ *
+ * TODO (b/149317051) Remove this once USF exposes an API to acquire a
+ * transport for communicating with the AP.
+ */
+ void init(usf::UsfTransport *transport);
+
+ /**
+ * Sends a message to the CHRE daemon.
+ *
+ * @param data data to be sent to the daemon
+ * @param dataLen length of the data being sent
+ * @return true if the data was successfully queued for sending
+ */
+ bool send(uint8_t *data, size_t dataLen);
+
+ refcount::reffed_ptr<usf::UsfWorker> &getWorker() {
+ return mWorker;
+ }
+
+ /**
+ * The USF external message handler function for CHRE, that
+ * is invoked on CHRE inbound messages from the AP
+ *
+ * @param transport A pointer to a transport that is used to send messages
+ * out of CHRE to the AP
+ * @param msg flatbuffer encoded message of type UsfMsgType_CHRE
+ * @param arg yields the message length when cast to size_t
+ * @return usf::kErrNone if success, an error code indicating the failure
+ * otherwise
+ */
+ static usf::UsfErr handleUsfMessage(usf::UsfTransport *transport,
+ const usf::UsfMsg *msg, void *arg);
+
+ /**
+ * Sends a request to the host for a time sync message.
+ */
+ static void sendTimeSyncRequest();
+
+ /**
+ * Enqueues a log message to be sent to the host.
+ *
+ * @param logMessage Pointer to a buffer that has the log message. Note that
+ * the message might be encoded
+ *
+ * @param logMessageSize length of the log message buffer
+ */
+ void sendLogMessage(const uint8_t *logMessage, size_t logMessageSize);
+
+ private:
+ //! The last time a time sync request message has been sent.
+ static Nanoseconds mLastTimeSyncRequestNanos;
+
+ //! Transport handle provided when the CHRE daemon sends its first message
+ //! to AoC.
+ usf::UsfTransport *mTransportHandle = nullptr;
+
+ //! Worker thread used to ensure single-threaded communication with CHRE.
+ refcount::reffed_ptr<usf::UsfWorker> mWorker;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_HOST_LINK_BASE_H_
diff --git a/platform/aoc/include/chre/target_platform/log.h b/platform/aoc/include/chre/target_platform/log.h
new file mode 100644
index 0000000..f2fb3d5
--- /dev/null
+++ b/platform/aoc/include/chre/target_platform/log.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+#ifndef CHRE_PLATFORM_AOC_LOG_H_
+#define CHRE_PLATFORM_AOC_LOG_H_
+
+#include <chre.h>
+#include <stdio.h>
+#include "efw_log.h"
+
+#ifndef __FILENAME__
+#ifdef __BASE_FILE__
+#define __FILENAME__ __BASE_FILE__
+#else
+#define __FILENAME__ __FILE__
+#endif // __BASE_FILE__
+#endif // __FILE_NAME__
+
+#ifndef CHRE_DL_VERBOSE
+#define CHRE_DL_VERBOSE false
+#endif // CHRE_DL_VERBOSE
+
+// TODO(b/149317051): The printf in the below macro is needed until CHRE can log
+// to the AP before the daemon has connected to AoC.
+#define CHRE_AOC_LOG(level, fmt, ...) \
+ do { \
+ CHRE_LOG_PREAMBLE \
+ chre::log(level, fmt, ##__VA_ARGS__); \
+ DBG_MSG_RAW(255, "CHRE:%s:%d\t" fmt "", __FILENAME__, __LINE__, \
+ ##__VA_ARGS__); \
+ CHRE_LOG_EPILOGUE \
+ } while (0)
+
+#define LOGE(fmt, ...) CHRE_AOC_LOG(CHRE_LOG_ERROR, fmt, ##__VA_ARGS__)
+#define LOGW(fmt, ...) CHRE_AOC_LOG(CHRE_LOG_WARN, fmt, ##__VA_ARGS__)
+#define LOGI(fmt, ...) CHRE_AOC_LOG(CHRE_LOG_INFO, fmt, ##__VA_ARGS__)
+#define LOGD(fmt, ...) CHRE_AOC_LOG(CHRE_LOG_DEBUG, fmt, ##__VA_ARGS__)
+
+#if CHRE_DL_VERBOSE
+#define LOGV(fmt, ...) LOGD(fmt, ##__VA_ARGS__)
+#else
+#define LOGV(fmt, ...)
+#endif // CHRE_DL_VERBOSE
+
+namespace chre {
+
+void log(enum chreLogLevel level, const char *formatStr, ...);
+void vaLog(enum chreLogLevel level, const char *format, va_list args);
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_LOG_H_
diff --git a/platform/aoc/include/chre/target_platform/platform_audio_base.h b/platform/aoc/include/chre/target_platform/platform_audio_base.h
new file mode 100644
index 0000000..d42d2c8
--- /dev/null
+++ b/platform/aoc/include/chre/target_platform/platform_audio_base.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_PLATFORM_AUDIO_BASE_H_
+#define CHRE_PLATFORM_AOC_PLATFORM_AUDIO_BASE_H_
+
+#include <cstring>
+
+#include "chre/platform/aoc/audio_controller.h"
+#include "chre/platform/platform_audio.h"
+
+namespace chre {
+
+/**
+ * The base PlatformAudio class for AoC to inject platform
+ * specific functionality from. Instantiates an Audio Controller object,
+ * which handles communications, data buffering, and audio data event
+ * dispatch for CHRE.
+ */
+class PlatformAudioBase {
+ protected:
+ AudioController mAudioController;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_PLATFORM_AUDIO_BASE_H_
diff --git a/platform/aoc/include/chre/target_platform/platform_log_base.h b/platform/aoc/include/chre/target_platform/platform_log_base.h
new file mode 100644
index 0000000..1d5657a
--- /dev/null
+++ b/platform/aoc/include/chre/target_platform/platform_log_base.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_PLATFORM_LOG_BASE_H_
+#define CHRE_PLATFORM_AOC_PLATFORM_LOG_BASE_H_
+
+namespace chre {
+
+class PlatformLogBase {
+ // TODO: Stubbed out, implement this
+ // Can we EFWObject::PrintSet(printf) here instead of in the application?
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_PLATFORM_LOG_BASE_H_
diff --git a/platform/aoc/include/chre/target_platform/power_control_manager_base.h b/platform/aoc/include/chre/target_platform/power_control_manager_base.h
new file mode 100644
index 0000000..fdd9d46
--- /dev/null
+++ b/platform/aoc/include/chre/target_platform/power_control_manager_base.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_POWER_CONTROL_MANAGER_BASE_H
+#define CHRE_PLATFORM_AOC_POWER_CONTROL_MANAGER_BASE_H
+
+#include "chre/platform/atomic.h"
+
+namespace chre {
+
+class PowerControlManagerBase {
+ public:
+ PowerControlManagerBase();
+
+ /**
+ * Updates internal wake/suspend flag and pushes awake/sleep notification
+ * to nanoapps that are listening for it.
+ *
+ * @param awake true if host is awake, otherwise suspended.
+ */
+ void onHostWakeSuspendEvent(bool awake);
+
+ protected:
+ //! Set to true if the host is awake, false if suspended.
+ AtomicBool mHostIsAwake;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_POWER_CONTROL_MANAGER_BASE_H
diff --git a/platform/aoc/include/chre/target_platform/system_timer_base.h b/platform/aoc/include/chre/target_platform/system_timer_base.h
new file mode 100644
index 0000000..94469c0
--- /dev/null
+++ b/platform/aoc/include/chre/target_platform/system_timer_base.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_AOC_SYSTEM_TIMER_BASE_H_
+#define CHRE_PLATFORM_AOC_SYSTEM_TIMER_BASE_H_
+
+#include <cstdint>
+
+#include "chre/platform/assert.h"
+#include "chre/util/time_impl.h"
+
+#include "efw/include/timer.h"
+
+#include "FreeRTOS.h"
+#include "task.h"
+
+namespace chre {
+
+/**
+ * The AOC platform base class for the SystemTimer. The AOC implementation uses
+ * a EFW timer.
+ */
+class SystemTimerBase {
+ protected:
+ //! The timer handle that is generated by Timer::EventAdd.
+ void *mTimerHandle;
+
+ // TODO(b/168645313): Share the same dispatch thread amongst all timer
+ // instances.
+ //! A FreeRTOS Thread to dispatch timer callbacks
+ TaskHandle_t mTimerCbDispatchThreadHandle = nullptr;
+
+ //! Stack size (in words) of the timer callback dispatch thread
+ static constexpr uint32_t kStackDepthWords = configMINIMAL_STACK_SIZE;
+
+ //! Task stack associated with this timer.
+ StackType_t mTaskStack[kStackDepthWords];
+
+ //! FreeRTOS struct to hold the TCB of the timer dispatch thread
+ StaticTask_t mTaskBuffer;
+
+ //! Tracks whether the timer has been initialized correctly.
+ bool mInitialized = false;
+
+ //! A static method that is invoked by the underlying EFW timer.
+ static bool systemTimerNotifyCallback(void *context);
+
+ //! This function implements the timer callback dispatch thread.
+ // It blocks until it's woken up by the underlying system timer's ISR,
+ // then executes the CHRE timer callback from the dispatch thread context.
+ static void timerCallbackDispatch(void *context);
+};
+
+static_assert(configSUPPORT_STATIC_ALLOCATION == 1,
+ "Static task allocation must be supported!");
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_AOC_SYSTEM_TIMER_BASE_H_
diff --git a/platform/aoc/log.cc b/platform/aoc/log.cc
new file mode 100644
index 0000000..689cef1
--- /dev/null
+++ b/platform/aoc/log.cc
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#include "chre/target_platform/log.h"
+
+#include "chre/core/event_loop_manager.h"
+#include "chre/platform/system_time.h"
+
+#include <endian.h>
+
+namespace chre {
+namespace {
+constexpr size_t kChreLogBufferSize = CHRE_MESSAGE_TO_HOST_MAX_SIZE;
+char logBuffer[kChreLogBufferSize];
+} // namespace
+
+void log(enum chreLogLevel level, const char *formatStr, ...) {
+ va_list args;
+ va_start(args, formatStr);
+ vaLog(level, formatStr, args);
+ va_end(args);
+}
+
+// TODO: b/146164384 - We will need to batch logs rather than send them
+// one at a time to avoid waking the AP.
+void vaLog(enum chreLogLevel level, const char *format, va_list args) {
+ auto &hostCommsMgr =
+ chre::EventLoopManagerSingleton::get()->getHostCommsManager();
+
+ // See host_messages.fbs for the log message format.
+ size_t logBufIndex = 0;
+ logBuffer[logBufIndex] = level + 1;
+ logBufIndex++;
+
+ uint64_t currentTimeNs = SystemTime::getMonotonicTime().toRawNanoseconds();
+ memcpy(&logBuffer[logBufIndex], ¤tTimeNs, sizeof(uint64_t));
+ logBufIndex += sizeof(uint64_t);
+
+ int msgLen = vsnprintf(&logBuffer[logBufIndex],
+ kChreLogBufferSize - logBufIndex, format, args);
+ if (msgLen >= 0) {
+ // msgLen doesn't include the terminating null char.
+ logBufIndex += msgLen + 1;
+
+ hostCommsMgr.sendLogMessage(reinterpret_cast<uint8_t *>(logBuffer),
+ logBufIndex);
+ }
+}
+
+} // namespace chre
diff --git a/platform/aoc/memory.cc b/platform/aoc/memory.cc
new file mode 100644
index 0000000..92d3fa9
--- /dev/null
+++ b/platform/aoc/memory.cc
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/aoc/memory.h"
+#include "chre/platform/memory.h"
+#include "chre/platform/mutex.h"
+#include "chre/platform/shared/dram_vote_client.h"
+#include "chre/platform/shared/pal_system_api.h"
+
+#include "basic.h"
+#include "heap.h"
+
+#include <cstdlib>
+
+//! Beginning of the CHRE SRAM heap.
+extern uintptr_t __heap_chre_beg__;
+//! End of the CHRE SRAM heap.
+extern uintptr_t __heap_chre_end__;
+//! Beginning of the CHRE DRAM heap.
+extern uintptr_t __heap_chre_dram_beg__;
+//! End of the CHRE DRAM heap.
+extern uintptr_t __heap_chre_dram_end__;
+
+namespace chre {
+namespace {
+
+//! Handle to the SRAM heap.
+void *gSramHeap = nullptr;
+//! Mutex used to synchronize access to the SRAM heap.
+Mutex gSramMutex;
+//! Handle to the DRAM heap.
+void *gDramHeap = nullptr;
+//! Mutex used to synchronize access to the DRAM heap.
+Mutex gDramMutex;
+
+enum ChreHeap { DRAM, SRAM };
+
+//! Helper functions passed to EFW so it can lock when heap allocations / frees
+//! are performed.
+inline void LockMutex(void *mutex) {
+ static_cast<Mutex *>(mutex)->lock();
+}
+inline void UnlockMutex(void *mutex) {
+ static_cast<Mutex *>(mutex)->unlock();
+}
+
+void *GetSramHeap() {
+ if (gSramHeap == nullptr) {
+ auto heap_base = &__heap_chre_beg__;
+ auto heap_size = PTR_OFFSET_GET(&__heap_chre_end__, heap_base);
+
+ gSramHeap = HeapInit(heap_base, heap_size);
+ CHRE_ASSERT(gSramHeap != nullptr);
+ if (gSramHeap == nullptr) {
+ return nullptr;
+ } else {
+ HeapLocking(gSramHeap, &gSramMutex, LockMutex, UnlockMutex);
+ }
+ }
+ return gSramHeap;
+}
+
+void *GetDramHeap() {
+ if (gDramHeap == nullptr) {
+ auto heap_base = &__heap_chre_dram_beg__;
+ auto heap_size = PTR_OFFSET_GET(&__heap_chre_dram_end__, heap_base);
+
+ gDramHeap = HeapInit(heap_base, heap_size);
+ CHRE_ASSERT(gDramHeap != nullptr);
+ if (gDramHeap == nullptr) {
+ return nullptr;
+ } else {
+ HeapLocking(gDramHeap, &gDramMutex, LockMutex, UnlockMutex);
+ }
+ }
+ return gDramHeap;
+}
+
+void *GetHeap(ChreHeap heap) {
+ if (heap == ChreHeap::SRAM) {
+ return GetSramHeap();
+ } else if (heap == ChreHeap::DRAM) {
+ return GetDramHeap();
+ } else {
+ CHRE_ASSERT(false);
+ }
+ return nullptr;
+}
+
+bool IsInHeap(ChreHeap heap, const void *pointer) {
+ void *handle = GetHeap(heap);
+ bool found = false;
+ if (handle != nullptr) {
+ const struct HEAP_STATS *stats = HeapStats(handle);
+ const uintptr_t heapBase = reinterpret_cast<uintptr_t>(stats->base);
+ const uintptr_t castPointer = reinterpret_cast<uintptr_t>(pointer);
+ found = castPointer >= heapBase && castPointer < (heapBase + stats->len);
+ }
+
+ return found;
+}
+
+} // namespace
+
+void *memoryAlloc(size_t size) {
+ void *ptr = nullptr;
+ void *handle = GetSramHeap();
+ if (handle != nullptr) {
+ ptr = HeapMalloc(handle, size);
+ // Fall back to DRAM memory when SRAM memory is exhausted. Must exclude size
+ // 0 as clients may not explicitly free memory of size 0, which may
+ // mistakenly leave DRAM accessible
+ if (ptr == nullptr && size != 0) {
+ // Increment DRAM vote count to prevent system from powering down DRAM
+ // while DRAM memory is in use.
+ DramVoteClientSingleton::get()->incrementDramVoteCount();
+ ptr = memoryAllocDram(size);
+
+ // DRAM allocation failed too.
+ if (ptr == nullptr) {
+ DramVoteClientSingleton::get()->decrementDramVoteCount();
+ }
+ }
+ }
+
+ return ptr;
+}
+
+void *memoryAllocAligned(size_t alignment, size_t size) {
+ void *ptr = nullptr;
+ void *handle = GetSramHeap();
+
+ if (handle != nullptr) {
+ ptr = HeapAlignedAlloc(handle, alignment, size);
+ // This method is currently only used for nanoapp loading so don't fall back
+ // to DRAM or there can be power implications.
+ }
+
+ return ptr;
+}
+
+void *memoryAllocDramAligned(size_t alignment, size_t size) {
+ CHRE_ASSERT_LOG(DramVoteClientSingleton::get()->isDramVoteActive(),
+ "DRAM allocation when not accessible");
+
+ void *ptr = nullptr;
+ void *handle = GetDramHeap();
+
+ if (handle != nullptr) {
+ ptr = HeapAlignedAlloc(handle, alignment, size);
+ }
+
+ return ptr;
+}
+
+void *memoryAllocDram(size_t size) {
+ CHRE_ASSERT_LOG(DramVoteClientSingleton::get()->isDramVoteActive(),
+ "DRAM allocation when not accessible");
+
+ void *ptr = nullptr;
+ void *handle = GetDramHeap();
+ if (handle != nullptr) {
+ ptr = HeapMalloc(handle, size);
+ }
+
+ return ptr;
+}
+
+void memoryFree(void *pointer) {
+ if (pointer != nullptr) {
+ if (IsInHeap(ChreHeap::SRAM, pointer)) {
+ HeapFree(GetHeap(ChreHeap::SRAM), pointer);
+ } else {
+ memoryFreeDram(pointer);
+ DramVoteClientSingleton::get()->decrementDramVoteCount();
+ }
+ }
+}
+
+void memoryFreeDram(void *pointer) {
+ CHRE_ASSERT_LOG(DramVoteClientSingleton::get()->isDramVoteActive(),
+ "DRAM freed when not accessible");
+
+ if (pointer != nullptr) {
+ bool inDramHeap = IsInHeap(ChreHeap::DRAM, pointer);
+ CHRE_ASSERT(inDramHeap);
+ if (inDramHeap) {
+ HeapFree(GetDramHeap(), pointer);
+ }
+ }
+}
+
+void forceDramAccess() {
+ DramVoteClientSingleton::get()->voteDramAccess(true /* enabled */);
+}
+
+void removeDramAccessVote() {
+ DramVoteClientSingleton::get()->voteDramAccess(false /* enabled */);
+}
+
+void *palSystemApiMemoryAlloc(size_t size) {
+ return memoryAlloc(size);
+}
+
+void palSystemApiMemoryFree(void *pointer) {
+ memoryFree(pointer);
+}
+
+} // namespace chre
diff --git a/platform/aoc/platform_audio.cc b/platform/aoc/platform_audio.cc
new file mode 100644
index 0000000..0019697
--- /dev/null
+++ b/platform/aoc/platform_audio.cc
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/platform_audio.h"
+#include <cinttypes>
+#include "chre/core/event_loop_manager.h"
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+#include "chre/platform/system_time.h"
+
+namespace chre {
+
+PlatformAudio::PlatformAudio() {}
+
+PlatformAudio::~PlatformAudio() {}
+
+void PlatformAudio::init() {
+ mAudioController.Start();
+ mAudioController.SetUpRemoteCore();
+}
+
+size_t PlatformAudio::getSourceCount() {
+ return AudioController::kMaxAocMicrophones;
+}
+
+bool PlatformAudio::getAudioSource(uint32_t handle,
+ struct chreAudioSource *source) const {
+ bool success = false;
+ if ((handle < AudioController::kMaxAocMicrophones) && (source != nullptr)) {
+ memcpy(source, mAudioController.GetSource(handle), sizeof(chreAudioSource));
+ success = true;
+ }
+
+ return success;
+}
+
+void PlatformAudio::setHandleEnabled(uint32_t handle, bool enabled) {
+ mAudioController.SetEnabled(handle, enabled);
+}
+
+void PlatformAudio::cancelAudioDataEventRequest(uint32_t handle) {
+ mAudioController.ReleaseAudio(handle);
+}
+
+bool PlatformAudio::requestAudioDataEvent(uint32_t handle, uint32_t numSamples,
+ Nanoseconds eventDelay) {
+ // TODO: Event delay
+ bool success = mAudioController.RequestAudio(handle);
+ LOGV("Request audio data for hdl %u, numSamples %u @ %" PRIu64
+ ", success: %d",
+ handle, numSamples, SystemTime::getMonotonicTime().toRawNanoseconds(),
+ success);
+
+ return success;
+}
+
+void PlatformAudio::releaseAudioDataEvent(
+ struct chreAudioDataEvent * /*event*/) {
+ mAudioController.OnBufferReleased();
+}
+
+} // namespace chre
diff --git a/platform/aoc/platform_cache_management.cc b/platform/aoc/platform_cache_management.cc
new file mode 100644
index 0000000..b375d16
--- /dev/null
+++ b/platform/aoc/platform_cache_management.cc
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#include "chre/target_platform/platform_cache_management.h"
+#include "core/arm/generic/include/cache_management.h"
+#include "core/arm/generic/include/memory_barrier.h"
+
+namespace chre {
+
+namespace {
+
+void invalidateInstructionCache() {
+ INSTRUCTION_CACHE_INVALIDATE();
+ MB();
+}
+
+void cleanAndInvalidateDataCache() {
+ CACHE_SET_WAY_FLUSH_AND_INVALIDATE_ALL();
+ MB();
+}
+
+} // anonymous namespace
+
+void wipeSystemCaches() {
+ cleanAndInvalidateDataCache();
+ invalidateInstructionCache();
+}
+
+} // namespace chre
diff --git a/platform/aoc/platform_pal.cc b/platform/aoc/platform_pal.cc
new file mode 100644
index 0000000..515a3e5
--- /dev/null
+++ b/platform/aoc/platform_pal.cc
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/shared/platform_pal.h"
+
+namespace chre {
+
+void PlatformPal::prePalApiCall() const {}
+
+} // namespace chre
diff --git a/platform/aoc/power_control_manager.cc b/platform/aoc/power_control_manager.cc
new file mode 100644
index 0000000..8e57651
--- /dev/null
+++ b/platform/aoc/power_control_manager.cc
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/power_control_manager.h"
+
+#include "chre/core/event_loop_manager.h"
+#include "chre/platform/shared/memory.h"
+
+namespace chre {
+
+PowerControlManagerBase::PowerControlManagerBase() : mHostIsAwake(true) {}
+
+void PowerControlManagerBase::onHostWakeSuspendEvent(bool awake) {
+ if (mHostIsAwake != awake) {
+ mHostIsAwake = awake;
+
+ if (!awake) {
+ EventLoopManagerSingleton::get()
+ ->getHostCommsManager()
+ .resetBlameForNanoappHostWakeup();
+ }
+
+ EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
+ awake ? CHRE_EVENT_HOST_AWAKE : CHRE_EVENT_HOST_ASLEEP,
+ nullptr /* eventData */, nullptr /* freeCallback */);
+ }
+}
+
+void PowerControlManager::postEventLoopProcess(size_t numPendingEvents) {
+ if (numPendingEvents == 0) {
+ removeDramAccessVote();
+ }
+}
+
+bool PowerControlManager::hostIsAwake() {
+ return mHostIsAwake;
+}
+
+} // namespace chre
diff --git a/platform/aoc/system_time.cc b/platform/aoc/system_time.cc
new file mode 100644
index 0000000..ded9a63
--- /dev/null
+++ b/platform/aoc/system_time.cc
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#include "chre/platform/system_time.h"
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+
+#include "efw/include/timer.h"
+
+namespace chre {
+
+namespace {
+
+int64_t gEstimatedHostTimeOffset = 0;
+
+}
+
+Nanoseconds SystemTime::getMonotonicTime() {
+ return Nanoseconds(Timer::Instance()->TimestampNanoseconds());
+}
+
+int64_t SystemTime::getEstimatedHostTimeOffset() {
+ return gEstimatedHostTimeOffset;
+}
+
+void setEstimatedHostTimeOffset(int64_t offset) {
+ gEstimatedHostTimeOffset = offset;
+}
+
+} // namespace chre
diff --git a/platform/aoc/system_timer.cc b/platform/aoc/system_timer.cc
new file mode 100644
index 0000000..4650f8c
--- /dev/null
+++ b/platform/aoc/system_timer.cc
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/system_timer.h"
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+#include "chre/util/time.h"
+
+#include "efw/include/timer.h"
+
+namespace {
+
+// The timer dispatch thread is notifed from a
+// timer interrupt context, and there are context checks in the FreeRTOS code
+// (i.e. the xxGive and xxGiveFromISR are not interchangeable). Since there
+// are no interrupts in a simulated platform, we end up with two different
+// notification mechanisms that accomplish the same purpose.
+void wakeupDispatchThreadFromIsr(TaskHandle_t &handle) {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ vTaskNotifyGiveFromISR(handle, &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+}
+
+} // namespace
+
+namespace chre {
+
+bool SystemTimerBase::systemTimerNotifyCallback(void *context) {
+ SystemTimer *pTimer = static_cast<SystemTimer *>(context);
+ if (pTimer != nullptr) {
+ wakeupDispatchThreadFromIsr(pTimer->mTimerCbDispatchThreadHandle);
+ }
+
+ // The EFW timer callback is setup in such a way that returning 'true'
+ // here reschedules the timer, while returning 'false' does not.
+ // Since we're interested in a one-shot timer, we return 'false' here.
+ return false;
+}
+
+void SystemTimerBase::timerCallbackDispatch(void *context) {
+ SystemTimer *pTimer = static_cast<SystemTimer *>(context);
+ if (pTimer == nullptr) {
+ FATAL_ERROR("Null System Timer");
+ }
+
+ while (true) {
+ ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+ // Obtain pointer to callback before checking its value to avoid race
+ // with cancel().
+ SystemTimerCallback *callback = pTimer->mCallback;
+ if (callback != nullptr) {
+ pTimer->mTimerHandle = nullptr;
+ callback(pTimer->mData);
+ }
+ }
+}
+
+SystemTimer::SystemTimer() {}
+
+SystemTimer::~SystemTimer() {
+ // cancel an existing timer if any
+ cancel();
+ // Delete the timer dispatch thread if it was created
+ if (mTimerCbDispatchThreadHandle != nullptr) {
+ vTaskDelete(mTimerCbDispatchThreadHandle);
+ mTimerCbDispatchThreadHandle = nullptr;
+ }
+}
+
+bool SystemTimer::init() {
+ // TODO(b/168526254): Balance this priority with other CHRE tasks.
+ constexpr UBaseType_t kTaskPriority = tskIDLE_PRIORITY + 15;
+ const char *const kTaskName = "ChreTimerCB";
+
+ mTimerCbDispatchThreadHandle =
+ xTaskCreateStatic(timerCallbackDispatch, kTaskName, kStackDepthWords,
+ this, kTaskPriority, mTaskStack, &mTaskBuffer);
+ if (mTimerCbDispatchThreadHandle != nullptr) {
+ mInitialized = true;
+ } else {
+ LOGE("Failed to create Timer Dispatch Thread");
+ }
+
+ return mInitialized;
+}
+
+bool SystemTimer::set(SystemTimerCallback *callback, void *data,
+ Nanoseconds delay) {
+ bool success = false;
+
+ if (mInitialized) {
+ // Cancel the existing timer to avoid a race where the old timer expires
+ // when setting up the new timer.
+ cancel();
+
+ mCallback = callback;
+ mData = data;
+
+ Timer *timer = Timer::Instance();
+ int rc =
+ timer->EventAddAtOffset(timer->NsToTicks(delay.toRawNanoseconds()),
+ systemTimerNotifyCallback, this, &mTimerHandle);
+
+ if (rc != 0) {
+ LOGE("Failed to set timer");
+ } else {
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool SystemTimer::cancel() {
+ int rc = -1;
+ if (mTimerHandle != nullptr) {
+ rc = Timer::Instance()->EventRemove(mTimerHandle);
+ mTimerHandle = nullptr;
+ mCallback = nullptr;
+ }
+
+ return (rc == 0) ? true : false;
+}
+
+bool SystemTimer::isActive() {
+ return mTimerHandle != nullptr;
+}
+
+} // namespace chre
diff --git a/platform/freertos/context.cc b/platform/freertos/context.cc
new file mode 100644
index 0000000..8ee7448
--- /dev/null
+++ b/platform/freertos/context.cc
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/context.h"
+#include "chre/target_platform/init.h"
+
+#include "FreeRTOS.h"
+#include "task.h"
+
+namespace chre {
+
+bool inEventLoopThread() {
+ TaskHandle_t evtLoopTaskHandle = xTaskGetHandle(freertos::getChreTaskName());
+ TaskHandle_t currentTaskHandle = xTaskGetCurrentTaskHandle();
+
+ return (evtLoopTaskHandle == currentTaskHandle);
+}
+
+} // namespace chre
diff --git a/platform/freertos/include/chre/target_platform/atomic_base.h b/platform/freertos/include/chre/target_platform/atomic_base.h
new file mode 100644
index 0000000..582d0ae
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/atomic_base.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_ATOMIC_BASE_H_
+#define CHRE_PLATFORM_FREERTOS_ATOMIC_BASE_H_
+
+#include <atomic>
+
+namespace chre {
+
+template <typename AtomicType>
+class AtomicBase {
+ protected:
+ std::atomic<AtomicType> mAtomic;
+};
+
+typedef AtomicBase<bool> AtomicBoolBase;
+typedef AtomicBase<uint32_t> AtomicUint32Base;
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_FREERTOS_ATOMIC_BASE_H_
diff --git a/platform/freertos/include/chre/target_platform/atomic_base_impl.h b/platform/freertos/include/chre/target_platform/atomic_base_impl.h
new file mode 100644
index 0000000..d44a197
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/atomic_base_impl.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_ATOMIC_BASE_IMPL_H_
+#define CHRE_PLATFORM_FREERTOS_ATOMIC_BASE_IMPL_H_
+
+#include "chre/platform/atomic.h"
+
+namespace chre {
+
+inline AtomicBool::AtomicBool(bool startingValue) {
+ std::atomic_init(&mAtomic, startingValue);
+}
+
+inline bool AtomicBool::operator=(bool desired) {
+ mAtomic = desired;
+ return desired;
+}
+
+inline bool AtomicBool::load() const {
+ return mAtomic.load();
+}
+
+inline void AtomicBool::store(bool desired) {
+ mAtomic.store(desired);
+}
+
+inline bool AtomicBool::exchange(bool desired) {
+ return mAtomic.exchange(desired);
+}
+
+inline AtomicUint32::AtomicUint32(uint32_t startingValue) {
+ std::atomic_init(&mAtomic, startingValue);
+}
+
+inline uint32_t AtomicUint32::operator=(uint32_t desired) {
+ mAtomic = desired;
+ return desired;
+}
+
+inline uint32_t AtomicUint32::load() const {
+ return mAtomic.load();
+}
+
+inline void AtomicUint32::store(uint32_t desired) {
+ mAtomic.store(desired);
+}
+
+inline uint32_t AtomicUint32::exchange(uint32_t desired) {
+ return mAtomic.exchange(desired);
+}
+
+inline uint32_t AtomicUint32::fetch_add(uint32_t arg) {
+ return mAtomic.fetch_add(arg);
+}
+
+inline uint32_t AtomicUint32::fetch_increment() {
+ return mAtomic.fetch_add(1);
+}
+
+inline uint32_t AtomicUint32::fetch_sub(uint32_t arg) {
+ return mAtomic.fetch_sub(arg);
+}
+
+inline uint32_t AtomicUint32::fetch_decrement() {
+ return mAtomic.fetch_sub(1);
+}
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_FREERTOS_ATOMIC_BASE_IMPL_H_
diff --git a/platform/freertos/include/chre/target_platform/condition_variable_base.h b/platform/freertos/include/chre/target_platform/condition_variable_base.h
new file mode 100644
index 0000000..d290c62
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/condition_variable_base.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_CONDITION_VARIABLE_BASE_H_
+#define CHRE_PLATFORM_FREERTOS_CONDITION_VARIABLE_BASE_H_
+
+#include "chre/platform/mutex.h"
+#include "chre/platform/system_timer.h"
+#include "chre/target_platform/fatal_error.h"
+
+#include "FreeRTOS.h"
+#include "semphr.h"
+
+namespace chre {
+
+/**
+ * @brief FreeRTOS implementation of the condition variable
+ * using FreeRTOS primitives
+ *
+ * The following base class implements the CHRE ConditionVariable
+ * interface for FreeRTOS. Some points to be noted:
+ * - We have the benefit of not needing to support notify_all(),
+ * as we only provide the notify_one() abstraction to common code.
+ * - We don't have the requirement where more than 1 thread can wait
+ * on a given condition variable in parallel (which kind of goes against the
+ * CV name, but really we were looking for something that let us implement
+ * @ref FixedSizeBlockingQueue while keeping familiarity to the C++11 thread
+ * support library
+ * - Currently, there's exactly one user of the condition variable in the common
+ * code, and that's the FixedSizeBlockingQueue used by the EventLoop. Hence,
+ * we can codify assumptions that are well supported by the current code into
+ * the API. One example of this would be ensuring the caller can handle
+ * spurious wakeups (which is fine for CHRE) as the worst side effect of this
+ * is an extra iteration of the while-empty-queue loop.
+ */
+class ConditionVariableBase {
+ public:
+ /**
+ * While in an interrupt context, unblock one thread that is waiting on this
+ * condition variable.
+ */
+ void notify_one_from_isr();
+
+ protected:
+ // Since, per CHRE specification, only one thread is ever
+ // going to be blocked on this condition variable, all we
+ // need is a Binary Semaphore. Note that:
+ // - while std::condition_variable allows for multiple threads
+ // to wait on the same condition variable object
+ // concurrently, the CHRE platform implementation is only required
+ // to allow for a single waiting thread.
+ // - The calling code has to allow for spurious wakeups
+ SemaphoreHandle_t mCvSemaphoreHandle;
+
+ //! Buffer used to store state used by the semaphore.
+ StaticSemaphore_t mSemaphoreBuffer;
+
+ //! The timer used for timed condition variable wait.
+ SystemTimer mTimeoutTimer;
+
+ //! Set to true when the timeout timer is initialized.
+ bool mTimerInitialized = false;
+
+ //! Set to true if the timeout timer timed out.
+ bool mTimedOut = false;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_FREERTOS_CONDITION_VARIABLE_BASE_H_
diff --git a/platform/freertos/include/chre/target_platform/condition_variable_impl.h b/platform/freertos/include/chre/target_platform/condition_variable_impl.h
new file mode 100644
index 0000000..83dadae
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/condition_variable_impl.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_CONDITION_VARIABLE_IMPL_H_
+#define CHRE_PLATFORM_FREERTOS_CONDITION_VARIABLE_IMPL_H_
+
+#include "chre/platform/condition_variable.h"
+
+namespace chre {
+
+inline ConditionVariable::ConditionVariable() {
+ mCvSemaphoreHandle = xSemaphoreCreateBinaryStatic(&mSemaphoreBuffer);
+ if (mCvSemaphoreHandle == NULL) {
+ FATAL_ERROR("failed to create cv semaphore");
+ }
+}
+
+inline ConditionVariable::~ConditionVariable() {
+ if (mCvSemaphoreHandle != NULL) {
+ vSemaphoreDelete(mCvSemaphoreHandle);
+ }
+}
+
+inline void ConditionVariable::notify_one() {
+ xSemaphoreGive(mCvSemaphoreHandle);
+}
+
+inline void ConditionVariable::wait(Mutex &mutex) {
+ mutex.unlock();
+ xSemaphoreTake(mCvSemaphoreHandle, portMAX_DELAY /* xBlockTime */);
+ mutex.lock();
+}
+
+inline bool ConditionVariable::wait_for(Mutex &mutex, Nanoseconds timeout) {
+ if (!mTimerInitialized) {
+ if (!mTimeoutTimer.init()) {
+ FATAL_ERROR("Failed to initialize condition variable timer");
+ } else {
+ mTimerInitialized = true;
+ }
+ }
+
+ // Reset semaphore in case notify_one was invoked twice previously. This can
+ // happen if the timer expires at the same time as the wait condition is met.
+ xQueueReset(mCvSemaphoreHandle);
+ mTimedOut = false;
+
+ auto callback = [](void *data) {
+ auto cbData = static_cast<ConditionVariable *>(data);
+ cbData->mTimedOut = true;
+ cbData->notify_one();
+ };
+ if (!mTimeoutTimer.set(callback, this, timeout)) {
+ LOGE("Failed to set condition variable timer");
+ }
+
+ wait(mutex);
+ if (mTimeoutTimer.isActive()) {
+ if (!mTimeoutTimer.cancel()) {
+ LOGD("Failed to cancel condition variable timer");
+ }
+ }
+ return !mTimedOut;
+}
+
+inline void ConditionVariableBase::notify_one_from_isr() {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xSemaphoreGiveFromISR(mCvSemaphoreHandle, &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+}
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_FREERTOS_CONDITION_VARIABLE_IMPL_H_
diff --git a/platform/freertos/include/chre/target_platform/init.h b/platform/freertos/include/chre/target_platform/init.h
new file mode 100644
index 0000000..5eb90c9
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/init.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_INIT_H_
+#define CHRE_PLATFORM_FREERTOS_INIT_H_
+
+#include "FreeRTOS.h"
+
+namespace chre {
+namespace freertos {
+
+/**
+ * This init function spawns a (non-privileged) FreeRTOS task that
+ * initializes the CHRE core, loads any static nanoapps, and starts
+ * the CHRE event loop.
+ * The task attribute constants are located in the corresponding init.cc
+ * source file, in case they need to be altered. The defaults chosen are:
+ * - Task Stack Depth: 8K Words
+ * - Task Priority: 1 (idle_task + 1)
+ *
+ * @return pdPASS on success, a FreeRTOS error code otherwise.
+ */
+BaseType_t init();
+
+/**
+ * Delete the task spawned in the init function
+ */
+void deinit();
+
+const char *getChreTaskName();
+
+} // namespace freertos
+} // namespace chre
+
+#endif // CHRE_PLATFORM_FREERTOS_INIT_H_
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 100644
index 0000000..d26170a
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/mutex_base.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_MUTEX_BASE_H_
+#define CHRE_PLATFORM_FREERTOS_MUTEX_BASE_H_
+
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+
+#include "FreeRTOS.h"
+#include "semphr.h"
+
+namespace chre {
+
+/**
+ * The FreeRTOS implementation of MutexBase
+ */
+class MutexBase {
+ protected:
+ SemaphoreHandle_t mSemaphoreHandle;
+
+ /**
+ * Initialize the mutex handle using a static semaphore
+ * to avoid heap allocations
+ */
+ void initStaticMutex() {
+ mSemaphoreHandle = xSemaphoreCreateMutexStatic(&mStaticSemaphore);
+ if (mSemaphoreHandle == NULL) {
+ FATAL_ERROR("Failed to initialize mutex");
+ }
+ }
+
+ private:
+ StaticSemaphore_t mStaticSemaphore;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_FREERTOS_MUTEX_BASE_H_
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 100644
index 0000000..23e957d
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/mutex_base_impl.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_MUTEX_BASE_IMPL_H_
+#define CHRE_PLATFORM_FREERTOS_MUTEX_BASE_IMPL_H_
+
+#include "chre/platform/mutex.h"
+
+namespace chre {
+
+inline Mutex::Mutex() {
+ initStaticMutex();
+}
+
+inline Mutex::~Mutex() {
+ if (mSemaphoreHandle) {
+ vSemaphoreDelete(mSemaphoreHandle);
+ }
+}
+
+inline void Mutex::lock() {
+ TickType_t blockForever = portMAX_DELAY;
+ if (pdTRUE != xSemaphoreTake(mSemaphoreHandle, blockForever)) {
+ LOGE("Failed to lock mutex");
+ }
+}
+
+inline bool Mutex::try_lock() {
+ TickType_t doNotBlock = static_cast<TickType_t>(0);
+ BaseType_t rv = xSemaphoreTake(mSemaphoreHandle, doNotBlock);
+
+ return (rv == pdTRUE) ? true : false;
+}
+
+inline void Mutex::unlock() {
+ if (pdTRUE != xSemaphoreGive(mSemaphoreHandle)) {
+ LOGE("Failed to properly unlock mutex!");
+ }
+}
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_FREERTOS_MUTEX_BASE_IMPL_H_
diff --git a/platform/freertos/include/chre/target_platform/platform_debug_dump_manager_base.h b/platform/freertos/include/chre/target_platform/platform_debug_dump_manager_base.h
new file mode 100644
index 0000000..5c2399c
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/platform_debug_dump_manager_base.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_PLATFORM_DEBUG_DUMP_MANAGER_BASE_H_
+#define CHRE_PLATFORM_FREERTOS_PLATFORM_DEBUG_DUMP_MANAGER_BASE_H_
+
+#include <cstdbool>
+#include <cstddef>
+#include <cstdint>
+
+namespace chre {
+
+/**
+ * FreeRTOS-specific debug dump functionality.
+ */
+class PlatformDebugDumpManagerBase {
+ public:
+ /**
+ * To be called when receiving a debug dump request from host.
+ *
+ * @param hostClientId The host client ID that requested the debug dump.
+ */
+ void onDebugDumpRequested(uint16_t hostClientId);
+
+ /**
+ * @see PlatformDebugDumpManager::sendDebugDump
+ */
+ void sendDebugDumpResult(const char *debugStr, size_t debugStrSize,
+ bool complete);
+
+ protected:
+ //! Host client ID that triggered the debug dump process.
+ uint16_t mHostClientId = 0;
+
+ //! Number of times sendDebugDumpToHost called with debugStrSize > 0.
+ uint32_t mDataCount = 0;
+
+ //! Whenther the last debug dump session has been marked complete.
+ bool mComplete = true;
+
+ static constexpr size_t kDebugDumpStrMaxSize = CHRE_MESSAGE_TO_HOST_MAX_SIZE;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SLPI_PLATFORM_DEBUG_DUMP_MANAGER_BASE_H_
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 100644
index 0000000..42541e0
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/platform_nanoapp_base.h
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_PLATFORM_NANOAPP_BASE_H_
+#define CHRE_PLATFORM_FREERTOS_PLATFORM_NANOAPP_BASE_H_
+
+#include "chre/platform/shared/memory.h"
+#include "chre/platform/shared/nanoapp_support_lib_dso.h"
+
+namespace chre {
+
+/**
+ * FREERTOS-specific nanoapp functionality.
+ */
+class PlatformNanoappBase {
+ public:
+ /**
+ * Associate this Nanoapp instance with a nanoapp that is statically built
+ * into the CHRE binary with the given app info structure.
+ */
+ void loadStatic(const struct chreNslNanoappInfo *appInfo);
+
+ /**
+ * @return true if the app's binary data is resident in memory or if the app's
+ * filename is saved, i.e. all binary fragments are loaded through
+ * copyNanoappFragment, loadFromFile/loadStatic() was successful
+ */
+ bool isLoaded() const;
+
+ /**
+ * @return true if this app is loaded into TCM.
+ */
+ bool isTcmApp() const;
+
+ /**
+ * Sets app info that will be used later when the app is loaded into the
+ * system.
+ *
+ * @param appId The unique app identifier associated with this binary
+ * @param appVersion An application-defined version number
+ * @param appFilename The filename of the app that should be loaded from disk
+ *
+ * @return true if the info was successfully stored
+ */
+ bool setAppInfo(uint64_t appId, uint32_t appVersion, const char *appFilename);
+
+ /**
+ * Reserves buffer space for a nanoapp's binary. This method should be called
+ * before copyNanoappFragment is called.
+ *
+ * @param appId The unique app identifier associated with this binary
+ * @param appVersion An application-defined version number
+ * @param appFlags The flags provided by the app being loaded
+ * @param appBinaryLen Size of appBinary, in bytes
+ *
+ * @return true if the allocation was successful, false otherwise
+ */
+ bool reserveBuffer(uint64_t appId, uint32_t appVersion, uint32_t appFlags,
+ size_t appBinaryLen);
+
+ /**
+ * Copies the (possibly fragmented) application binary data into the allocated
+ * buffer, and updates the pointer to the next address to write into. The
+ * application may be invalid - full checking and initialization happens just
+ * before invoking start() nanoapp entry point.
+ *
+ * @param buffer The pointer to the buffer
+ * @param bufferSize The size of the buffer in bytes
+ *
+ * @return true if the reserved buffer did not overflow, false otherwise
+ */
+ bool copyNanoappFragment(const void *buffer, size_t bufferSize);
+
+ protected:
+ //! The app ID we received in the metadata alongside the nanoapp binary. This
+ //! is also included in (and checked against) mAppInfo.
+ uint64_t mExpectedAppId;
+
+ //! The application-defined version number we received in the metadata
+ //! alongside the nanoapp binary. This is also included in (and checked
+ //! against) mAppInfo.
+ uint32_t mExpectedAppVersion = 0;
+
+ //! Whether the nanoapp is expected to be loaded into TCM.
+ bool mExpectedTcmCapable = false;
+
+ //! Whether this nanoapp is loaded into TCM.
+ bool mIsTcmNanoapp = false;
+
+ //! Buffer containing the complete DSO binary - only populated if
+ //! copyNanoappFragment() was used to load this nanoapp
+ void *mAppBinary = nullptr;
+ size_t mAppBinaryLen = 0;
+
+ //! Null-terminated ASCII string containing the file name that contains the
+ //! app binary to be loaded. This is used over mAppBinary to load the nanoapp
+ //! if set.
+ char *mAppFilename = nullptr;
+
+ //! The dynamic shared object (DSO) handle returned by dlopenbuf()
+ void *mDsoHandle = nullptr;
+
+ //! Pointer to the app info structure within this nanoapp
+ const struct chreNslNanoappInfo *mAppInfo = nullptr;
+
+ //! Pointer containing the unstable ID section for this nanoapp
+ const char *mAppUnstableId = nullptr;
+
+ //! Set to true if this app is built into the CHRE binary, and was loaded via
+ //! loadStatic(). In this case, the member variables above are not valid or
+ //! applicable.
+ bool mIsStatic = false;
+
+ //! The number of bytes of the binary that has been loaded so far.
+ size_t mBytesLoaded = 0;
+
+ /**
+ * Loads the nanoapp symbols from the currently loaded binary and verifies
+ * they match the expected information the nanoapp should have.
+ *
+ * @return true if the app info structure passed validation.
+ */
+ bool verifyNanoappInfo();
+
+ /**
+ * Calls through to openNanoappFromBuffer or openNanoappFromFile, depending on
+ * how this nanoapp was loaded.
+ */
+ bool openNanoapp();
+
+ /**
+ * Releases the DSO handle if it was active, by calling dlclose(). This will
+ * result in execution of any unload handlers in the nanoapp.
+ */
+ void closeNanoapp();
+
+ /**
+ * Retrieves the nanoapp's version string. This is intended to be a human
+ * readable version string to aid in debugging. This must always return a
+ * valid string so if none is available it is recommended to return
+ * "<undefined>" or similar.
+ *
+ * @param length The length of the returned version string
+ * @return A char array containing the version string for this nanoapp.
+ */
+ const char *getAppVersionString(size_t *length) const;
+
+ /** If this app needs to access DRAM to function, enables DRAM access. */
+ inline void enableDramAccessIfRequired() const {
+ if (!isTcmApp()) {
+ forceDramAccess();
+ }
+ }
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_FREERTOS_PLATFORM_NANOAPP_BASE_H_
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 100644
index 0000000..75d3531
--- /dev/null
+++ b/platform/freertos/include/chre/target_platform/static_nanoapp_init.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_FREERTOS_STATIC_NANOAPP_INIT_H_
+#define CHRE_PLATFORM_FREERTOS_STATIC_NANOAPP_INIT_H_
+
+#include "chre/core/nanoapp.h"
+#include "chre/platform/fatal_error.h"
+#include "chre/util/unique_ptr.h"
+
+#define CHRE_STATIC_NANOAPP_INIT(appName, appId_, appVersion_) \
+ namespace chre { \
+ \
+ UniquePtr<Nanoapp> initializeStaticNanoapp##appName() { \
+ UniquePtr<Nanoapp> nanoapp = MakeUnique<Nanoapp>(); \
+ static struct chreNslNanoappInfo appInfo; \
+ appInfo.magic = CHRE_NSL_NANOAPP_INFO_MAGIC; \
+ appInfo.structMinorVersion = CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION; \
+ appInfo.targetApiVersion = CHRE_API_VERSION; \
+ appInfo.vendor = "Google"; /* TODO: make this configurable */ \
+ appInfo.name = #appName; \
+ appInfo.isSystemNanoapp = true; \
+ appInfo.isTcmNanoapp = false; \
+ appInfo.appId = appId_; \
+ appInfo.appVersion = appVersion_; \
+ appInfo.entryPoints.start = nanoappStart; \
+ appInfo.entryPoints.handleEvent = nanoappHandleEvent; \
+ appInfo.entryPoints.end = nanoappEnd; \
+ appInfo.appVersionString = "<undefined>"; \
+ if (nanoapp.isNull()) { \
+ FATAL_ERROR("Failed to allocate nanoapp " #appName); \
+ } else { \
+ nanoapp->loadStatic(&appInfo); \
+ } \
+ \
+ return nanoapp; \
+ } \
+ } /* namespace chre */
+
+#endif // CHRE_PLATFORM_FREERTOS_NANOAPP_INIT_H_
diff --git a/platform/freertos/init.cc b/platform/freertos/init.cc
new file mode 100644
index 0000000..c1cd445
--- /dev/null
+++ b/platform/freertos/init.cc
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#include "chre/core/init.h"
+
+#include "chpp/platform/chpp_init.h"
+#include "chre/core/event_loop_manager.h"
+#include "chre/core/static_nanoapps.h"
+#include "chre/platform/shared/dram_vote_client.h"
+#include "chre/target_platform/init.h"
+
+#include "task.h"
+
+namespace chre {
+namespace freertos {
+namespace {
+
+constexpr configSTACK_DEPTH_TYPE kChreTaskStackDepthWords = 0x800;
+
+constexpr UBaseType_t kChreTaskPriority = tskIDLE_PRIORITY + 1;
+
+TaskHandle_t gChreTaskHandle;
+
+// This function is intended to be the task action function for FreeRTOS.
+// It Initializes CHRE, runs the event loop, and only exits if it receives
+// a message to shutdown. Note that depending on the hardware platform this
+// runs on, CHRE might create additional threads, which are cleaned up when
+// CHRE exits.
+void chreThreadEntry(void *context) {
+ DramVoteClientSingleton::init();
+
+ chre::init();
+ chre::EventLoopManagerSingleton::get()->lateInit();
+ chre::loadStaticNanoapps();
+
+ chre::EventLoopManagerSingleton::get()->getEventLoop().run();
+
+ // we only get here if the CHRE EventLoop exited
+ chre::deinit();
+
+ DramVoteClientSingleton::deinit();
+
+ vTaskDelete(nullptr);
+ gChreTaskHandle = nullptr;
+}
+
+} // namespace
+
+BaseType_t init() {
+ BaseType_t rc = xTaskCreate(chreThreadEntry, freertos::getChreTaskName(),
+ kChreTaskStackDepthWords, nullptr /* args */,
+ kChreTaskPriority, &gChreTaskHandle);
+ CHRE_ASSERT(rc == pdPASS);
+
+ chpp::init();
+
+ return rc;
+}
+
+void deinit() {
+ // On a deinit call, we just stop the CHRE event loop. This causes the 'run'
+ // method in the task function exit, and move on to handle task cleanup
+ if (gChreTaskHandle != nullptr) {
+ chre::EventLoopManagerSingleton::get()->getEventLoop().stop();
+ }
+
+ chpp::deinit();
+}
+
+const char *getChreTaskName() {
+ static constexpr char kChreTaskName[] = "CHRE";
+ return kChreTaskName;
+}
+
+} // namespace freertos
+} // namespace chre
diff --git a/platform/freertos/memory.cc b/platform/freertos/memory.cc
new file mode 100644
index 0000000..ada6702
--- /dev/null
+++ b/platform/freertos/memory.cc
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/memory.h"
+#include "chre/platform/shared/pal_system_api.h"
+
+#include <cstdlib>
+
+namespace chre {
+
+void *memoryAlloc(size_t size) {
+ return malloc(size);
+}
+
+void *palSystemApiMemoryAlloc(size_t size) {
+ return malloc(size);
+}
+
+void memoryFree(void *pointer) {
+ free(pointer);
+}
+
+void palSystemApiMemoryFree(void *pointer) {
+ free(pointer);
+}
+
+} // namespace chre
diff --git a/platform/freertos/memory_manager.cc b/platform/freertos/memory_manager.cc
new file mode 100644
index 0000000..d3e5b10
--- /dev/null
+++ b/platform/freertos/memory_manager.cc
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/memory_manager.h"
+#include "chre/platform/shared/memory.h"
+#include "chre/util/memory.h"
+
+namespace chre {
+
+void *MemoryManager::doAlloc(Nanoapp *app, uint32_t bytes) {
+ if (app->isTcmApp()) {
+ return chre::memoryAlloc(bytes);
+ } else {
+ return chre::memoryAllocDram(bytes);
+ }
+}
+
+void MemoryManager::doFree(Nanoapp *app, void *ptr) {
+ if (app->isTcmApp()) {
+ chre::memoryFree(ptr);
+ } else {
+ chre::memoryFreeDram(ptr);
+ }
+}
+
+} // namespace chre
diff --git a/platform/freertos/platform_debug_dump_manager.cc b/platform/freertos/platform_debug_dump_manager.cc
new file mode 100644
index 0000000..056817e
--- /dev/null
+++ b/platform/freertos/platform_debug_dump_manager.cc
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/platform_debug_dump_manager.h"
+
+#include "chre/core/event_loop_manager.h"
+#include "chre/platform/log.h"
+#include "chre/target_platform/host_link_base.h"
+#include "chre/target_platform/platform_debug_dump_manager_base.h"
+
+#include <cstring>
+
+namespace chre {
+
+void PlatformDebugDumpManager::sendDebugDump(const char *debugStr,
+ bool complete) {
+ // DDM is guaranteed to call complete=true at the end of a debug dump session.
+ // However, sendDebugDumpResult may not get called with complete=true.
+ // Therefore, mDataCount has to be reset here instead of in
+ // sendDebugDumpResult(), to properly start the next debug dump session.
+ if (mComplete) {
+ mDataCount = 0;
+ }
+ mComplete = complete;
+
+ sendDebugDumpResult(debugStr, strlen(debugStr), complete);
+}
+
+void PlatformDebugDumpManagerBase::onDebugDumpRequested(uint16_t hostClientId) {
+ mHostClientId = hostClientId;
+
+ EventLoopManagerSingleton::get()->getDebugDumpManager().trigger();
+}
+
+void PlatformDebugDumpManagerBase::sendDebugDumpResult(const char *debugStr,
+ size_t debugStrSize,
+ bool complete) {
+ if (debugStrSize > 0) {
+ mDataCount++;
+ }
+ sendDebugDumpResultToHost(mHostClientId, debugStr, debugStrSize, complete,
+ mDataCount);
+}
+
+} // namespace chre
diff --git a/platform/freertos/platform_nanoapp.cc b/platform/freertos/platform_nanoapp.cc
new file mode 100644
index 0000000..d2de853
--- /dev/null
+++ b/platform/freertos/platform_nanoapp.cc
@@ -0,0 +1,301 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/platform_nanoapp.h"
+
+#include <dlfcn.h>
+#include <cinttypes>
+
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+#include "chre/platform/shared/authentication.h"
+#include "chre/platform/shared/nanoapp_dso_util.h"
+#include "chre/platform/shared/nanoapp_loader.h"
+#include "chre/util/macros.h"
+#include "chre/util/system/napp_header_utils.h"
+#include "chre_api/chre/version.h"
+
+namespace chre {
+namespace {
+
+const char kDefaultAppVersionString[] = "<undefined>";
+size_t kDefaultAppVersionStringSize = ARRAY_SIZE(kDefaultAppVersionString);
+
+} // namespace
+
+PlatformNanoapp::~PlatformNanoapp() {
+ closeNanoapp();
+
+ if (mAppBinary != nullptr) {
+ forceDramAccess();
+ memoryFreeDram(mAppBinary);
+ }
+}
+
+bool PlatformNanoapp::start() {
+ //! Always force DRAM access when starting since nanoapps are loaded via DRAM.
+ forceDramAccess();
+
+ bool success = false;
+ if (!openNanoapp()) {
+ LOGE("Failed to open nanoapp");
+ } else if (mAppInfo == nullptr) {
+ LOGE("Null app info!");
+ } else {
+ success = mAppInfo->entryPoints.start();
+ }
+
+ return success;
+}
+
+void PlatformNanoapp::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) {
+ enableDramAccessIfRequired();
+ mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
+}
+
+void PlatformNanoapp::end() {
+ enableDramAccessIfRequired();
+ mAppInfo->entryPoints.end();
+ closeNanoapp();
+}
+
+uint64_t PlatformNanoapp::getAppId() const {
+ // TODO (karthikmb/stange): Ideally, we should store the metadata as
+ // variables in TCM, to avoid bumping into DRAM for basic queries.
+ enableDramAccessIfRequired();
+ return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
+}
+
+uint32_t PlatformNanoapp::getAppVersion() const {
+ enableDramAccessIfRequired();
+ return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
+}
+
+const char *PlatformNanoapp::getAppName() const {
+ enableDramAccessIfRequired();
+ return (mAppInfo != nullptr) ? mAppInfo->name : "Unknown";
+}
+
+uint32_t PlatformNanoapp::getTargetApiVersion() const {
+ enableDramAccessIfRequired();
+ return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion : 0;
+}
+
+bool PlatformNanoapp::isSystemNanoapp() const {
+ enableDramAccessIfRequired();
+ return (mAppInfo != nullptr && mAppInfo->isSystemNanoapp);
+}
+
+void PlatformNanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
+ if (mAppInfo != nullptr) {
+ size_t versionLen = 0;
+ const char *version = getAppVersionString(&versionLen);
+ debugDump.print("%s (%s) @ build: %.*s", mAppInfo->name, mAppInfo->vendor,
+ versionLen, version);
+ }
+}
+
+const char *PlatformNanoappBase::getAppVersionString(size_t *length) const {
+ const char *versionString = kDefaultAppVersionString;
+ *length = kDefaultAppVersionStringSize;
+
+ size_t endOffset = 0;
+ if (mAppUnstableId != nullptr) {
+ size_t appVersionStringLength = strlen(mAppUnstableId);
+
+ //! The unstable ID is expected to be in the format of
+ //! <descriptor>=<build ID>@<more descriptors>. Use this expected layout
+ //! knowledge to parse the string and only return the build ID portion that
+ //! should be printed.
+ size_t startOffset = SIZE_MAX;
+ for (size_t i = 0; i < appVersionStringLength; i++) {
+ size_t offset = i + 1;
+ if (startOffset == SIZE_MAX && mAppUnstableId[i] == '=' &&
+ offset < appVersionStringLength) {
+ startOffset = offset;
+ }
+
+ if (mAppUnstableId[i] == '@') {
+ endOffset = i;
+ break;
+ }
+ }
+
+ if (startOffset < endOffset) {
+ versionString = &mAppUnstableId[startOffset];
+ *length = endOffset - startOffset;
+ }
+ }
+
+ return versionString;
+}
+
+bool PlatformNanoappBase::isLoaded() const {
+ return (mIsStatic ||
+ (mAppBinary != nullptr && mBytesLoaded == mAppBinaryLen) ||
+ mDsoHandle != nullptr || mAppFilename != nullptr);
+}
+
+bool PlatformNanoappBase::isTcmApp() const {
+ return mIsTcmNanoapp;
+}
+
+void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
+ CHRE_ASSERT(!isLoaded());
+ mIsStatic = true;
+ mAppInfo = appInfo;
+}
+
+bool PlatformNanoappBase::reserveBuffer(uint64_t appId, uint32_t appVersion,
+ uint32_t appFlags,
+ size_t appBinaryLen) {
+ CHRE_ASSERT(!isLoaded());
+
+ forceDramAccess();
+
+ bool success = false;
+ mAppBinary = memoryAllocDram(appBinaryLen);
+
+ bool isSigned = IS_BIT_SET(appFlags, CHRE_NAPP_HEADER_SIGNED);
+ if (!isSigned) {
+ LOGE("Unable to load unsigned nanoapps");
+ } else if (mAppBinary == nullptr) {
+ LOG_OOM();
+ } else {
+ bool tcmCapable = IS_BIT_SET(appFlags, CHRE_NAPP_HEADER_TCM_CAPABLE);
+ mExpectedAppId = appId;
+ mExpectedAppVersion = appVersion;
+ mExpectedTcmCapable = tcmCapable;
+ mAppBinaryLen = appBinaryLen;
+ success = true;
+ }
+
+ return success;
+}
+
+bool PlatformNanoappBase::copyNanoappFragment(const void *buffer,
+ size_t bufferLen) {
+ CHRE_ASSERT(!isLoaded());
+
+ forceDramAccess();
+
+ bool success = true;
+
+ if ((mBytesLoaded + bufferLen) > mAppBinaryLen) {
+ LOGE("Overflow: cannot load %zu bytes to %zu/%zu nanoapp binary buffer",
+ bufferLen, mBytesLoaded, mAppBinaryLen);
+ success = false;
+ } else {
+ uint8_t *binaryBuffer = static_cast<uint8_t *>(mAppBinary) + mBytesLoaded;
+ memcpy(binaryBuffer, buffer, bufferLen);
+ mBytesLoaded += bufferLen;
+ }
+
+ return success;
+}
+
+bool PlatformNanoappBase::verifyNanoappInfo() {
+ bool success = false;
+
+ if (mDsoHandle == nullptr) {
+ LOGE("No nanoapp info to verify");
+ } else {
+ mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
+ dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
+ mAppUnstableId = static_cast<const char *>(
+ dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_UNSTABLE_ID_SYMBOL_NAME));
+ if (mAppInfo == nullptr) {
+ LOGE("Failed to find app info symbol");
+ } else if (mAppUnstableId == nullptr) {
+ LOGE("Failed to find unstable ID symbol");
+ } else {
+ success = validateAppInfo(mExpectedAppId, mExpectedAppVersion, mAppInfo);
+ if (success && mAppInfo->isTcmNanoapp != mExpectedTcmCapable) {
+ success = false;
+ LOGE("Expected TCM nanoapp %d found %d", mExpectedTcmCapable,
+ mAppInfo->isTcmNanoapp);
+ }
+
+ if (!success) {
+ mAppInfo = nullptr;
+ } else {
+ LOGI("Successfully loaded nanoapp: %s (0x%016" PRIx64
+ ") version 0x%" PRIx32 " uimg %d system %d",
+ mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion,
+ mAppInfo->isTcmNanoapp, mAppInfo->isSystemNanoapp);
+ }
+ }
+ }
+ return success;
+}
+
+bool PlatformNanoappBase::openNanoapp() {
+ bool success = false;
+ if (mIsStatic) {
+ success = true;
+ } else if (mAppBinary != nullptr) {
+ //! The true start of the binary will be after the authentication header.
+ //! Use the returned value from authenticateBinary to ensure dlopenbuf has
+ //! the starting address to a valid ELF.
+ void *binaryStart = mAppBinary;
+
+ // TODO(158770259): Enforce this and make the log message and error once all
+ // nanoapp binaries are signed.
+ if (!authenticateBinary(mAppBinary, &binaryStart)) {
+ LOGV("Unable to authenticate 0x%" PRIx32 " not stopping loading for now",
+ mExpectedAppId);
+ }
+ if (mDsoHandle == nullptr) {
+ mDsoHandle = dlopenbuf(binaryStart, mExpectedTcmCapable);
+ success = verifyNanoappInfo();
+ } else {
+ LOGE("Trying to reopen an existing buffer");
+ }
+ }
+
+ if (!success) {
+ closeNanoapp();
+ }
+
+ if (mAppBinary != nullptr) {
+ memoryFreeDram(mAppBinary);
+ mAppBinary = nullptr;
+ }
+
+ // Save this flag locally since it may be referenced while the system is in
+ // TCM-only mode.
+ if (mAppInfo != nullptr) {
+ mIsTcmNanoapp = mAppInfo->isTcmNanoapp;
+ }
+
+ return success;
+}
+
+void PlatformNanoappBase::closeNanoapp() {
+ if (mDsoHandle != nullptr) {
+ // Force DRAM access since dl* functions are only safe to call with DRAM
+ // available.
+ forceDramAccess();
+ mAppInfo = nullptr;
+ if (dlclose(mDsoHandle) != 0) {
+ LOGE("dlclose failed");
+ }
+ mDsoHandle = nullptr;
+ }
+}
+
+} // namespace chre
diff --git a/platform/platform.mk b/platform/platform.mk
index e170fa8..19467b7 100644
--- a/platform/platform.mk
+++ b/platform/platform.mk
@@ -26,6 +26,9 @@
SLPI_CFLAGS += -I$(SLPI_PREFIX)/platform/inc/a1std
SLPI_CFLAGS += -I$(SLPI_PREFIX)/platform/inc/stddef
SLPI_CFLAGS += -I$(SLPI_PREFIX)/platform/rtld/inc
+SLPI_CFLAGS += -I$(SLPI_PREFIX)/ssc/goog/api
+SLPI_CFLAGS += -I$(SLPI_PREFIX)/ssc/inc
+SLPI_CFLAGS += -I$(SLPI_PREFIX)/ssc/inc/internal
SLPI_CFLAGS += -Iplatform/shared/include
SLPI_CFLAGS += -Iplatform/slpi/include
@@ -33,6 +36,10 @@
# We use FlatBuffers in the SLPI platform layer
SLPI_CFLAGS += $(FLATBUFFERS_CFLAGS)
+# Needed to define __SIZEOF_ATTR_THREAD in sns_osa_thread.h, included in
+# sns_memmgr.h.
+SLPI_CFLAGS += -DSSC_TARGET_HEXAGON
+
ifneq ($(CHRE_ENABLE_ACCEL_CAL), false)
SLPI_CFLAGS += -DCHRE_ENABLE_ACCEL_CAL
endif
@@ -49,19 +56,12 @@
SLPI_SEE_CFLAGS += -I$(SLPI_PREFIX)/qmimsgs/common/api
SLPI_SEE_CFLAGS += -I$(SLPI_PREFIX)/ssc_api/pb
SLPI_SEE_CFLAGS += -I$(SLPI_PREFIX)/ssc/framework/cm/inc
-SLPI_SEE_CFLAGS += -I$(SLPI_PREFIX)/ssc/goog/api
-SLPI_SEE_CFLAGS += -I$(SLPI_PREFIX)/ssc/inc
-SLPI_SEE_CFLAGS += -I$(SLPI_PREFIX)/ssc/inc/internal
SLPI_SEE_CFLAGS += -I$(SLPI_PREFIX)/ssc/inc/utils/nanopb
SLPI_SEE_CFLAGS += -Iplatform/slpi/see/include
SLPI_SEE_CFLAGS += -DCHRE_SLPI_SEE
-# Needed to define __SIZEOF_ATTR_THREAD in sns_osa_thread.h, included in
-# sns_memmgr.h.
-SLPI_SEE_CFLAGS += -DSSC_TARGET_HEXAGON
-
# Defined in slpi_proc/ssc/build/ssc.scons
SLPI_SEE_CFLAGS += -DPB_FIELD_16BIT
@@ -153,6 +153,140 @@
SLPI_SEE_SRCS += platform/slpi/see/island_vote_client.cc
endif
+# FreeRTOS-specific Source Files ###############################################
+
+FREERTOS_SRCS += platform/freertos/context.cc
+FREERTOS_SRCS += platform/freertos/init.cc
+FREERTOS_SRCS += platform/freertos/memory_manager.cc
+FREERTOS_SRCS += platform/freertos/platform_debug_dump_manager.cc
+FREERTOS_SRCS += platform/freertos/platform_nanoapp.cc
+
+# Optional FreeRTOS-specific Source Files ######################################
+
+# memory.cc is only needed if the given platform doesn't have its own memory
+# allocation setup.
+FREERTOS_OPTIONAL_SRCS += platform/freertos/memory.cc
+
+# FreeRTOS-specific Compiler Flags #############################################
+FREERTOS_CFLAGS += -I$(AOC_TOP_DIR)/external/FreeRTOS/FreeRTOS/Source/include
+FREERTOS_CFLAGS += -Iplatform/freertos/include
+FREERTOS_CFLAGS += -Iplatform/shared/include
+
+# AoC-specific Source Files ####################################################
+
+AOC_SRCS += chpp/transport.c
+AOC_SRCS += chpp/app.c
+AOC_SRCS += chpp/services.c
+AOC_SRCS += chpp/services/discovery.c
+AOC_SRCS += chpp/services/loopback.c
+AOC_SRCS += chpp/services/nonhandle.c
+AOC_SRCS += chpp/clients.c
+AOC_SRCS += chpp/clients/discovery.c
+AOC_SRCS += chpp/clients/loopback.c
+AOC_SRCS += chpp/platform/pal_api.c
+AOC_SRCS += chpp/platform/aoc/condition_variable.cc
+AOC_SRCS += chpp/platform/aoc/link.cc
+AOC_SRCS += chpp/platform/aoc/chpp_init.cc
+AOC_SRCS += chpp/platform/aoc/chpp_task_util.cc
+AOC_SRCS += chpp/platform/aoc/chpp_uart_link_manager.cc
+AOC_SRCS += chpp/platform/aoc/memory.cc
+AOC_SRCS += chpp/platform/aoc/notifier.cc
+AOC_SRCS += chpp/platform/aoc/time.cc
+AOC_SRCS += platform/aoc/audio_controller.cc
+AOC_SRCS += platform/aoc/audio_filter.cc
+AOC_SRCS += platform/aoc/chre_api_re.cc
+AOC_SRCS += platform/aoc/dram_vote_client.cc
+AOC_SRCS += platform/aoc/fatal_error.cc
+AOC_SRCS += platform/aoc/host_link.cc
+AOC_SRCS += platform/aoc/log.cc
+AOC_SRCS += platform/aoc/memory.cc
+AOC_SRCS += platform/aoc/power_control_manager.cc
+AOC_SRCS += platform/aoc/platform_audio.cc
+AOC_SRCS += platform/aoc/platform_cache_management.cc
+AOC_SRCS += platform/aoc/platform_pal.cc
+AOC_SRCS += platform/aoc/system_time.cc
+AOC_SRCS += platform/aoc/system_timer.cc
+AOC_SRCS += platform/shared/chre_api_audio.cc
+AOC_SRCS += platform/shared/chre_api_core.cc
+AOC_SRCS += platform/shared/chre_api_gnss.cc
+AOC_SRCS += platform/shared/chre_api_re.cc
+AOC_SRCS += platform/shared/chre_api_sensor.cc
+AOC_SRCS += platform/shared/chre_api_version.cc
+AOC_SRCS += platform/shared/chre_api_wifi.cc
+AOC_SRCS += platform/shared/chre_api_wwan.cc
+AOC_SRCS += platform/shared/dlfcn.cc
+AOC_SRCS += platform/shared/dram_vote_client.cc
+AOC_SRCS += platform/shared/host_protocol_chre.cc
+AOC_SRCS += platform/shared/host_protocol_common.cc
+AOC_SRCS += platform/shared/memory_manager.cc
+AOC_SRCS += platform/shared/nanoapp_loader.cc
+AOC_SRCS += platform/shared/nanoapp_load_manager.cc
+AOC_SRCS += platform/shared/pal_system_api.cc
+AOC_SRCS += platform/shared/pal_sensor_stub.cc
+AOC_SRCS += platform/shared/system_time.cc
+AOC_SRCS += platform/shared/nanoapp/nanoapp_dso_util.cc
+AOC_SRCS += platform/usf/platform_sensor.cc
+AOC_SRCS += platform/usf/platform_sensor_manager.cc
+AOC_SRCS += platform/usf/platform_sensor_type_helpers.cc
+AOC_SRCS += platform/usf/usf_helper.cc
+
+# Optional GNSS support.
+ifeq ($(CHRE_GNSS_SUPPORT_ENABLED), true)
+AOC_SRCS += platform/shared/platform_gnss.cc
+AOC_SRCS += chpp/clients/gnss.c
+AOC_CFLAGS += -DCHPP_CLIENT_ENABLED_CHRE_GNSS
+endif
+
+# Optional Wi-Fi support.
+ifeq ($(CHRE_WIFI_SUPPORT_ENABLED), true)
+AOC_SRCS += platform/shared/platform_wifi.cc
+AOC_SRCS += chpp/clients/wifi.c
+AOC_CFLAGS += -DCHPP_CLIENT_ENABLED_CHRE_WIFI
+endif
+
+# Optional WWAN support.
+ifeq ($(CHRE_WWAN_SUPPORT_ENABLED), true)
+AOC_SRCS += platform/shared/platform_wwan.cc
+AOC_SRCS += chpp/clients/wwan.c
+AOC_CFLAGS += -DCHPP_CLIENT_ENABLED_CHRE_WWAN
+endif
+
+# AoC-specific Compiler Flags ##################################################
+AOC_CFLAGS += -Iplatform/aoc/include
+AOC_CFLAGS += -Iplatform/usf/include
+AOC_CFLAGS += -I$(AOC_AUTOGEN_DIR)/regions
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/apps
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/core/arm/generic/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/drivers/gpio/aoc/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/drivers/gpio/base/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/drivers/gpi/aoc/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/drivers/gpi/base/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/drivers/processor
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/drivers/processor/aoc/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/drivers/sys_mem/base/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/efw/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/filters/audio/common/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/libs/common/heap/common/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/os/common/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/platform/common/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/AOC/product/$(AOC_PRODUCT_FAMILY)/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/usf/core/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/usf/core/fbs
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/usf/pal/include
+AOC_CFLAGS += -I$(AOC_TOP_DIR)/usf/pal/efw/include
+
+AOC_CFLAGS += -Ichpp/include
+AOC_CFLAGS += -Ichpp/platform/aoc/include
+AOC_CFLAGS += -DCHPP_CLIENT_ENABLED_DISCOVERY
+
+# We use FlatBuffers in the AOC platform layer
+AOC_CFLAGS += $(FLATBUFFERS_CFLAGS)
+
+# Ensure USF uses its own flatbuffers header. This is needed while USF migrates
+# away from CHRE's header.
+AOC_CFLAGS += -DFLATBUFFERS_USF
+
# Simulator-specific Compiler Flags ############################################
SIM_CFLAGS += -Iplatform/shared/include
diff --git a/platform/shared/dlfcn.cc b/platform/shared/dlfcn.cc
new file mode 100644
index 0000000..630b73c
--- /dev/null
+++ b/platform/shared/dlfcn.cc
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+
+#include "chre/platform/assert.h"
+#include "chre/platform/shared/nanoapp_loader.h"
+#include "chre/util/unique_ptr.h"
+
+void *dlopenbuf(void *elfBinary, bool mapIntoTcm) {
+ return chre::NanoappLoader::create(elfBinary, mapIntoTcm);
+}
+
+void *dlsym(void *handle, const char *symbol) {
+ LOGV("Attempting to find %s", symbol);
+
+ void *resolvedSymbol = nullptr;
+ if (handle == RTLD_NEXT) {
+ resolvedSymbol = chre::NanoappLoader::findExportedSymbol(symbol);
+ } else if (handle != nullptr) {
+ auto *loader = reinterpret_cast<chre::NanoappLoader *>(handle);
+ resolvedSymbol = loader->findSymbolByName(symbol);
+ }
+
+ if (resolvedSymbol == nullptr) {
+ LOGE("dlsym unable to resolve %s", symbol);
+ } else {
+ LOGV("Found symbol at %p", resolvedSymbol);
+ }
+
+ return resolvedSymbol;
+}
+
+int dlclose(void *handle) {
+ int rv = -1;
+
+ if (handle != nullptr) {
+ chre::NanoappLoader::destroy(static_cast<chre::NanoappLoader *>(handle));
+ rv = 0;
+ }
+
+ return rv;
+}
+
+const char *dlerror() {
+ return "Shared library load failed";
+}
diff --git a/platform/shared/dram_vote_client.cc b/platform/shared/dram_vote_client.cc
new file mode 100644
index 0000000..9a15120
--- /dev/null
+++ b/platform/shared/dram_vote_client.cc
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/shared/dram_vote_client.h"
+
+#include <cinttypes>
+
+#include "chre/platform/assert.h"
+#include "chre/platform/fatal_error.h"
+#include "chre/platform/log.h"
+#include "chre/platform/system_time.h"
+
+namespace chre {
+
+void DramVoteClient::voteDramAccess(bool enabled) {
+ LockGuard<Mutex> lock(mMutex);
+ mLastDramRequest = enabled;
+
+ bool needDram = (enabled || mDramVoteCount > 0);
+ if (needDram != mLastDramVote) {
+ issueDramVote(needDram);
+ mLastDramVote = needDram;
+ }
+}
+
+void DramVoteClient::incrementDramVoteCount() {
+ LockGuard<Mutex> lock(mMutex);
+
+ if (mDramVoteCount++ == 0) {
+ mVoteCountStart = Milliseconds(SystemTime::getMonotonicTime());
+ LOGW("DRAM vote count begins");
+
+ if (!mLastDramVote) {
+ // Do not call voteDramAccess() directly as it will override
+ // mLastDramRequest.
+ issueDramVote(true /* enabled */);
+ mLastDramVote = true;
+ }
+ } else {
+ checkDramDuration();
+ }
+}
+
+void DramVoteClient::decrementDramVoteCount() {
+ LockGuard<Mutex> lock(mMutex);
+ CHRE_ASSERT_LOG(mDramVoteCount > 0,
+ "Tried to decrement DRAM vote count when it's 0");
+
+ if (--mDramVoteCount == 0) {
+ LOGW("DRAM vote count ends: %" PRIu64 " ms", checkDramDuration());
+
+ // There's no DRAM activity now, remove CHRE's DRAM access vote.
+ if (!mLastDramRequest) {
+ issueDramVote(false /* enabled */);
+ mLastDramVote = false;
+ }
+ }
+}
+
+Milliseconds DramVoteClient::checkDramDuration() const {
+ Milliseconds duration(0);
+ if (mDramVoteCount > 0) {
+ duration = Milliseconds(SystemTime::getMonotonicTime()) - mVoteCountStart;
+ }
+
+ // DRAM memory fallback only intends to handle a surge of DRAM memory
+ // requests. If there's a prolonged period of memory fallback, this might
+ // indicate a memory leak or inadequate SRAM heap size.
+ if (duration > kMaxDramDuration) {
+ FATAL_ERROR("Forced into DRAM for %" PRIu64 " msec", duration);
+ }
+ return duration;
+}
+
+//! Explicitly instantiate the DramVoteClient singleton to reduce code size.
+template class Singleton<DramVoteClient>;
+
+} // namespace chre
diff --git a/platform/shared/host_protocol_chre.cc b/platform/shared/host_protocol_chre.cc
index 7aa751e..9e04a1a 100644
--- a/platform/shared/host_protocol_chre.cc
+++ b/platform/shared/host_protocol_chre.cc
@@ -81,9 +81,10 @@
getStringFromByteVector(request->app_binary_file_name());
HostMessageHandlers::handleLoadNanoappRequest(
hostClientId, request->transaction_id(), request->app_id(),
- request->app_version(), request->target_api_version(),
- appBinary->data(), appBinary->size(), appBinaryFilename,
- request->fragment_id(), request->total_app_size());
+ request->app_version(), request->app_flags(),
+ request->target_api_version(), appBinary->data(), appBinary->size(),
+ appBinaryFilename, request->fragment_id(),
+ request->total_app_size());
break;
}
diff --git a/platform/shared/idl/host_messages.fbs b/platform/shared/idl/host_messages.fbs
index a34836c..c6f0451 100644
--- a/platform/shared/idl/host_messages.fbs
+++ b/platform/shared/idl/host_messages.fbs
@@ -139,6 +139,10 @@
/// Null-terminated ASCII string containing the file name that contains the
/// app binary to be loaded.
app_binary_file_name:[byte];
+
+ /// The nanoapp flag values from the nanoapp header defined in
+ /// build/build_template.mk. Refer to that file for more details.
+ app_flags:uint;
}
table LoadNanoappResponse {
diff --git a/platform/shared/include/chre/platform/shared/authentication.h b/platform/shared/include/chre/platform/shared/authentication.h
new file mode 100644
index 0000000..6409b47
--- /dev/null
+++ b/platform/shared/include/chre/platform/shared/authentication.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_SHARED_AUTHENTICATION_H_
+#define CHRE_PLATFORM_SHARED_AUTHENTICATION_H_
+
+namespace chre {
+
+/**
+ * Authenticates the signature of the provided binary. If not provided
+ * elsewhere by the platform, this method must ensure that nanoapps are signed
+ * appropriately and no corruption has occurred to the binary prior to being
+ * loaded. If this method succeeds, CHRE will assume the binary has the same
+ * execution privileges as the core framework itself.
+ *
+ * @param binary Pointer to the binary that should be authenticated.
+ * @param realBinaryStart A non-null pointer that, if this method succeeds, must
+ * be filled with the starting address of the raw binary after any headers
+ * used by the authentication code. This will be passed to the dynamic
+ * loader which will assume the starting address is a valid ELF binary.
+ * @return True if the binary passed authentication.
+ */
+bool authenticateBinary(void *binary, void **realBinaryStart);
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SHARED_AUTHENTICATION_H_
diff --git a/platform/shared/include/chre/platform/shared/dram_vote_client.h b/platform/shared/include/chre/platform/shared/dram_vote_client.h
new file mode 100644
index 0000000..00476a4
--- /dev/null
+++ b/platform/shared/include/chre/platform/shared/dram_vote_client.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_SHARED_VOTE_CLIENT_H_
+#define CHRE_PLATFORM_SHARED_VOTE_CLIENT_H_
+
+#include "chre/platform/mutex.h"
+#include "chre/util/lock_guard.h"
+#include "chre/util/singleton.h"
+#include "chre/util/time.h"
+
+namespace chre {
+
+/**
+ * Class used to manage voting for access to DRAM for platforms that require
+ * voting for DRAM memory to be accessible.
+ */
+class DramVoteClient : public NonCopyable {
+ public:
+ /**
+ * @return true if CHRE currently has a DRAM vote in place.
+ */
+ bool isDramVoteActive() {
+ LockGuard<Mutex> lock(mMutex);
+ return mLastDramVote;
+ }
+
+ /**
+ * Makes a DRAM access request. An actual vote to the memory manager may not
+ * be cast depending on the current mode and mDramVoteCount.
+ *
+ * @param enabled Whether to request DRAM access.
+ */
+ void voteDramAccess(bool enabled);
+
+ /**
+ * Increment the DRAM vote count when a client needs to perform some DRAM
+ * activity. A DRAM vote is issued when the count increments from 0.
+ */
+ void incrementDramVoteCount();
+
+ /**
+ * Decrement the DRAM vote count when a client finishes some activity that has
+ * to be performed in DRAM. A DRAM vote may be removed when the count
+ * decrements to 0, depending on if explicit DRAM votes have been issued from
+ * voteDramAccess.
+ */
+ void decrementDramVoteCount();
+
+ private:
+ //! The maximum allowed duration to be voted into DRAM by
+ //! incrementDramVoteCount before a FATAL_ERROR is triggered.
+ static constexpr Seconds kMaxDramDuration = Seconds(300);
+
+ //! Last DRAM request made through voteDramAccess().
+ bool mLastDramRequest = false;
+
+ //! Last DRAM vote cast to the system.
+ bool mLastDramVote = false;
+
+ //! The system time mDramVoteCount increments from 0.
+ Milliseconds mVoteCountStart = Milliseconds(0);
+
+ //! The count of DRAM activities.
+ uint32_t mDramVoteCount = 0;
+
+ //! Used to protect access to member variables from other threads.
+ Mutex mMutex;
+
+ /**
+ * Issue a vote to the underlying system. This must be implemented by each
+ * platform to communicate with the right system.
+ *
+ * @param enabled Whether DRAM should be accessible.
+ */
+ void issueDramVote(bool enabled);
+
+ /**
+ * Check how long the system has been voted into DRAM due to
+ * incrementDramVoteCount. If longer than kMaxDramDuration, trigger a crash.
+ *
+ * @return the duration in milliseconds since the system has been voted into
+ * big image due to incrementDramVoteCount.
+ */
+ Milliseconds checkDramDuration() const;
+};
+
+//! Provides an alias to the DramVoteClient singleton
+typedef Singleton<DramVoteClient> DramVoteClientSingleton;
+
+extern template class Singleton<DramVoteClient>;
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SHARED_VOTE_CLIENT_H_
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 15a9284..8eeb753 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
@@ -839,7 +839,8 @@
VT_APP_BINARY = 12,
VT_FRAGMENT_ID = 14,
VT_TOTAL_APP_SIZE = 16,
- VT_APP_BINARY_FILE_NAME = 18
+ VT_APP_BINARY_FILE_NAME = 18,
+ VT_APP_FLAGS = 20
};
uint32_t transaction_id() const {
return GetField<uint32_t>(VT_TRANSACTION_ID, 0);
@@ -871,6 +872,11 @@
const flatbuffers::Vector<int8_t> *app_binary_file_name() const {
return GetPointer<const flatbuffers::Vector<int8_t> *>(VT_APP_BINARY_FILE_NAME);
}
+ /// The nanoapp flag values from the nanoapp header defined in
+ /// build/build_template.mk. Refer to that file for more details.
+ uint32_t app_flags() const {
+ return GetField<uint32_t>(VT_APP_FLAGS, 0);
+ }
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint32_t>(verifier, VT_TRANSACTION_ID) &&
@@ -883,6 +889,7 @@
VerifyField<uint32_t>(verifier, VT_TOTAL_APP_SIZE) &&
VerifyOffset(verifier, VT_APP_BINARY_FILE_NAME) &&
verifier.VerifyVector(app_binary_file_name()) &&
+ VerifyField<uint32_t>(verifier, VT_APP_FLAGS) &&
verifier.EndTable();
}
};
@@ -915,6 +922,9 @@
void add_app_binary_file_name(flatbuffers::Offset<flatbuffers::Vector<int8_t>> app_binary_file_name) {
fbb_.AddOffset(LoadNanoappRequest::VT_APP_BINARY_FILE_NAME, app_binary_file_name);
}
+ void add_app_flags(uint32_t app_flags) {
+ fbb_.AddElement<uint32_t>(LoadNanoappRequest::VT_APP_FLAGS, app_flags, 0);
+ }
explicit LoadNanoappRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
@@ -937,9 +947,11 @@
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> app_binary = 0,
uint32_t fragment_id = 0,
uint32_t total_app_size = 0,
- flatbuffers::Offset<flatbuffers::Vector<int8_t>> app_binary_file_name = 0) {
+ flatbuffers::Offset<flatbuffers::Vector<int8_t>> app_binary_file_name = 0,
+ uint32_t app_flags = 0) {
LoadNanoappRequestBuilder builder_(_fbb);
builder_.add_app_id(app_id);
+ builder_.add_app_flags(app_flags);
builder_.add_app_binary_file_name(app_binary_file_name);
builder_.add_total_app_size(total_app_size);
builder_.add_fragment_id(fragment_id);
@@ -959,7 +971,8 @@
const std::vector<uint8_t> *app_binary = nullptr,
uint32_t fragment_id = 0,
uint32_t total_app_size = 0,
- const std::vector<int8_t> *app_binary_file_name = nullptr) {
+ const std::vector<int8_t> *app_binary_file_name = nullptr,
+ uint32_t app_flags = 0) {
auto app_binary__ = app_binary ? _fbb.CreateVector<uint8_t>(*app_binary) : 0;
auto app_binary_file_name__ = app_binary_file_name ? _fbb.CreateVector<int8_t>(*app_binary_file_name) : 0;
return chre::fbs::CreateLoadNanoappRequest(
@@ -971,7 +984,8 @@
app_binary__,
fragment_id,
total_app_size,
- app_binary_file_name__);
+ app_binary_file_name__,
+ app_flags);
}
struct LoadNanoappResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
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 8ee6f80..0b3deea 100644
--- a/platform/shared/include/chre/platform/shared/host_protocol_chre.h
+++ b/platform/shared/include/chre/platform/shared/host_protocol_chre.h
@@ -59,9 +59,9 @@
static void handleLoadNanoappRequest(
uint16_t hostClientId, uint32_t transactionId, uint64_t appId,
- uint32_t appVersion, uint32_t targetApiVersion, const void *buffer,
- size_t bufferLen, const char *appFileName, uint32_t fragmentId,
- size_t appBinaryLen);
+ uint32_t appVersion, uint32_t appFlags, uint32_t targetApiVersion,
+ const void *buffer, size_t bufferLen, const char *appFileName,
+ uint32_t fragmentId, size_t appBinaryLen);
static void handleUnloadNanoappRequest(uint16_t hostClientId,
uint32_t transactionId, uint64_t appId,
diff --git a/platform/shared/include/chre/platform/shared/libc/dlfcn.h b/platform/shared/include/chre/platform/shared/libc/dlfcn.h
new file mode 100644
index 0000000..9758378
--- /dev/null
+++ b/platform/shared/include/chre/platform/shared/libc/dlfcn.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_SHARED_DLFCN_H_
+#define CHRE_PLATFORM_SHARED_DLFCN_H_
+
+//! This file is intended to be used when a platform doesn't provide a dlfcn.h
+//! implementation so that dynamic loading support can be provided to nanoapps.
+
+#include <cstdlib>
+
+//! Indicates that the dlsym call is attempting to lookup the provided symbol
+//! in another library (CHRE).
+#ifndef RTLD_NEXT
+#define RTLD_NEXT ((void *)-1L)
+#endif // RTLD_NEXT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This function parses, verifies, and loads a buffer containing an ELF
+ * file, and returns an opaque handle for symbol lookup via dlsym.
+ * Note that there is no requirement to pass in a 'buffer size' argument,
+ * since all the necessary information is gathered from the headers
+ * embedded in the elf file itself. The buffer is expected to contain the
+ * entire elf file in memory (cannot be a FILE pointer, for example).
+ *
+ * @param elfBinary is a buffer containing the elf file
+ * @param mapIntoTcm Indicates whether the elfBinary should be mapped into
+ * tightly coupled memory.
+ */
+void *dlopenbuf(void *elfBinary, bool mapIntoTcm);
+
+/**
+ * Returns a (function) pointer to the symbol named by the input argument.
+ *
+ * @return pointer to the symbol if it was found, nullptr if not found or
+ * the handle was invalid
+ */
+void *dlsym(void *handle, const char *symbol);
+
+/**
+ * Invalidate the handle used for symbol lookup, and free up any
+ * allocated memory.
+ *
+ * @return 0 on success
+ */
+int dlclose(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CHRE_PLATFORM_SHARED_DLFCN_H_
diff --git a/platform/shared/include/chre/platform/shared/loader_util.h b/platform/shared/include/chre/platform/shared/loader_util.h
new file mode 100644
index 0000000..8e33428
--- /dev/null
+++ b/platform/shared/include/chre/platform/shared/loader_util.h
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_SHARED_LOADER_UTIL_H_
+#define CHRE_PLATFORM_SHARED_LOADER_UTIL_H_
+
+// The below macros allow switching the ELF symbol type between 32/64-bit
+// depending on what the chipset supports.
+#ifndef ELFW
+#ifndef __WORDSIZE
+// Until we can get a hold of wordsize.h, we need to define it here.
+// Only 32-bit architectures currently supported.
+#ifndef __aarch64__
+#define __WORDSIZE 32
+#else
+#error "Only 32-bit architectures currently supported"
+#endif
+#endif
+// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
+#define ELF32_R_SYM(info) ((info) >> 8)
+#define ELF32_R_TYPE(info) ((unsigned char)(info))
+#define ELF32_R_INFO(sym, type) (((sym) << 8) + (unsigned char)(type))
+#define __ELF_NATIVE_CLASS __WORDSIZE
+#define ELFW(type) _ELFW(__ELF_NATIVE_CLASS, type)
+#define _ELFW(bits, type) __ELFW(bits, type)
+#define __ELFW(bits, type) ELF##bits##_##type
+#endif
+#define ELFW_R_TYPE(x) ELFW(R_TYPE)(x)
+#define ELFW_R_SYM(x) ELFW(R_SYM)(x)
+
+struct ExportedData {
+ void *data;
+ const char *dataName;
+};
+
+// The below is copied from bionic/libc/kernel/uapi/linux/elf.h
+// to avoid pulling those deps into the build.
+#if defined(__LP64__)
+#define ElfW(type) Elf64_##type
+#else
+#define ElfW(type) Elf32_##type
+#endif
+
+#define EM_ARM 40
+#define EI_MAG0 0
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_OSABI 7
+#define EI_PAD 8
+#define ELFMAG0 0x7f
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+#define ELFCLASSNONE 0
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+#define ELFCLASSNUM 3
+#define ELFDATANONE 0
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+#define EV_NONE 0
+#define EV_CURRENT 1
+#define EV_NUM 2
+#define ELFOSABI_NONE 0
+#define ELFOSABI_LINUX 3
+#define PT_NULL 0
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+#define PT_INTERP 3
+#define PT_NOTE 4
+#define PT_SHLIB 5
+#define PT_PHDR 6
+#define PT_TLS 7
+#define PT_LOOS 0x60000000
+#define PT_HIOS 0x6fffffff
+#define PT_LOPROC 0x70000000
+#define PT_HIPROC 0x7fffffff
+#define PT_GNU_EH_FRAME 0x6474e550
+#define PT_GNU_STACK (PT_LOOS + 0x474e551)
+#define PN_XNUM 0xffff
+#define ET_NONE 0
+#define ET_REL 1
+#define ET_EXEC 2
+#define ET_DYN 3
+#define ET_CORE 4
+#define ET_LOPROC 0xff00
+#define ET_HIPROC 0xffff
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_RELA 7
+#define DT_RELASZ 8
+#define DT_RELAENT 9
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+#define DT_INIT 12
+#define DT_FINI 13
+#define DT_SONAME 14
+#define DT_RPATH 15
+#define DT_SYMBOLIC 16
+#define DT_REL 17
+#define DT_RELSZ 18
+#define DT_RELENT 19
+#define DT_PLTREL 20
+#define DT_DEBUG 21
+#define DT_TEXTREL 22
+#define DT_JMPREL 23
+#define DT_ENCODING 32
+
+typedef __signed__ char __s8;
+typedef unsigned char __u8;
+typedef __signed__ short __s16;
+typedef unsigned short __u16;
+typedef __signed__ int __s32;
+typedef unsigned int __u32;
+typedef __signed__ long __s64;
+typedef unsigned long __u64;
+
+typedef __u32 Elf32_Addr;
+typedef __u16 Elf32_Half;
+typedef __u32 Elf32_Off;
+typedef __s32 Elf32_Sword;
+typedef __u32 Elf32_Word;
+
+#define EI_NIDENT 16
+typedef struct elf32_hdr {
+ unsigned char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry;
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct dynamic {
+ Elf32_Sword d_tag;
+ union {
+ Elf32_Sword d_val;
+ Elf32_Addr d_ptr;
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct elf32_phdr {
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} Elf32_Phdr;
+
+typedef struct elf32_shdr {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+typedef struct elf32_rela {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+ Elf32_Sword r_addend;
+} Elf32_Rela;
+
+typedef struct elf32_rel {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+} Elf32_Rel;
+
+typedef struct elf32_sym {
+ Elf32_Word st_name;
+ Elf32_Addr st_value;
+ Elf32_Word st_size;
+ unsigned char st_info;
+ unsigned char st_other;
+ Elf32_Half st_shndx;
+} Elf32_Sym;
+
+// The following defines are copied from bionic's elf_arm.h header
+// at bionic/libc/kernel/uapi/linux/elf.h
+// Only the relocation types currently supported are copied.
+#define R_ARM_NONE 0
+#define R_ARM_ABS32 2
+#define R_ARM_COPY 20
+#define R_ARM_GLOB_DAT 21
+#define R_ARM_JUMP_SLOT 22
+#define R_ARM_RELATIVE 23
+
+// The following (legal values for segment flags) are copied from
+// bionic's elf.h
+/* http://www.sco.com/developers/gabi/latest/ch5.pheader.html */
+#define PF_X 0x1
+#define PF_W 0x2
+#define PF_R 0x4
+#define PF_MASKOS 0x0ff00000
+#define PF_MASKPROC 0xf0000000
+
+#endif // CHRE_PLATFORM_SHARED_LOADER_UTIL_H_
diff --git a/platform/shared/include/chre/platform/shared/memory.h b/platform/shared/include/chre/platform/shared/memory.h
new file mode 100644
index 0000000..46a7721
--- /dev/null
+++ b/platform/shared/include/chre/platform/shared/memory.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_SHARED_MEMORY_H_
+#define CHRE_PLATFORM_SHARED_MEMORY_H_
+
+#include <cstddef>
+#include <type_traits>
+
+namespace chre {
+
+/**
+ * Aligned memory allocation using the lowest power heap available. The
+ * semantics are the same as malloc.
+ */
+void *memoryAllocAligned(size_t alignment, size_t size);
+
+/**
+ * Aligned memory allocation specifically using the DRAM heap. The semantics are
+ * the same as malloc.
+ *
+ * If DRAM or another large memory capacity region is not available, this will
+ * allocate from the same memory region as memoryAlloc.
+ */
+void *memoryAllocDramAligned(size_t alignment, size_t size);
+
+/**
+ * Memory allocation specifically using the DRAM heap. The semantics are the
+ * same as malloc.
+ *
+ * If DRAM or another large memory capacity region is not available, this will
+ * allocate from the same memory region as memoryAlloc.
+ */
+void *memoryAllocDram(size_t size);
+
+/**
+ * Memory free from memory allocated using the DRAM heap. The semantics are the
+ * same as free.
+ *
+ * If DRAM or another large memory capacity region is not available, this will
+ * free from the same memory region as memoryFree.
+ */
+void memoryFreeDram(void *pointer);
+
+/**
+ * Ensures memory allocated through memoryAllocDram can be utilized. If memory
+ * allocated through memoryAllocDram is always available, this method can be a
+ * no-op.
+ *
+ * This must be support being invoked from multiple threads.
+ */
+void forceDramAccess();
+
+/**
+ * Removes CHRE's vote to keep DRAM accessible. This must only be called when
+ * CHRE is idle.
+ */
+void removeDramAccessVote();
+
+/**
+ * Allocates memory in DRAM for an object of size T and constructs the object in
+ * the newly allocated object by forwarding the provided parameters.
+ */
+template <typename T, typename... Args>
+inline T *memoryAllocDram(Args &&... args) {
+ auto *storage = static_cast<T *>(memoryAllocDram(sizeof(T)));
+ if (storage != nullptr) {
+ new (storage) T(std::forward<Args>(args)...);
+ }
+
+ return storage;
+}
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SHARED_MEMORY_H_
diff --git a/platform/shared/include/chre/platform/shared/nanoapp_load_manager.h b/platform/shared/include/chre/platform/shared/nanoapp_load_manager.h
index f8b17a5..c8ae589 100644
--- a/platform/shared/include/chre/platform/shared/nanoapp_load_manager.h
+++ b/platform/shared/include/chre/platform/shared/nanoapp_load_manager.h
@@ -52,12 +52,13 @@
* @param transactionId the ID of the transaction
* @param appId the ID of the app to load
* @param appVersion the version of the app to load
+ * @param appFlags the flags provided by the app being loaded
* @param totalBinaryLen the total nanoapp binary length
*
* @return true if the preparation was successful, false otherwise
*/
bool prepareForLoad(uint16_t hostClientId, uint32_t transactionId,
- uint64_t appId, uint32_t appVersion,
+ uint64_t appId, uint32_t appVersion, uint32_t appFlags,
size_t totalBinaryLen);
/**
diff --git a/platform/shared/include/chre/platform/shared/nanoapp_loader.h b/platform/shared/include/chre/platform/shared/nanoapp_loader.h
new file mode 100644
index 0000000..8185f4c
--- /dev/null
+++ b/platform/shared/include/chre/platform/shared/nanoapp_loader.h
@@ -0,0 +1,349 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_
+#define CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_
+
+#include <cinttypes>
+#include <cstdlib>
+
+#include "chre/platform/shared/loader_util.h"
+
+#include "chre/util/dynamic_vector.h"
+#include "chre/util/nested_data_ptr.h"
+
+namespace chre {
+
+/**
+ * Provides dynamic loading support for nanoapps on FreeRTOS-based platforms.
+ * At a high level, this class is responsible for mapping the provided binary
+ * into CHRE's address space, relocating and resolving symbols, and initializing
+ * and freeing static data.
+ */
+class NanoappLoader {
+ public:
+ NanoappLoader() = delete;
+
+ explicit NanoappLoader(void *elfInput, bool mapIntoTcm) {
+ mBinary.dataPtr = elfInput;
+ mIsTcmBinary = mapIntoTcm;
+ }
+
+ /**
+ * Factory method to create a NanoappLoader Instance after loading
+ * the buffer containing the ELF binary.
+ *
+ * @param elfInput Buffer containing the elf file
+ * @param mapIntoTcm Indicates whether the elfBinary should be mapped into
+ * tightly coupled memory.
+ * @return Class instance on successful load and verification,
+ * nullptr otherwise.
+ */
+ static void *create(void *elfInput, bool mapIntoTcm);
+
+ /**
+ * Closes and destroys the NanoappLoader instance.
+ *
+ * @param loader A non-null pointer to the loader that must be destroyed.
+ */
+ static void destroy(NanoappLoader *loader);
+
+ /**
+ * Attempts to locate the exported symbol specified by the given function
+ * name.
+ *
+ * @param name A null-terminated char array that is the name of the function
+ * to be found.
+ * @return The address of the function. nullptr if not found.
+ */
+ static void *findExportedSymbol(const char *name);
+
+ /**
+ * Method for pointer lookup by symbol name. Only function pointers
+ * are currently supported.
+ *
+ * @return function pointer on successful lookup, nullptr otherwise
+ */
+ void *findSymbolByName(const char *name);
+
+ /**
+ * Registers a function provided through atexit during static initialization
+ * that should be called prior to unloading a nanoapp.
+ *
+ * @param function Function that should be invoked prior to unloading a
+ * nanoapp.
+ */
+ void registerAtexitFunction(void (*function)(void));
+
+ private:
+ /**
+ * Opens the ELF binary. This maps the binary into memory, resolves symbols,
+ * and invokes any static initializers.
+ *
+ * @return true if all required opening steps were completed.
+ */
+ bool open();
+
+ /**
+ * Closes the loader, freeing any state associated with the loaded ELF binary
+ * and unmapping it from memory. Prior to unmapping from memory, any static
+ * termination functions will be invoked.
+ */
+ void close();
+
+ using DynamicHeader = ElfW(Dyn);
+ using ElfAddr = ElfW(Addr);
+ using ElfHeader = ElfW(Ehdr);
+ using ElfRel = ElfW(Rel); // Relocation table entry,
+ // in section of type SHT_REL
+ using ElfRela = ElfW(Rela);
+ using ElfSym = ElfW(Sym);
+ using ElfWord = ElfW(Word);
+ using ProgramHeader = ElfW(Phdr);
+ using SectionHeader = ElfW(Shdr);
+
+ //! Name of various segments in the ELF that need to be looked up
+ static constexpr const char *kSymTableName = ".symtab";
+ static constexpr const char *kStrTableName = ".strtab";
+ static constexpr const char *kInitArrayName = ".init_array";
+ static constexpr const char *kFiniArrayName = ".fini_array";
+ // For now, assume all segments are 4K aligned.
+ // TODO(karthikmb/stange): See about reducing this.
+ static constexpr size_t kBinaryAlignment = 4096;
+
+ //! Pointer to the table of all the section names.
+ char *mSectionNamesPtr = nullptr;
+ //! Pointer to the table of symbol names of defined symbols.
+ char *mStringTablePtr = nullptr;
+ //! Pointer to the table of symbol information for defined symbols.
+ uint8_t *mSymbolTablePtr = nullptr;
+ //! Pointer to the array of section header entries.
+ SectionHeader *mSectionHeadersPtr;
+ //! Number of SectionHeaders pointed to by mSectionHeadersPtr.
+ size_t mNumSectionHeaders = 0;
+ //! Size of the data pointed to by mSymbolTablePtr.
+ size_t mSymbolTableSize = 0;
+
+ // TODO(stange): Store this elsewhere since the location will be invalid after
+ // loading is complete.
+ //! The ELF that is being mapped into the system. This pointer will be invalid
+ //! after open returns.
+ NestedDataPtr<uintptr_t> mBinary = {0};
+ //! The starting location of the memory that has been mapped into the system.
+ NestedDataPtr<uintptr_t> mMapping = {0};
+ //! The difference between where the first load segment was mapped into
+ //! virtual memory and what the virtual load offset was of that segment.
+ ElfAddr mLoadBias = 0;
+ //! Dynamic vector containing functions that should be invoked prior to
+ //! unloading this nanoapp. Note that functions are stored in the order they
+ //! were added and should be called in reverse.
+ DynamicVector<void (*)(void)> mAtexitFunctions;
+ //! Whether this loader instance is managing a TCM nanoapp binary.
+ bool mIsTcmBinary;
+
+ /**
+ * Invokes all functions registered via atexit during static initialization.
+ */
+ void callAtexitFunctions();
+
+ /**
+ * Invokes all initialization functions in .init_array segment.
+ *
+ * @return true if static initialization succeeded.
+ */
+ bool callInitArray();
+
+ /**
+ * Invokes all termination functions in the .fini_array segment.
+ */
+ void callTerminatorArray();
+
+ /**
+ * Allocates memory for all load segments that need to be mapped into virtual
+ * memory and copies the load segments into the newly allocated memory.
+ *
+ * @return true if the memory for mapping was allocated and the load segments
+ * were formatted correctly.
+ */
+ bool createMappings();
+
+ /**
+ * Copies various sections and headers from the ELF while verifying that they
+ * match the ELF format specficiation.
+ *
+ * @return true if all data was copied and verified.
+ */
+ bool copyAndVerifyHeaders();
+
+ /**
+ * Resolves all relocated symbols located in the DT_REL table.
+ *
+ * @return true if all relocated symbols were resolved.
+ */
+ bool fixRelocations();
+
+ /**
+ * Resolves entries in the Global Offset Table (GOT) to facility the ELF's
+ * compiled using position independent code (PIC).
+ *
+ * @return true if all symbols were resolved.
+ */
+ bool resolveGot();
+
+ /**
+ * Verifies the ELF header has correct values based on the ELF spec.
+ *
+ * @return true if the header passed verification.
+ */
+ bool verifyElfHeader();
+
+ /**
+ * Verifies basic information about program headers.
+ *
+ * @return true if the headers passed verification.
+ */
+ bool verifyProgramHeaders();
+
+ /**
+ * Verifies basic information about section headers.
+ *
+ * @return true if the headers passed verification.
+ */
+ bool verifySectionHeaders();
+
+ /**
+ * Retrieves the symbol name of data located at the given position in the
+ * symbol table.
+ *
+ * @param posInSymbolTable The position in the symbol table where information
+ * about the symbol can be found.
+ * @return The symbol's name or nullptr if not found.
+ */
+ const char *getDataName(size_t posInSymbolTable);
+
+ /**
+ * Retrieves the name of the section header located at the given offset in the
+ * section name table.
+ *
+ * @param headerOffset The offset in the section names table where the header
+ * is located.
+ * @return The section's name or the empty string if the offset is 0.
+ */
+ const char *getSectionHeaderName(size_t headerOffset);
+
+ /**
+ * Rounds the given address down to the closest alignment boundary.
+ *
+ * @param virtualAddr The address to be rounded.
+ * @return An address that is a multiple of the platform's alignment and is
+ * less than or equal to virtualAddr.
+ */
+ uintptr_t roundDownToAlign(uintptr_t virtualAddr);
+
+ /**
+ * Frees any data that was allocated as part of loading the ELF into memory.
+ */
+ void freeAllocatedData();
+
+ /**
+ * Ensures the BSS section is properly mapped into memory. If there is a
+ * difference between the size of the BSS section in the ELF binary and the
+ * size it needs to be in memory, the rest of the section is zeroed out.
+ *
+ * @param header The ProgramHeader of the BSS section that is being mapped in.
+ */
+ void mapBss(const ProgramHeader *header);
+
+ /**
+ * Resolves the address of an undefined symbol located at the given position
+ * in the symbol table. This symbol must be defined and exposed by the given
+ * platform in order for it to be resolved successfully.
+ *
+ * @param posInSymbolTable The position of the undefined symbol in the symbol
+ * table.
+ * @return The address of the resolved symbol. nullptr if not found.
+ */
+ void *resolveData(size_t posInSymbolTable);
+
+ /**
+ * @return The address for the dynamic segment. nullptr if not found.
+ */
+ DynamicHeader *getDynamicHeader();
+
+ /**
+ * @return The address of the first read-only segment. nullptr if not found.
+ */
+ ProgramHeader *getFirstRoSegHeader();
+
+ /**
+ * Retrieves the section header with the given name.
+ *
+ * @param headerName The name of the section header to find.
+ * @return The address of the section. nullptr if not found.
+ */
+ SectionHeader *getSectionHeader(const char *headerName);
+
+ /**
+ * @return The ELF header for the binary being loaded. nullptr if it doesn't
+ * exist or no binary is being loaded.
+ */
+ ElfHeader *getElfHeader();
+
+ /**
+ * @return The array of program headers for the binary being loaded. nullptr
+ * if it doesn't exist or no binary is being loaded.
+ */
+ ProgramHeader *getProgramHeaderArray();
+
+ /**
+ * @return The size of the array of program headers for the binary being
+ * loaded. 0 if it doesn't exist or no binary is being loaded.
+ */
+ size_t getProgramHeaderArraySize();
+
+ /**
+ * @return An array of characters containing the symbol names for dynamic
+ * symbols inside the binary being loaded. nullptr if it doesn't exist or
+ * no binary is being loaded.
+ */
+ char *getDynamicStringTable();
+
+ /**
+ * @return An array of dynamic symbol information for the binary being loaded.
+ * nullptr if it doesn't exist or no binary is being loaded.
+ */
+ uint8_t *getDynamicSymbolTable();
+
+ /**
+ * @return The size of the array of dynamic symbol information for the binary
+ * being loaded. 0 if it doesn't exist or no binary is being loaded.
+ */
+ size_t getDynamicSymbolTableSize();
+
+ /**
+ * Returns the first entry in the dynamic header that has a tag that matches
+ * the given field.
+ *
+ * @param dyn The dynamic header for the binary.
+ * @param field The field to be searched for.
+ * @return The value found at the entry. 0 if the entry isn't found.
+ */
+ static ElfWord getDynEntry(DynamicHeader *dyn, int field);
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SHARED_NANOAPP_LOADER_H_
diff --git a/platform/shared/include/chre/platform/shared/nanoapp_support_lib_dso.h b/platform/shared/include/chre/platform/shared/nanoapp_support_lib_dso.h
index 239206e..abce1bb 100644
--- a/platform/shared/include/chre/platform/shared/nanoapp_support_lib_dso.h
+++ b/platform/shared/include/chre/platform/shared/nanoapp_support_lib_dso.h
@@ -46,6 +46,10 @@
//! The symbol name expected from the nanoapp's definition of its info struct
#define CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME "_chreNslDsoNanoappInfo"
+//! The symbol name expected from the nanoapp's definition of its unstable ID
+//! char array
+#define CHRE_NSL_DSO_NANOAPP_UNSTABLE_ID_SYMBOL_NAME "_chreNanoappUnstableId"
+
//! Maximum length of vendor and name strings
#define CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN (32)
diff --git a/platform/shared/include/chre/target_platform/platform_cache_management.h b/platform/shared/include/chre/target_platform/platform_cache_management.h
new file mode 100644
index 0000000..123de17
--- /dev/null
+++ b/platform/shared/include/chre/target_platform/platform_cache_management.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_SHARED_CACHE_MANAGEMENT_H_
+#define CHRE_PLATFORM_SHARED_CACHE_MANAGEMENT_H_
+
+namespace chre {
+
+/**
+ * Invalidates and/or cleans the system instruction and data caches.
+ *
+ * When nanoapps in CHRE are loaded dynamically, the data and
+ * executable segments are parsed and relocated into memory. This operation
+ * can cause cached instructions and data to be invalid when we start executing
+ * the newly loaded nanoapp's instructions, and could possibly lead to an
+ * exception. To support custom dynamic loading implementations (for example,
+ * when it's not part of the underlying OS/system), the platform needs to
+ * implement (or provide an empty stub for) the following method to invalidate
+ * and/or clean the system data and instruction caches.
+ */
+void wipeSystemCaches();
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SHARED_CACHE_MANAGEMENT_H_
diff --git a/platform/shared/nanoapp/nanoapp_support_lib_dso.cc b/platform/shared/nanoapp/nanoapp_support_lib_dso.cc
index 51cba03..48131ea 100644
--- a/platform/shared/nanoapp/nanoapp_support_lib_dso.cc
+++ b/platform/shared/nanoapp/nanoapp_support_lib_dso.cc
@@ -31,7 +31,7 @@
namespace {
-#ifdef CHRE_SLPI_UIMG_ENABLED
+#if defined(CHRE_SLPI_UIMG_ENABLED) || defined(CHRE_TCM_ENABLED)
constexpr int kIsTcmNanoapp = 1;
#else
constexpr int kIsTcmNanoapp = 0;
@@ -75,6 +75,15 @@
} // anonymous namespace
+//! Additional symbol used to determine the given unstable ID that was provided
+//! when building this nanoapp, if any. The symbol is placed in its own section
+//! so it be stripped to determine if the nanoapp changed compared to a previous
+//! version.
+#ifdef NANOAPP_UNSTABLE_ID
+DLL_EXPORT extern "C" const char _chreNanoappUnstableId[]
+ __attribute__((section(".unstable_id"))) = NANOAPP_UNSTABLE_ID;
+#endif // NANOAPP_UNSTABLE_ID
+
DLL_EXPORT extern "C" const struct chreNslNanoappInfo _chreNslDsoNanoappInfo = {
/* magic */ CHRE_NSL_NANOAPP_INFO_MAGIC,
/* structMinorVersion */ CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION,
diff --git a/platform/shared/nanoapp_load_manager.cc b/platform/shared/nanoapp_load_manager.cc
index 03e2b0a..d16b822 100644
--- a/platform/shared/nanoapp_load_manager.cc
+++ b/platform/shared/nanoapp_load_manager.cc
@@ -20,13 +20,14 @@
bool NanoappLoadManager::prepareForLoad(uint16_t hostClientId,
uint32_t transactionId, uint64_t appId,
- uint32_t appVersion,
+ uint32_t appVersion, uint32_t appFlags,
size_t totalBinaryLen) {
if (hasPendingLoadTransaction()) {
LOGW(
"Pending load transaction already exists. Overriding previous"
" transaction.");
}
+
mCurrentLoadInfo.hostClientId = hostClientId;
mCurrentLoadInfo.transactionId = transactionId;
mCurrentLoadInfo.nextFragmentId = 1;
@@ -36,7 +37,8 @@
if (mNanoapp.isNull()) {
LOG_OOM();
} else {
- success = mNanoapp->reserveBuffer(appId, appVersion, totalBinaryLen);
+ success =
+ mNanoapp->reserveBuffer(appId, appVersion, appFlags, totalBinaryLen);
}
if (!success) {
diff --git a/platform/shared/nanoapp_loader.cc b/platform/shared/nanoapp_loader.cc
new file mode 100644
index 0000000..cd31614
--- /dev/null
+++ b/platform/shared/nanoapp_loader.cc
@@ -0,0 +1,862 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include <cctype>
+#include <cmath>
+#include <cstring>
+
+#include "chre/platform/shared/nanoapp_loader.h"
+
+#include "ash.h"
+#include "chre.h"
+#include "chre/platform/assert.h"
+#include "chre/platform/shared/debug_dump.h"
+#include "chre/platform/shared/memory.h"
+#include "chre/target_platform/platform_cache_management.h"
+#include "chre/util/dynamic_vector.h"
+#include "chre/util/macros.h"
+
+#ifndef CHRE_LOADER_ARCH
+#define CHRE_LOADER_ARCH EM_ARM
+#endif // CHRE_LOADER_ARCH
+
+namespace chre {
+namespace {
+
+using ElfHeader = ElfW(Ehdr);
+using ProgramHeader = ElfW(Phdr);
+
+struct ExportedData {
+ void *data;
+ const char *dataName;
+};
+
+//! If non-null, a nanoapp is currently being loaded. This allows certain C
+//! functions to access the nanoapp if called during static init.
+NanoappLoader *gCurrentlyLoadingNanoapp = nullptr;
+//! Indicates whether a failure occurred during static initialization.
+bool gStaticInitFailure = false;
+
+// The new operator is used by singleton.h which causes the delete operator to
+// be undefined in nanoapp binaries even though it's unused. Define this in case
+// a nanoapp actually tries to use the operator.
+void deleteOverride(void *ptr) {
+ FATAL_ERROR("Nanoapp tried to free %p through delete operator", ptr);
+}
+
+// atexit is used to register functions that must be called when a binary is
+// removed from the system.
+int atexitOverride(void (*function)(void)) {
+ LOGV("atexit invoked with %p", function);
+ if (gCurrentlyLoadingNanoapp == nullptr) {
+ CHRE_ASSERT_LOG(false,
+ "atexit is only supported during static initialization.");
+ return -1;
+ }
+
+ gCurrentlyLoadingNanoapp->registerAtexitFunction(function);
+ return 0;
+}
+
+// The following functions from the cmath header need to be overridden, since
+// they're overloaded functions, and we need to specify explicit types of the
+// template for the compiler.
+double frexpOverride(double value, int *exp) {
+ return frexp(value, exp);
+}
+
+double sinOverride(double rad) {
+ return sin(rad);
+}
+
+double asinOverride(double val) {
+ return asin(val);
+}
+
+double cosOverride(double rad) {
+ return cos(rad);
+}
+
+float sqrtOverride(float val) {
+ return sqrt(val);
+}
+
+// This function is required to be exposed to nanoapps to handle errors from
+// invoking virtual functions.
+void __cxa_pure_virtual(void) {
+ chreAbort(CHRE_ERROR /* abortCode */);
+}
+
+#define ADD_EXPORTED_SYMBOL(function_name, function_string) \
+ { reinterpret_cast<void *>(function_name), function_string }
+#define ADD_EXPORTED_C_SYMBOL(function_name) \
+ ADD_EXPORTED_SYMBOL(function_name, STRINGIFY(function_name))
+
+// TODO(karthikmb/stange): While this array was hand-coded for simple
+// "hello-world" prototyping, the list of exported symbols must be
+// generated to minimize runtime errors and build breaks.
+const ExportedData gExportedData[] = {
+ /* libmath overrrides and symbols */
+ ADD_EXPORTED_SYMBOL(asinOverride, "asin"),
+ ADD_EXPORTED_SYMBOL(cosOverride, "cos"),
+ ADD_EXPORTED_SYMBOL(frexpOverride, "frexp"),
+ ADD_EXPORTED_SYMBOL(sinOverride, "sin"),
+ ADD_EXPORTED_SYMBOL(sqrtOverride, "sqrt"),
+ ADD_EXPORTED_C_SYMBOL(atan2f),
+ ADD_EXPORTED_C_SYMBOL(expf),
+ ADD_EXPORTED_C_SYMBOL(fmodf),
+ ADD_EXPORTED_C_SYMBOL(sqrtf),
+ ADD_EXPORTED_C_SYMBOL(tanhf),
+ /* libc overrides and symbols */
+ ADD_EXPORTED_C_SYMBOL(__cxa_pure_virtual),
+ ADD_EXPORTED_SYMBOL(atexitOverride, "atexit"),
+ ADD_EXPORTED_SYMBOL(deleteOverride, "_ZdlPv"),
+ ADD_EXPORTED_C_SYMBOL(dlsym),
+ ADD_EXPORTED_C_SYMBOL(memcmp),
+ ADD_EXPORTED_C_SYMBOL(memcpy),
+ ADD_EXPORTED_C_SYMBOL(memmove),
+ ADD_EXPORTED_C_SYMBOL(memset),
+ ADD_EXPORTED_C_SYMBOL(strcmp),
+ ADD_EXPORTED_C_SYMBOL(strlen),
+ ADD_EXPORTED_C_SYMBOL(strncmp),
+ ADD_EXPORTED_C_SYMBOL(tolower),
+ /* ash symbols */
+ ADD_EXPORTED_C_SYMBOL(ashLoadCalibrationParams),
+ ADD_EXPORTED_C_SYMBOL(ashSaveCalibrationParams),
+ ADD_EXPORTED_C_SYMBOL(ashSetCalibration),
+ /* CHRE symbols */
+ ADD_EXPORTED_C_SYMBOL(chreAbort),
+ ADD_EXPORTED_C_SYMBOL(chreAudioConfigureSource),
+ ADD_EXPORTED_C_SYMBOL(chreAudioGetSource),
+ ADD_EXPORTED_C_SYMBOL(chreConfigureDebugDumpEvent),
+ ADD_EXPORTED_C_SYMBOL(chreConfigureHostSleepStateEvents),
+ ADD_EXPORTED_C_SYMBOL(chreConfigureNanoappInfoEvents),
+ ADD_EXPORTED_C_SYMBOL(chreGetApiVersion),
+ ADD_EXPORTED_C_SYMBOL(chreGetAppId),
+ ADD_EXPORTED_C_SYMBOL(chreGetInstanceId),
+ ADD_EXPORTED_C_SYMBOL(chreGetEstimatedHostTimeOffset),
+ ADD_EXPORTED_C_SYMBOL(chreGetNanoappInfoByAppId),
+ ADD_EXPORTED_C_SYMBOL(chreGetPlatformId),
+ ADD_EXPORTED_C_SYMBOL(chreGetSensorInfo),
+ ADD_EXPORTED_C_SYMBOL(chreGetSensorSamplingStatus),
+ ADD_EXPORTED_C_SYMBOL(chreGetTime),
+ ADD_EXPORTED_C_SYMBOL(chreGnssGetCapabilities),
+ ADD_EXPORTED_C_SYMBOL(chreGnssLocationSessionStartAsync),
+ ADD_EXPORTED_C_SYMBOL(chreGnssLocationSessionStopAsync),
+ ADD_EXPORTED_C_SYMBOL(chreGnssMeasurementSessionStartAsync),
+ ADD_EXPORTED_C_SYMBOL(chreGnssMeasurementSessionStopAsync),
+ ADD_EXPORTED_C_SYMBOL(chreHeapAlloc),
+ ADD_EXPORTED_C_SYMBOL(chreHeapFree),
+ ADD_EXPORTED_C_SYMBOL(chreIsHostAwake),
+ ADD_EXPORTED_C_SYMBOL(chreLog),
+ ADD_EXPORTED_C_SYMBOL(chreSendEvent),
+ ADD_EXPORTED_C_SYMBOL(chreSendMessageToHostEndpoint),
+ ADD_EXPORTED_C_SYMBOL(chreSensorConfigure),
+ ADD_EXPORTED_C_SYMBOL(chreSensorFindDefault),
+ ADD_EXPORTED_C_SYMBOL(chreTimerCancel),
+ ADD_EXPORTED_C_SYMBOL(chreTimerSet),
+ ADD_EXPORTED_C_SYMBOL(chreWifiConfigureScanMonitorAsync),
+ ADD_EXPORTED_C_SYMBOL(chreWifiGetCapabilities),
+ ADD_EXPORTED_C_SYMBOL(chreWifiRequestScanAsync),
+ ADD_EXPORTED_C_SYMBOL(chreWwanGetCapabilities),
+ ADD_EXPORTED_C_SYMBOL(chreWwanGetCellInfoAsync),
+ ADD_EXPORTED_C_SYMBOL(platform_chreDebugDumpVaLog),
+};
+
+} // namespace
+
+void *NanoappLoader::create(void *elfInput, bool mapIntoTcm) {
+ void *instance = nullptr;
+ NanoappLoader *loader = memoryAllocDram<NanoappLoader>(elfInput, mapIntoTcm);
+ if (loader != nullptr) {
+ if (loader->open()) {
+ instance = loader;
+ } else {
+ memoryFreeDram(loader);
+ }
+ } else {
+ LOG_OOM();
+ }
+
+ return instance;
+}
+
+void NanoappLoader::destroy(NanoappLoader *loader) {
+ loader->close();
+ // TODO(b/151847750): Modify utilities to support free'ing from regions other
+ // than SRAM.
+ loader->~NanoappLoader();
+ memoryFreeDram(loader);
+}
+
+void *NanoappLoader::findExportedSymbol(const char *name) {
+ for (size_t i = 0; i < ARRAY_SIZE(gExportedData); i++) {
+ if (strncmp(name, gExportedData[i].dataName,
+ strlen(gExportedData[i].dataName)) == 0) {
+ return gExportedData[i].data;
+ }
+ }
+
+ LOGE("Unable to find %s", name);
+ return nullptr;
+}
+
+bool NanoappLoader::open() {
+ bool success = false;
+ if (mBinary.dataPtr != nullptr) {
+ if (!copyAndVerifyHeaders()) {
+ LOGE("Failed to verify headers");
+ } else if (!createMappings()) {
+ LOGE("Failed to create mappings");
+ } else if (!fixRelocations()) {
+ LOGE("Failed to fix relocations");
+ } else if (!resolveGot()) {
+ LOGE("Failed to resolve GOT");
+ } else {
+ // Wipe caches before calling init array to ensure initializers are not in
+ // the data cache.
+ wipeSystemCaches();
+ if (!callInitArray()) {
+ LOGE("Failed to perform static init");
+ } else {
+ success = true;
+ }
+ }
+ }
+
+ if (!success) {
+ freeAllocatedData();
+ }
+
+ return success;
+}
+
+void NanoappLoader::close() {
+ callAtexitFunctions();
+ callTerminatorArray();
+ freeAllocatedData();
+}
+
+void *NanoappLoader::findSymbolByName(const char *name) {
+ void *symbol = nullptr;
+ uint8_t *index = mSymbolTablePtr;
+ while (index < (mSymbolTablePtr + mSymbolTableSize)) {
+ ElfSym *currSym = reinterpret_cast<ElfSym *>(index);
+ const char *symbolName = &mStringTablePtr[currSym->st_name];
+
+ if (strncmp(symbolName, name, strlen(name)) == 0) {
+ symbol = reinterpret_cast<void *>(mMapping.data + currSym->st_value);
+ break;
+ }
+
+ index += sizeof(ElfSym);
+ }
+ return symbol;
+}
+
+void NanoappLoader::registerAtexitFunction(void (*function)(void)) {
+ if (!mAtexitFunctions.push_back(function)) {
+ LOG_OOM();
+ gStaticInitFailure = true;
+ }
+}
+
+void NanoappLoader::mapBss(const ProgramHeader *hdr) {
+ // if the memory size of this segment exceeds the file size zero fill the
+ // difference.
+ LOGV("Program Hdr mem sz: %zu file size: %zu", hdr->p_memsz, hdr->p_filesz);
+ if (hdr->p_memsz > hdr->p_filesz) {
+ ElfAddr endOfFile = hdr->p_vaddr + hdr->p_filesz + mLoadBias;
+ ElfAddr endOfMem = hdr->p_vaddr + hdr->p_memsz + mLoadBias;
+ if (endOfMem > endOfFile) {
+ auto deltaMem = endOfMem - endOfFile;
+ LOGV("Zeroing out %zu from page %p", deltaMem, endOfFile);
+ memset(reinterpret_cast<void *>(endOfFile), 0, deltaMem);
+ }
+ }
+}
+
+bool NanoappLoader::callInitArray() {
+ bool success = true;
+ // Sets global variable used by atexit in case it's invoked as part of
+ // initializing static data.
+ gCurrentlyLoadingNanoapp = this;
+
+ // TODO(b/151847750): ELF can have other sections like .init, .preinit, .fini
+ // etc. Be sure to look for those if they end up being something that should
+ // be supported for nanoapps.
+ for (size_t i = 0; i < mNumSectionHeaders; ++i) {
+ const char *name = getSectionHeaderName(mSectionHeadersPtr[i].sh_name);
+ if (strncmp(name, kInitArrayName, strlen(kInitArrayName)) == 0) {
+ LOGV("Invoking init function");
+ uintptr_t initArray = reinterpret_cast<uintptr_t>(
+ mLoadBias + mSectionHeadersPtr[i].sh_addr);
+ uintptr_t offset = 0;
+ while (offset < mSectionHeadersPtr[i].sh_size) {
+ ElfAddr *funcPtr = reinterpret_cast<ElfAddr *>(initArray + offset);
+ uintptr_t initFunction = reinterpret_cast<uintptr_t>(*funcPtr);
+ ((void (*)())initFunction)();
+ offset += sizeof(initFunction);
+ if (gStaticInitFailure) {
+ success = false;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ //! Reset global state so it doesn't leak into the next load.
+ gCurrentlyLoadingNanoapp = nullptr;
+ gStaticInitFailure = false;
+ return success;
+}
+
+uintptr_t NanoappLoader::roundDownToAlign(uintptr_t virtualAddr) {
+ return virtualAddr & -kBinaryAlignment;
+}
+
+void NanoappLoader::freeAllocatedData() {
+ if (mIsTcmBinary) {
+ memoryFree(mMapping.dataPtr);
+ } else {
+ memoryFreeDram(mMapping.dataPtr);
+ }
+ memoryFreeDram(mSectionHeadersPtr);
+ memoryFreeDram(mSectionNamesPtr);
+ memoryFreeDram(mSymbolTablePtr);
+ memoryFreeDram(mStringTablePtr);
+}
+
+bool NanoappLoader::verifyElfHeader() {
+ bool success = false;
+ ElfHeader *elfHeader = getElfHeader();
+ if (elfHeader != nullptr && (elfHeader->e_ident[EI_MAG0] == ELFMAG0) &&
+ (elfHeader->e_ident[EI_MAG1] == ELFMAG1) &&
+ (elfHeader->e_ident[EI_MAG2] == ELFMAG2) &&
+ (elfHeader->e_ident[EI_MAG3] == ELFMAG3) &&
+ (elfHeader->e_ehsize == sizeof(ElfHeader)) &&
+ (elfHeader->e_phentsize == sizeof(ProgramHeader)) &&
+ (elfHeader->e_shentsize == sizeof(SectionHeader)) &&
+ (elfHeader->e_shstrndx < elfHeader->e_shnum) &&
+ (elfHeader->e_version == EV_CURRENT) &&
+ (elfHeader->e_machine == CHRE_LOADER_ARCH) &&
+ (elfHeader->e_type == ET_DYN)) {
+ success = true;
+ }
+ return success;
+}
+
+bool NanoappLoader::verifyProgramHeaders() {
+ // This is a minimal check for now -
+ // there should be at least one load segment.
+ bool success = false;
+ for (size_t i = 0; i < getProgramHeaderArraySize(); ++i) {
+ if (getProgramHeaderArray()[i].p_type == PT_LOAD) {
+ success = true;
+ break;
+ }
+ }
+ return success;
+}
+
+const char *NanoappLoader::getSectionHeaderName(size_t headerOffset) {
+ if (headerOffset == 0) {
+ return "";
+ }
+
+ return &mSectionNamesPtr[headerOffset];
+}
+
+NanoappLoader::SectionHeader *NanoappLoader::getSectionHeader(
+ const char *headerName) {
+ SectionHeader *rv = nullptr;
+ for (size_t i = 0; i < mNumSectionHeaders; ++i) {
+ const char *name = getSectionHeaderName(mSectionHeadersPtr[i].sh_name);
+ if (strncmp(name, headerName, strlen(headerName)) == 0) {
+ rv = &mSectionHeadersPtr[i];
+ break;
+ }
+ }
+ return rv;
+}
+
+ElfHeader *NanoappLoader::getElfHeader() {
+ return reinterpret_cast<ElfHeader *>(mBinary.data);
+}
+
+ProgramHeader *NanoappLoader::getProgramHeaderArray() {
+ ElfHeader *elfHeader = getElfHeader();
+ ProgramHeader *programHeader = nullptr;
+ if (elfHeader != nullptr) {
+ programHeader =
+ reinterpret_cast<ProgramHeader *>(mBinary.data + elfHeader->e_phoff);
+ }
+
+ return programHeader;
+}
+
+size_t NanoappLoader::getProgramHeaderArraySize() {
+ ElfHeader *elfHeader = getElfHeader();
+ size_t arraySize = 0;
+ if (elfHeader != nullptr) {
+ arraySize = elfHeader->e_phnum;
+ }
+
+ return arraySize;
+}
+
+char *NanoappLoader::getDynamicStringTable() {
+ char *table = nullptr;
+
+ SectionHeader *dynamicStringTablePtr = getSectionHeader(".dynstr");
+ CHRE_ASSERT(dynamicStringTablePtr != nullptr);
+ if (dynamicStringTablePtr != nullptr && mBinary.dataPtr != nullptr) {
+ table = reinterpret_cast<char *>(mBinary.data +
+ dynamicStringTablePtr->sh_offset);
+ }
+
+ return table;
+}
+
+uint8_t *NanoappLoader::getDynamicSymbolTable() {
+ uint8_t *table = nullptr;
+
+ SectionHeader *dynamicSymbolTablePtr = getSectionHeader(".dynsym");
+ CHRE_ASSERT(dynamicSymbolTablePtr != nullptr);
+ if (dynamicSymbolTablePtr != nullptr && mBinary.dataPtr != nullptr) {
+ table = reinterpret_cast<uint8_t *>(mBinary.data +
+ dynamicSymbolTablePtr->sh_offset);
+ }
+
+ return table;
+}
+
+size_t NanoappLoader::getDynamicSymbolTableSize() {
+ size_t tableSize = 0;
+
+ SectionHeader *dynamicSymbolTablePtr = getSectionHeader(".dynsym");
+ CHRE_ASSERT(dynamicSymbolTablePtr != nullptr);
+ if (dynamicSymbolTablePtr != nullptr) {
+ tableSize = dynamicSymbolTablePtr->sh_size;
+ }
+
+ return tableSize;
+}
+
+bool NanoappLoader::verifySectionHeaders() {
+ bool foundSymbolTableHeader = false;
+ bool foundStringTableHeader = false;
+
+ for (size_t i = 0; i < mNumSectionHeaders; ++i) {
+ const char *name = getSectionHeaderName(mSectionHeadersPtr[i].sh_name);
+
+ if (strncmp(name, kSymTableName, strlen(kSymTableName)) == 0) {
+ foundSymbolTableHeader = true;
+ } else if (strncmp(name, kStrTableName, strlen(kStrTableName)) == 0) {
+ foundStringTableHeader = true;
+ }
+ }
+
+ return foundSymbolTableHeader && foundStringTableHeader;
+}
+
+bool NanoappLoader::copyAndVerifyHeaders() {
+ size_t offset = 0;
+ bool success = false;
+ uint8_t *pDataBytes = static_cast<uint8_t *>(mBinary.dataPtr);
+
+ // Verify the ELF Header
+ ElfHeader *elfHeader = getElfHeader();
+ success = verifyElfHeader();
+
+ LOGV("Verified ELF header %d", success);
+
+ // Verify Program Headers
+ if (success) {
+ success = verifyProgramHeaders();
+ }
+
+ LOGV("Verified Program headers %d", success);
+
+ // Load Section Headers
+ if (success) {
+ offset = elfHeader->e_shoff;
+ size_t sectionHeaderSizeBytes = sizeof(SectionHeader) * elfHeader->e_shnum;
+ mSectionHeadersPtr =
+ static_cast<SectionHeader *>(memoryAllocDram(sectionHeaderSizeBytes));
+ if (mSectionHeadersPtr == nullptr) {
+ success = false;
+ LOG_OOM();
+ } else {
+ memcpy(mSectionHeadersPtr, (pDataBytes + offset), sectionHeaderSizeBytes);
+ mNumSectionHeaders = elfHeader->e_shnum;
+ }
+ }
+
+ LOGV("Loaded section headers %d", success);
+
+ // Load section header names
+ if (success) {
+ SectionHeader &stringSection = mSectionHeadersPtr[elfHeader->e_shstrndx];
+ size_t sectionSize = stringSection.sh_size;
+ mSectionNamesPtr = static_cast<char *>(memoryAllocDram(sectionSize));
+ if (mSectionNamesPtr == nullptr) {
+ LOG_OOM();
+ success = false;
+ } else {
+ memcpy(mSectionNamesPtr,
+ reinterpret_cast<void *>(mBinary.data + stringSection.sh_offset),
+ sectionSize);
+ }
+ }
+
+ LOGV("Loaded section header names %d", success);
+
+ success = verifySectionHeaders();
+ LOGV("Verified Section headers %d", success);
+
+ // Load symbol table
+ if (success) {
+ SectionHeader *symbolTableHeader = getSectionHeader(kSymTableName);
+ mSymbolTableSize = symbolTableHeader->sh_size;
+ if (mSymbolTableSize == 0) {
+ LOGE("No symbols to resolve");
+ success = false;
+ } else {
+ mSymbolTablePtr =
+ static_cast<uint8_t *>(memoryAllocDram(mSymbolTableSize));
+ if (mSymbolTablePtr == nullptr) {
+ LOG_OOM();
+ success = false;
+ } else {
+ memcpy(mSymbolTablePtr,
+ reinterpret_cast<void *>(mBinary.data +
+ symbolTableHeader->sh_offset),
+ mSymbolTableSize);
+ }
+ }
+ }
+
+ LOGV("Loaded symbol table %d", success);
+
+ // Load string table
+ if (success) {
+ SectionHeader *stringTableHeader = getSectionHeader(kStrTableName);
+ size_t stringTableSize = stringTableHeader->sh_size;
+ if (mSymbolTableSize == 0) {
+ LOGE("No string table corresponding to symbols");
+ success = false;
+ } else {
+ mStringTablePtr = static_cast<char *>(memoryAllocDram(stringTableSize));
+ if (mStringTablePtr == nullptr) {
+ LOG_OOM();
+ success = false;
+ } else {
+ memcpy(mStringTablePtr,
+ reinterpret_cast<void *>(mBinary.data +
+ stringTableHeader->sh_offset),
+ stringTableSize);
+ }
+ }
+ }
+
+ LOGV("Loaded string table %d", success);
+
+ return success;
+}
+
+bool NanoappLoader::createMappings() {
+ // ELF needs pt_load segments to be in contiguous ascending order of
+ // virtual addresses. So the first and last segs can be used to
+ // calculate the entire address span of the image.
+ ElfHeader *elfHeader = getElfHeader();
+ ProgramHeader *programHeaderArray = getProgramHeaderArray();
+ size_t numProgramHeaders = getProgramHeaderArraySize();
+ const ProgramHeader *first = &programHeaderArray[0];
+ const ProgramHeader *last = &programHeaderArray[numProgramHeaders - 1];
+
+ // Find first load segment
+ while (first->p_type != PT_LOAD && first <= last) {
+ ++first;
+ }
+
+ bool success = false;
+ if (first->p_type != PT_LOAD) {
+ LOGE("Unable to find any load segments in the binary");
+ } else {
+ // Verify that the first load segment has a program header
+ // first byte of a valid load segment can't be greater than the
+ // program header offset
+ bool valid =
+ (first->p_offset < elfHeader->e_phoff) &&
+ (first->p_filesz >
+ (elfHeader->e_phoff + (numProgramHeaders * sizeof(ProgramHeader))));
+ if (!valid) {
+ LOGE("Load segment program header validation failed");
+ } else {
+ // Get the last load segment
+ while (last > first && last->p_type != PT_LOAD) --last;
+
+ size_t memorySpan = last->p_vaddr + last->p_memsz - first->p_vaddr;
+ LOGV("Nanoapp image Memory Span: %u", memorySpan);
+
+ if (mIsTcmBinary) {
+ mMapping.dataPtr = memoryAllocAligned(kBinaryAlignment, memorySpan);
+ } else {
+ mMapping.dataPtr = memoryAllocDramAligned(kBinaryAlignment, memorySpan);
+ }
+
+ if (mMapping.dataPtr == nullptr) {
+ LOG_OOM();
+ } else {
+ LOGV("Starting location of mappings %p", mMapping.dataPtr);
+
+ // Calculate the load bias using the first load segment.
+ uintptr_t adjustedFirstLoadSegAddr = roundDownToAlign(first->p_vaddr);
+ mLoadBias = mMapping.data - adjustedFirstLoadSegAddr;
+ LOGV("Load bias is %" PRIu32, mLoadBias);
+
+ success = true;
+ }
+ }
+ }
+
+ if (success) {
+ // Map the remaining segments
+ for (const ProgramHeader *ph = first; ph <= last; ++ph) {
+ if (ph->p_type == PT_LOAD) {
+ ElfAddr segStart = ph->p_vaddr + mLoadBias;
+ void *startPage = reinterpret_cast<void *>(roundDownToAlign(segStart));
+ ElfAddr phOffsetPage = roundDownToAlign(ph->p_offset);
+ void *binaryStartPage =
+ reinterpret_cast<void *>(mBinary.data + phOffsetPage);
+ size_t segmentLen = ph->p_filesz;
+
+ LOGV("Mapping start page %p from %p with length %zu", startPage,
+ binaryStartPage, segmentLen);
+ memcpy(startPage, binaryStartPage, segmentLen);
+ mapBss(ph);
+ } else {
+ LOGE("Non-load segment found between load segments");
+ success = false;
+ break;
+ }
+ }
+ }
+
+ return success;
+}
+
+const char *NanoappLoader::getDataName(size_t posInSymbolTable) {
+ size_t sectionSize = getDynamicSymbolTableSize();
+ uint8_t *dynamicSymbolTable = getDynamicSymbolTable();
+ size_t numElements = sectionSize / sizeof(ElfSym);
+ CHRE_ASSERT(posInSymbolTable < numElements);
+ char *dataName = nullptr;
+ if (posInSymbolTable < numElements) {
+ ElfSym *sym = reinterpret_cast<ElfSym *>(
+ &dynamicSymbolTable[posInSymbolTable * sizeof(ElfSym)]);
+ dataName = &getDynamicStringTable()[sym->st_name];
+ }
+ return dataName;
+}
+
+void *NanoappLoader::resolveData(size_t posInSymbolTable) {
+ const char *dataName = getDataName(posInSymbolTable);
+
+ if (dataName != nullptr) {
+ LOGV("Resolving %s", dataName);
+ return findExportedSymbol(dataName);
+ }
+
+ return nullptr;
+}
+
+NanoappLoader::DynamicHeader *NanoappLoader::getDynamicHeader() {
+ DynamicHeader *dyn = nullptr;
+ ProgramHeader *programHeaders = getProgramHeaderArray();
+ for (size_t i = 0; i < getProgramHeaderArraySize(); ++i) {
+ if (programHeaders[i].p_type == PT_DYNAMIC) {
+ dyn = reinterpret_cast<DynamicHeader *>(programHeaders[i].p_vaddr +
+ mLoadBias);
+ break;
+ }
+ }
+ return dyn;
+}
+
+NanoappLoader::ProgramHeader *NanoappLoader::getFirstRoSegHeader() {
+ // return the first read only segment found
+ ProgramHeader *ro = nullptr;
+ ProgramHeader *programHeaders = getProgramHeaderArray();
+ for (size_t i = 0; i < getProgramHeaderArraySize(); ++i) {
+ if (!(programHeaders[i].p_flags & PF_W)) {
+ ro = &programHeaders[i];
+ break;
+ }
+ }
+ return ro;
+}
+
+NanoappLoader::ElfWord NanoappLoader::getDynEntry(DynamicHeader *dyn,
+ int field) {
+ ElfWord rv = 0;
+
+ while (dyn->d_tag != DT_NULL) {
+ if (dyn->d_tag == field) {
+ rv = dyn->d_un.d_val;
+ break;
+ }
+ ++dyn;
+ }
+
+ return rv;
+}
+
+bool NanoappLoader::fixRelocations() {
+ ElfAddr *addr;
+ DynamicHeader *dyn = getDynamicHeader();
+ ProgramHeader *roSeg = getFirstRoSegHeader();
+
+ bool success = false;
+ if ((dyn == nullptr) || (roSeg == nullptr)) {
+ LOGE("Mandatory headers missing from shared object, aborting load");
+ } else if (getDynEntry(dyn, DT_RELA) != 0) {
+ LOGE("Elf binaries with a DT_RELA dynamic entry are unsupported");
+ } else {
+ ElfRel *reloc =
+ reinterpret_cast<ElfRel *>(getDynEntry(dyn, DT_REL) + mBinary.data);
+ size_t relocSize = getDynEntry(dyn, DT_RELSZ);
+ size_t nRelocs = relocSize / sizeof(ElfRel);
+ LOGV("Relocation %zu entries in DT_REL table", nRelocs);
+
+ size_t i;
+ for (i = 0; i < nRelocs; ++i) {
+ ElfRel *curr = &reloc[i];
+ int relocType = ELFW_R_TYPE(curr->r_info);
+ switch (relocType) {
+ case R_ARM_RELATIVE:
+ LOGV("Resolving ARM_RELATIVE at offset %" PRIx32, curr->r_offset);
+ addr = reinterpret_cast<ElfAddr *>(curr->r_offset + mMapping.data);
+ // TODO: When we move to DRAM allocations, we need to check if the
+ // above address is in a Read-Only section of memory, and give it
+ // temporary write permission if that is the case.
+ *addr += mMapping.data;
+ break;
+
+ case R_ARM_ABS32:
+ case R_ARM_GLOB_DAT: {
+ LOGV("Resolving type %d at offset %" PRIx32, relocType,
+ curr->r_offset);
+ addr = reinterpret_cast<ElfAddr *>(curr->r_offset + mMapping.data);
+ size_t posInSymbolTable = ELFW_R_SYM(curr->r_info);
+ void *resolved = resolveData(posInSymbolTable);
+ if (resolved == nullptr) {
+ LOGV("Failed to resolve global symbol(%d) at offset 0x%x", i,
+ curr->r_offset);
+ return false;
+ }
+ // TODO: When we move to DRAM allocations, we need to check if the
+ // above address is in a Read-Only section of memory, and give it
+ // temporary write permission if that is the case.
+ *addr = reinterpret_cast<ElfAddr>(resolved);
+ break;
+ }
+
+ case R_ARM_COPY:
+ LOGE("R_ARM_COPY is an invalid relocation for shared libraries");
+ break;
+ default:
+ LOGE("Invalid relocation type %u", relocType);
+ break;
+ }
+ }
+
+ if (i != nRelocs) {
+ LOGE("Unable to resolve all symbols in the binary");
+ } else {
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool NanoappLoader::resolveGot() {
+ ElfAddr *addr;
+ ElfRel *reloc = reinterpret_cast<ElfRel *>(
+ getDynEntry(getDynamicHeader(), DT_JMPREL) + mMapping.data);
+ size_t relocSize = getDynEntry(getDynamicHeader(), DT_PLTRELSZ);
+ size_t nRelocs = relocSize / sizeof(ElfRel);
+ LOGV("Resolving GOT with %zu relocations", nRelocs);
+
+ for (size_t i = 0; i < nRelocs; ++i) {
+ ElfRel *curr = &reloc[i];
+ int relocType = ELFW_R_TYPE(curr->r_info);
+
+ switch (relocType) {
+ case R_ARM_JUMP_SLOT: {
+ LOGV("Resolving ARM_JUMP_SLOT at offset %" PRIx32, curr->r_offset);
+ addr = reinterpret_cast<ElfAddr *>(curr->r_offset + mMapping.data);
+ size_t posInSymbolTable = ELFW_R_SYM(curr->r_info);
+ void *resolved = resolveData(posInSymbolTable);
+ if (resolved == nullptr) {
+ LOGV("Failed to resolve symbol(%d) at offset 0x%x", i,
+ curr->r_offset);
+ return false;
+ }
+ *addr = reinterpret_cast<ElfAddr>(resolved);
+ break;
+ }
+
+ default:
+ LOGE("Unsupported relocation type: %u for symbol %s", relocType,
+ getDataName(ELFW_R_SYM(curr->r_info)));
+ return false;
+ }
+ }
+ return true;
+}
+
+void NanoappLoader::callAtexitFunctions() {
+ while (!mAtexitFunctions.empty()) {
+ LOGV("Calling atexit at %p", mAtexitFunctions.back());
+ mAtexitFunctions.back()();
+ mAtexitFunctions.pop_back();
+ }
+}
+
+void NanoappLoader::callTerminatorArray() {
+ for (size_t i = 0; i < mNumSectionHeaders; ++i) {
+ const char *name = getSectionHeaderName(mSectionHeadersPtr[i].sh_name);
+ if (strncmp(name, kFiniArrayName, strlen(kFiniArrayName)) == 0) {
+ uintptr_t finiArray = reinterpret_cast<uintptr_t>(
+ mLoadBias + mSectionHeadersPtr[i].sh_addr);
+ uintptr_t offset = 0;
+ while (offset < mSectionHeadersPtr[i].sh_size) {
+ ElfAddr *funcPtr = reinterpret_cast<ElfAddr *>(finiArray + offset);
+ uintptr_t finiFunction = reinterpret_cast<uintptr_t>(*funcPtr);
+ ((void (*)())finiFunction)();
+ offset += sizeof(finiFunction);
+ }
+ break;
+ }
+ }
+}
+
+} // namespace chre
diff --git a/platform/slpi/host_link.cc b/platform/slpi/host_link.cc
index 990f02d..ed2f6b8 100644
--- a/platform/slpi/host_link.cc
+++ b/platform/slpi/host_link.cc
@@ -514,6 +514,7 @@
* @param transactionId the ID of the transaction
* @param appId the ID of the app to load
* @param appVersion the version of the app to load
+ * @param appFlags The flags provided by the app being loaded
* @param targetApiVersion the API version this nanoapp is targeted for
* @param buffer the nanoapp binary data. May be only part of the nanoapp's
* binary if it's being sent over multiple fragments
@@ -524,20 +525,23 @@
* @return A valid pointer to a nanoapp that can be loaded into the system. A
* nullptr if the preparation process fails.
*/
-UniquePtr<Nanoapp> handleLoadNanoappData(
- uint16_t hostClientId, uint32_t transactionId, uint64_t appId,
- uint32_t appVersion, uint32_t targetApiVersion, const void *buffer,
- size_t bufferLen, uint32_t fragmentId, size_t appBinaryLen) {
+UniquePtr<Nanoapp> handleLoadNanoappData(uint16_t hostClientId,
+ uint32_t transactionId, uint64_t appId,
+ uint32_t appVersion, uint32_t appFlags,
+ uint32_t targetApiVersion,
+ const void *buffer, size_t bufferLen,
+ uint32_t fragmentId,
+ size_t appBinaryLen) {
static NanoappLoadManager sLoadManager;
bool success = true;
if (fragmentId == 0 || fragmentId == 1) { // first fragment
size_t totalAppBinaryLen = (fragmentId == 0) ? bufferLen : appBinaryLen;
LOGD("Load nanoapp request for app ID 0x%016" PRIx64 " ver 0x%" PRIx32
- " target API 0x%08" PRIx32 " size %zu (txnId %" PRIu32
- " client %" PRIu16 ")",
- appId, appVersion, targetApiVersion, totalAppBinaryLen, transactionId,
- hostClientId);
+ " flags 0x%" PRIx32 " target API 0x%08" PRIx32
+ " size %zu (txnId %" PRIu32 " client %" PRIu16 ")",
+ appId, appVersion, appFlags, targetApiVersion, totalAppBinaryLen,
+ transactionId, hostClientId);
if (sLoadManager.hasPendingLoadTransaction()) {
FragmentedLoadInfo info = sLoadManager.getTransactionInfo();
@@ -546,8 +550,9 @@
sLoadManager.markFailure();
}
- success = sLoadManager.prepareForLoad(hostClientId, transactionId, appId,
- appVersion, totalAppBinaryLen);
+ success =
+ sLoadManager.prepareForLoad(hostClientId, transactionId, appId,
+ appVersion, appFlags, totalAppBinaryLen);
}
success &= sLoadManager.copyNanoappFragment(
hostClientId, transactionId, (fragmentId == 0) ? 1 : fragmentId, buffer,
@@ -844,18 +849,18 @@
void HostMessageHandlers::handleLoadNanoappRequest(
uint16_t hostClientId, uint32_t transactionId, uint64_t appId,
- uint32_t appVersion, uint32_t targetApiVersion, const void *buffer,
- size_t bufferLen, const char *appFileName, uint32_t fragmentId,
- size_t appBinaryLen) {
+ uint32_t appVersion, uint32_t appFlags, uint32_t targetApiVersion,
+ const void *buffer, size_t bufferLen, const char *appFileName,
+ uint32_t fragmentId, size_t appBinaryLen) {
UniquePtr<Nanoapp> pendingNanoapp;
if (appFileName != nullptr) {
pendingNanoapp =
handleLoadNanoappFile(hostClientId, transactionId, appId, appVersion,
targetApiVersion, appFileName);
} else {
- pendingNanoapp = handleLoadNanoappData(hostClientId, transactionId, appId,
- appVersion, targetApiVersion, buffer,
- bufferLen, fragmentId, appBinaryLen);
+ pendingNanoapp = handleLoadNanoappData(
+ hostClientId, transactionId, appId, appVersion, appFlags,
+ targetApiVersion, buffer, bufferLen, fragmentId, appBinaryLen);
}
if (!pendingNanoapp.isNull()) {
diff --git a/platform/slpi/include/chre/target_platform/platform_nanoapp_base.h b/platform/slpi/include/chre/target_platform/platform_nanoapp_base.h
index 72aeb61..0dab266 100644
--- a/platform/slpi/include/chre/target_platform/platform_nanoapp_base.h
+++ b/platform/slpi/include/chre/target_platform/platform_nanoapp_base.h
@@ -48,11 +48,13 @@
*
* @param appId The unique app identifier associated with this binary
* @param appVersion An application-defined version number
+ * @param appFlags The flags provided by the app being loaded
* @param appBinaryLen Size of appBinary, in bytes
*
* @return true if the allocation was successful, false otherwise
*/
- bool reserveBuffer(uint64_t appId, uint32_t appVersion, size_t appBinarylen);
+ bool reserveBuffer(uint64_t appId, uint32_t appVersion, uint32_t appFlags,
+ size_t appBinarylen);
/**
* Copies the (possibly fragmented) application binary data into the allocated
diff --git a/platform/slpi/platform_nanoapp.cc b/platform/slpi/platform_nanoapp.cc
index 10d68f1..3ac54df 100644
--- a/platform/slpi/platform_nanoapp.cc
+++ b/platform/slpi/platform_nanoapp.cc
@@ -207,6 +207,7 @@
}
bool PlatformNanoappBase::reserveBuffer(uint64_t appId, uint32_t appVersion,
+ uint32_t /* appFlags */,
size_t appBinaryLen) {
CHRE_ASSERT(!isLoaded());
bool success = false;
diff --git a/platform/usf/include/chre/platform/usf/usf_helper.h b/platform/usf/include/chre/platform/usf/usf_helper.h
new file mode 100644
index 0000000..98fa6a2
--- /dev/null
+++ b/platform/usf/include/chre/platform/usf/usf_helper.h
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_USF_USF_HELPER_H_
+#define CHRE_PLATFORM_USF_USF_HELPER_H_
+
+#include "chre/core/sensor_type.h"
+#include "chre/platform/system_time.h"
+#include "chre/util/dynamic_vector.h"
+#include "chre/util/unique_ptr.h"
+
+/*
+ * Include USF FlatBuffers services header before including other USF
+ * FlatBuffers headers.
+ */
+#include "usf/usf_flatbuffers.h"
+
+#include "usf/error.h"
+#include "usf/fbs/usf_msg_sample_root_generated.h"
+#include "usf/reffed_ptr.h"
+#include "usf/usf_power.h"
+#include "usf/usf_sensor.h"
+#include "usf/usf_sensor_report.h"
+#include "usf/usf_sensor_req.h"
+#include "usf/usf_transport_client.h"
+
+namespace chre {
+
+struct SensorInfo {
+ //! Used to fill in the chreSensorDataHeader field. If the user of the helper
+ //! doesn't care about this field, then it can be set to 0.
+ uint32_t sensorHandle;
+
+ //! Sampling ID corresponding to the active subscription to this sensor, if
+ //! one exists. 0 otherwise.
+ uint32_t samplingId;
+};
+
+//! A callback interface for receiving UsfHelper data events and to allow the
+//! UsfHelper to ask for information.
+class UsfHelperCallbackInterface {
+ public:
+ //! Invoked from the USF worker thread to provide sensor data events. The
+ //! event data format is one of the chreSensorXXXData defined in the CHRE API,
+ //! implicitly specified by sensorType.
+ virtual void onSensorDataEvent(uint8_t sensorType,
+ UniquePtr<uint8_t> &&eventData) = 0;
+
+ /**
+ * Invoked by the USF Helper to obtain sensor info for a given sensor type.
+ *
+ * @param sensorType The sensor type for which info should be populated
+ * @param sensorInfo Non-null pointer that will be populated with sensor info
+ * if this method is successful
+ * @return true if all sensor info was obtained.
+ */
+ virtual bool getSensorInfo(uint8_t sensorType, SensorInfo *sensorInfo) = 0;
+
+ //! Invoked from the USF worker thread to provide a sensor sampling status
+ //! update. All fields are guaranteed to be valid.
+ virtual void onSamplingStatusUpdate(
+ uint8_t sensorType, const usf::UsfSensorSamplingEvent *update) = 0;
+
+ //! Invoked from the USF worker thread to indicate that flushing the given
+ //! sensor has completed.
+ virtual void onFlushComplete(usf::UsfErr err, uint32_t requestId,
+ void *cookie) = 0;
+
+ //! Invoked from the USF worker thread to provide a bias update.
+ virtual void onBiasUpdate(
+ uint8_t sensorType,
+ UniquePtr<struct chreSensorThreeAxisData> &&eventData) = 0;
+
+ //! Invoked from the USF worker thread to provide an host wake suspend update.
+ virtual void onHostWakeSuspendEvent(bool awake) = 0;
+};
+
+//! Class that allows clients of UsfHelper that only want to listen for sensor
+//! events to implement only functions that relate to those events.
+class SensorEventCallbackInterface : public UsfHelperCallbackInterface {
+ public:
+ void onSamplingStatusUpdate(
+ uint8_t /*sensorType*/,
+ const usf::UsfSensorSamplingEvent * /*update*/) override {}
+
+ void onFlushComplete(usf::UsfErr /*err*/, uint32_t /*requestId*/,
+ void * /*cookie*/) override {}
+
+ void onBiasUpdate(
+ uint8_t /*sensorType*/,
+ UniquePtr<struct chreSensorThreeAxisData> && /*eventData*/) override {}
+
+ void onHostWakeSuspendEvent(bool /*awake*/) override {}
+};
+
+//! Default timeout to wait for the USF transport client to return a response
+//! to a request
+constexpr Nanoseconds kDefaultUsfWaitTimeout = Seconds(5);
+
+/**
+ * Helper class used to abstract away most details of communicating with USF.
+ */
+class UsfHelper {
+ public:
+ ~UsfHelper();
+
+ //! Maps USF error codes to CHRE error codes.
+ static uint8_t usfErrorToChreError(usf::UsfErr err);
+
+ /**
+ * Initializes connection to USF.
+ *
+ * @param callback USF helper callback.
+ * @param worker USF worker pointer. If null, a new worker is created.
+ */
+ void init(UsfHelperCallbackInterface *callback,
+ usf::UsfWorker *worker = nullptr);
+
+ /**
+ * Retrieves the list of sensors available from USF.
+ *
+ * @param sensorList Non-null pointer to a UsfVector that will be populated
+ * with all the available sensors from USF.
+ * @return true if the list of sensors was retrieved successfully.
+ */
+ bool getSensorList(
+ usf::UsfVector<refcount::reffed_ptr<usf::UsfSensor>> *sensorList);
+
+ /**
+ * Starts sampling sensor data from the given sensor.
+ *
+ * @param request The various parameters needed to issue the request to the
+ * sensor.
+ * @param samplingId Non-null pointer that will be filled in with a valid
+ * sampling ID if the request is issued successfully.
+ * @return true if the sensor request was issued successfully.
+ */
+ bool startSampling(usf::UsfStartSamplingReq *request, uint32_t *samplingId);
+
+ /**
+ * Reconfigures an existing sensor request.
+ *
+ * @param request The various parameters needed to issue the request to the
+ * sensor.
+ * @return true if the sensor request was reconfigured successfully.
+ */
+ bool reconfigureSampling(usf::UsfReconfigSamplingReq *request);
+
+ /**
+ * Stops an active sensor request.
+ *
+ * @param request The various parameters needed to issue the request to the
+ * sensor.
+ * @return true if the sensor request was stopped successfully.
+ */
+ bool stopSampling(usf::UsfStopSamplingReq *request);
+
+ /**
+ * Registers the helper for sensor sampling status updates from USF for the
+ * given sensor.
+ *
+ * @param sensor The sensor to listen to sampling status updates from.
+ * @return true if registration was successful.
+ */
+ bool registerForStatusUpdates(refcount::reffed_ptr<usf::UsfSensor> &sensor);
+
+ /**
+ * Registers the helper for bias updates from USF for the given sensor.
+ * This method is not idempotent meaning a call to unregisterForBiasUpdates
+ * must be made for the same sensor prior to another call to this method.
+ *
+ * @param sensor A non-null reffed_ptr to the sensor to listen for bias
+ * updates from.
+ * @return true if registration was successful.
+ */
+ bool registerForBiasUpdates(
+ const refcount::reffed_ptr<usf::UsfSensor> &sensor);
+
+ /**
+ * Registers the helper for AP power state update from USF.
+ *
+ * @return true if successful.
+ */
+ bool registerForApPowerStateUpdates();
+
+ /**
+ * Unregisters the helper from bias updates from USF for the given sensor.
+ * If not registered, this method is a no-op.
+ *
+ * @param sensor A non-null reffed_ptr to the sensor to stop listening to bias
+ * updates from.
+ */
+ void unregisterForBiasUpdates(
+ const refcount::reffed_ptr<usf::UsfSensor> &sensor);
+
+ /**
+ * Obtains the latest three axis bias received from the given sensor, if
+ * available.
+ *
+ * @param sensor The USF sensor to obtain the latest bias values from.
+ * @param bias A non-null pointer that will be filled with the latest bias
+ * values if available.
+ * @return true if a bias value was available for the given sensor.
+ */
+ bool getThreeAxisBias(const refcount::reffed_ptr<usf::UsfSensor> &sensor,
+ struct chreSensorThreeAxisData *bias) const;
+
+ /**
+ * Flushes sensor data from the provided sensor asynchronously. Once all data
+ * has been flushed, {@link #onFlushCompleteEvent} will be invoked on the
+ * callback the helper was initialized with.
+ *
+ * @param usfSensorHandle The USF handle corresponding to the sensor to flush
+ * @param cookie A cookie that will be delivered when the flush has completed
+ * @param requestId The ID associated with this flush request, if it was
+ * made successfully
+ * @return True if the flush request was successfully sent.
+ */
+ bool flushAsync(const usf::UsfServerHandle usfSensorHandle, void *cookie,
+ uint32_t *requestId);
+
+ /**
+ * Used to process sensor reports delivered through a listener registered with
+ * USF.
+ *
+ * @param event Pointer containing a valid sensor report
+ */
+ void processSensorReport(const usf::UsfMsgEvent *event);
+
+ /**
+ * Used to process a sensor sampling status update delivered through a
+ * listener registered with USF.
+ *
+ * @param update Update containing the latest state from USF.
+ */
+ void processStatusUpdate(const usf::UsfSensorSamplingEvent *update);
+
+ /**
+ * Process the bias update delivered through a listener registered with USF.
+ *
+ * @param update Update containing latest bias information for a sensor.
+ */
+ void processBiasUpdate(const usf::UsfSensorTransformConfigEvent *update);
+
+ /**
+ * Process the AP power state update delivered through a listener registered
+ * with USF.
+ *
+ * @param update Update containing the current AP power state.
+ */
+ void processApPowerStateUpdate(const usf::UsfApPowerStateEvent *update);
+
+ private:
+ /**
+ * Sends a synchronous request to USF with any returned data stored in the
+ * given callback.
+ *
+ * @param req Request that should be issued to USF
+ * @param callback Helper callback function used to perform the synchronous
+ * request and contains any response message after the method returns
+ * @return true if no errors were encountered issuing the request
+ */
+ bool sendUsfReqSync(usf::UsfReq *req, usf::UsfReqSyncCallback *callback);
+
+ /**
+ * Retrieves the response message from the synchronous callback. The memory
+ * associated with the message is owned by the callback itself and will be
+ * freed upon callback going out of scope.
+ *
+ * @param callback Synchronous callback previously used in a successful
+ * sendUsfReqSync invocation
+ * @param respMsg Variable containing the response message if this method
+ * succeeds
+ * @return true if the response message was decoded and verified successfully
+ * and respMsg is valid.
+ */
+ template <class T>
+ bool getRespMsg(usf::UsfReqSyncCallback &callback, const T **respMsg);
+
+ /**
+ * Creates a CHRE sensor event from a USF sensor event
+ *
+ * @param sampleReport Valid decoded USF sensor sample report
+ * @param sensorType CHRE sensor type corresponding to the event
+ * @param sensorSample Upon success, populated with a valid CHRE sensor event
+ * @return true if the USF sensor event corresponds to a valid, active
+ * sampling request and event creation was successful
+ */
+ bool createSensorEvent(const usf::UsfSensorSampleReport *sampleReport,
+ uint8_t sensorType, UniquePtr<uint8_t> &sensorSample);
+
+ /**
+ * Converts a USF bias update event into the chreSensorThreeAxisData format.
+ *
+ * @param update USF bias update that needs to be converted.
+ * @return If successful, contains a populated UniquePtr. Otherwise, the
+ * UniquePtr will be set to nullptr.
+ */
+ UniquePtr<struct chreSensorThreeAxisData> convertUsfBiasUpdateToData(
+ const usf::UsfSensorTransformConfigEvent *update);
+
+ /**
+ * Get the index into the calibrated sensor data array for the given sensor
+ * type.
+ *
+ * @param usfSensorType The USF sensor type to obtain the index for.
+ * @return The index into the data array for the given sensor type. If the
+ * sensor type isn't present in the array, kNumUsfCalSensors is returned.
+ */
+ static size_t getCalArrayIndex(const usf::UsfSensorType usfSensorType);
+
+ //! A struct to store a sensor's calibration data.
+ struct UsfCalData {
+ uint64_t timestamp;
+ float bias[3];
+ bool hasBias;
+ uint8_t accuracy;
+ };
+
+ //! The list of calibrated USF sensors supported.
+ enum class UsfCalSensor : size_t {
+ AccelCal,
+ GyroCal,
+ MagCal,
+ NumCalSensors,
+ };
+
+ static constexpr size_t kNumUsfCalSensors =
+ static_cast<size_t>(UsfCalSensor::NumCalSensors);
+
+ //! Cal data of all the cal sensors.
+ UsfCalData mCalData[kNumUsfCalSensors] = {};
+
+ //! Client used to send messages to USF
+ refcount::reffed_ptr<usf::UsfTransportClient> mTransportClient;
+
+ //! UsfWorker used to dispatch messages to CHRE on its own thread
+ refcount::reffed_ptr<usf::UsfWorker> mWorker;
+
+ //! All registered USF event listeners.
+ DynamicVector<usf::UsfEventListener *> mUsfEventListeners;
+
+ //! Handle to the USF sensor manager
+ usf::UsfServerHandle mSensorMgrHandle;
+
+ //! Currently registered callback
+ UsfHelperCallbackInterface *mCallback = nullptr;
+
+ //! The error encountered during initialization, if any.
+ usf::UsfErr mInitError = usf::UsfErr::kErrNone;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_USF_USF_HELPER_H_
diff --git a/platform/usf/include/chre/target_platform/platform_sensor_base.h b/platform/usf/include/chre/target_platform/platform_sensor_base.h
new file mode 100644
index 0000000..a209d36
--- /dev/null
+++ b/platform/usf/include/chre/target_platform/platform_sensor_base.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_USF_PLATFORM_SENSOR_BASE_H_
+#define CHRE_PLATFORM_USF_PLATFORM_SENSOR_BASE_H_
+
+#include "chre/util/optional.h"
+
+#include "usf/usf.h"
+#include "usf/usf_sensor.h"
+
+namespace chre {
+
+//! The max length of sensorName
+constexpr size_t kSensorNameMaxLen = 64;
+
+class PlatformSensorBase {
+ public:
+ /**
+ * Initializes various members of PlatformSensorBase.
+ */
+ void initBase(refcount::reffed_ptr<usf::UsfSensor> &usfSensor,
+ uint8_t sensorType);
+
+ usf::UsfServerHandle getServerHandle() const {
+ return mUsfSensor->GetHandle();
+ }
+
+ bool isSamplingIdValid() const {
+ return mSamplingId.has_value();
+ }
+
+ void setSamplingIdInvalid() {
+ mSamplingId.reset();
+ }
+
+ void setSamplingId(uint32_t samplingId) {
+ mSamplingId = samplingId;
+ }
+
+ uint32_t getSamplingId() const {
+ return mSamplingId.value();
+ }
+
+ const refcount::reffed_ptr<usf::UsfSensor> &getUsfSensor() const {
+ return mUsfSensor;
+ }
+
+ protected:
+ //! The USF sensor instance for this sensor.
+ refcount::reffed_ptr<usf::UsfSensor> mUsfSensor;
+
+ //! The sampling ID of the active request with this sensor.
+ Optional<uint32_t> mSamplingId;
+
+ //! Sensor type of this sensor. Needed because USF shares the same sensor
+ //! type for two different CHRE sensors (ACCELEROMETER_TEMPERATURE and
+ //! GYROSCOPE_TEMPERATURE).
+ uint8_t mSensorType;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_USF_PLATFORM_SENSOR_BASE_H_
diff --git a/platform/usf/include/chre/target_platform/platform_sensor_manager_base.h b/platform/usf/include/chre/target_platform/platform_sensor_manager_base.h
new file mode 100644
index 0000000..24b882a
--- /dev/null
+++ b/platform/usf/include/chre/target_platform/platform_sensor_manager_base.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_PLATFORM_USF_PLATFORM_SENSOR_MANAGER_BASE_H_
+#define CHRE_PLATFORM_USF_PLATFORM_SENSOR_MANAGER_BASE_H_
+
+#include "chre/platform/usf/usf_helper.h"
+#include "chre/util/unique_ptr.h"
+
+namespace chre {
+
+class PlatformSensorManagerBase : public UsfHelperCallbackInterface {
+ public:
+ void onSensorDataEvent(uint8_t sensorType,
+ UniquePtr<uint8_t> &&eventData) override;
+
+ void onSamplingStatusUpdate(
+ uint8_t sensorType, const usf::UsfSensorSamplingEvent *update) override;
+
+ bool getSensorInfo(uint8_t sensorType, SensorInfo *sensorInfo) override;
+
+ void onFlushComplete(usf::UsfErr err, uint32_t requestId,
+ void *cookie) override;
+
+ void onBiasUpdate(
+ uint8_t sensorType,
+ UniquePtr<struct chreSensorThreeAxisData> &&eventData) override;
+
+ void onHostWakeSuspendEvent(bool awake) override;
+
+ /**
+ * Converts the given UsfSensor into one or more CHRE sensors and adds them
+ * to the given dynamic vector.
+ *
+ * @param usfSensor UsfSensor to be converted to one or more CHRE sensors
+ * @param chreSensors Dynamic vector that converted sensors must be added to
+ */
+ void addSensorsWithInfo(refcount::reffed_ptr<usf::UsfSensor> &usfSensor,
+ DynamicVector<Sensor> *chreSensors);
+
+ protected:
+ //! Helper used to communicate with USF.
+ UsfHelper mHelper;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_USF_PLATFORM_SENSOR_MANAGER_BASE_H_
diff --git a/platform/usf/include/chre/target_platform/platform_sensor_type_helpers_base.h b/platform/usf/include/chre/target_platform/platform_sensor_type_helpers_base.h
new file mode 100644
index 0000000..f762706
--- /dev/null
+++ b/platform/usf/include/chre/target_platform/platform_sensor_type_helpers_base.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_TARGET_PLATFORM_SENSOR_TYPE_HELPERS_BASE_H_
+#define CHRE_TARGET_PLATFORM_SENSOR_TYPE_HELPERS_BASE_H_
+
+#include "usf/usf_sensor_defs.h"
+
+namespace chre {
+
+/**
+ * This SensorSampleType is designed to help classify sensor's data type in
+ * event handling.
+ */
+enum class SensorSampleType {
+ Byte,
+ Float,
+ Uint64,
+ Occurrence,
+ ThreeAxis,
+ Unknown,
+};
+
+/**
+ * Can be used to expose static methods to the PlatformSensorTypeHelpers class
+ * for use in working with vendor sensor types. Currently, this is stubbed out
+ * in the AOC implementation.
+ */
+class PlatformSensorTypeHelpersBase {
+ public:
+ /**
+ * Maps a sensorType to its SensorSampleType.
+ *
+ * @param sensorType The type of the sensor to obtain its SensorSampleType
+ * for.
+ * @return The SensorSampleType of the sensorType.
+ */
+ static SensorSampleType getSensorSampleTypeFromSensorType(uint8_t sensorType);
+
+ /**
+ * @return Whether the given sensor type reports bias events.
+ */
+ static bool reportsBias(uint8_t sensorType);
+
+ //! Helper functions used to convert between USF and CHRE types
+ static usf::UsfSensorReportingMode getUsfReportingMode(ReportingMode mode);
+ static bool convertUsfToChreSensorType(usf::UsfSensorType usfSensorType,
+ uint8_t *chreSensorType);
+ static uint8_t convertUsfToChreSampleAccuracy(
+ usf::UsfSampleAccuracy usfSampleAccuracy);
+};
+
+} // namespace chre
+
+#endif // CHRE_TARGET_PLATFORM_SENSOR_TYPE_HELPERS_BASE_H_
diff --git a/platform/usf/memory.cc b/platform/usf/memory.cc
new file mode 100644
index 0000000..13ad629
--- /dev/null
+++ b/platform/usf/memory.cc
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#include "usf/usf_mem.h"
+
+namespace chre {
+
+void *memoryAlloc(size_t size) {
+ return usf::UsfAlloc(usf::kUsfHeapLowPower, size);
+}
+
+void memoryFree(void *pointer) {
+ usf::UsfFree(pointer);
+}
+
+} // namespace chre
diff --git a/platform/usf/platform_sensor.cc b/platform/usf/platform_sensor.cc
new file mode 100644
index 0000000..e4a310c
--- /dev/null
+++ b/platform/usf/platform_sensor.cc
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/platform_sensor.h"
+
+#include "chre/core/sensor_type_helpers.h"
+
+namespace chre {
+
+void PlatformSensorBase::initBase(
+ refcount::reffed_ptr<usf::UsfSensor> &usfSensor, uint8_t sensorType) {
+ mUsfSensor = usfSensor;
+ mSensorType = sensorType;
+}
+
+uint8_t PlatformSensor::getSensorType() const {
+ return mSensorType;
+}
+
+uint64_t PlatformSensor::getMinInterval() const {
+ return SensorTypeHelpers::isOneShot(mSensorType)
+ ? CHRE_SENSOR_INTERVAL_DEFAULT
+ : mUsfSensor->GetMinDelayNs();
+ ;
+}
+
+bool PlatformSensor::reportsBiasEvents() const {
+ return PlatformSensorTypeHelpersBase::reportsBias(getSensorType());
+}
+
+bool PlatformSensor::supportsPassiveMode() const {
+ return true;
+}
+
+const char *PlatformSensor::getSensorName() const {
+ return SensorTypeHelpers::getSensorTypeName(getSensorType());
+}
+
+PlatformSensor::PlatformSensor(PlatformSensor &&other) {
+ *this = std::move(other);
+}
+
+PlatformSensor &PlatformSensor::operator=(PlatformSensor &&other) {
+ // Note: if this implementation is ever changed to depend on "this" containing
+ // initialized values, the move constructor implementation must be updated.
+ mUsfSensor = other.mUsfSensor;
+ mSensorType = other.mSensorType;
+
+ return *this;
+}
+
+} // namespace chre
diff --git a/platform/usf/platform_sensor_manager.cc b/platform/usf/platform_sensor_manager.cc
new file mode 100644
index 0000000..1b35b39
--- /dev/null
+++ b/platform/usf/platform_sensor_manager.cc
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/platform_sensor_manager.h"
+
+#include "chre/core/event_loop_manager.h"
+#include "chre/util/nested_data_ptr.h"
+
+namespace chre {
+namespace {
+
+void addSensor(refcount::reffed_ptr<usf::UsfSensor> &usfSensor,
+ uint8_t sensorType, DynamicVector<Sensor> *chreSensors) {
+ if (!chreSensors->emplace_back()) {
+ FATAL_ERROR("Failed to allocate new sensor");
+ }
+
+ // The sensor base class must be initialized before the main Sensor init()
+ // can be invoked as init() is allowed to invoke base class methods.
+ chreSensors->back().initBase(usfSensor, sensorType);
+ chreSensors->back().init();
+
+ const Sensor &newSensor = chreSensors->back();
+ LOGI("%s, type %" PRIu8 ", minInterval %" PRIu64 " handle %" PRId32,
+ newSensor.getSensorName(), newSensor.getSensorType(),
+ newSensor.getMinInterval(), newSensor.getServerHandle());
+}
+
+ReportingMode getReportingMode(Sensor &sensor) {
+ if (sensor.isOneShot()) {
+ return ReportingMode::OneShot;
+ } else if (sensor.isOnChange()) {
+ return ReportingMode::OnChange;
+ } else {
+ return ReportingMode::Continuous;
+ }
+}
+
+void handleMissingSensor() {
+ // Try rebooting if a sensor is missing, which might help recover from a
+ // transient failure/race condition at startup. But to avoid endless crashes,
+ // only do this within 45 seconds of boot time - we rely on knowledge that
+ // getMonotonicTime() only resets when the device reboots and not from an SSR.
+#ifndef CHRE_LOG_ONLY_NO_SENSOR
+ if (SystemTime::getMonotonicTime() < (kDefaultUsfWaitTimeout + Seconds(15))) {
+ FATAL_ERROR("Missing required sensor(s)");
+ } else
+#endif
+ {
+ LOGE("Missing required sensor(s)");
+ }
+}
+
+} // namespace
+
+PlatformSensorManager::~PlatformSensorManager() {}
+
+void PlatformSensorManager::init() {
+ mHelper.init(this);
+ if (!mHelper.registerForApPowerStateUpdates()) {
+ LOGE("Failed to register for AP power state updates.");
+ }
+}
+
+DynamicVector<Sensor> PlatformSensorManager::getSensors() {
+ DynamicVector<Sensor> sensors;
+
+ usf::UsfVector<refcount::reffed_ptr<usf::UsfSensor>> sensorList;
+ if (mHelper.getSensorList(&sensorList)) {
+ for (size_t i = 0; i < sensorList.size(); i++) {
+ refcount::reffed_ptr<usf::UsfSensor> &sensor = sensorList[i];
+ addSensorsWithInfo(sensor, &sensors);
+ }
+ }
+
+ // TODO: Determine how many sensors are expected to be available through USF.
+ if (sensors.empty()) {
+ handleMissingSensor();
+ }
+
+ return sensors;
+}
+
+bool PlatformSensorManager::configureSensor(Sensor &sensor,
+ const SensorRequest &request) {
+ bool success = false;
+
+ if (request.getMode() == SensorMode::Off && !sensor.isSamplingIdValid()) {
+ // If no existing sampling ID exists and the sensor should be turned off,
+ // return true. This can happen when the core platform tries to remove a
+ // request for a one-shot sensor after it fires which automatically happens
+ // inside USF.
+ success = true;
+ } else if (request.getMode() == SensorMode::Off) {
+ usf::UsfStopSamplingReq usfReq;
+ usfReq.SetReqType(usf::UsfMsgReqType_STOP_SAMPLING);
+ usfReq.SetServerHandle(sensor.getServerHandle());
+ usfReq.SetSamplingId(sensor.getSamplingId());
+
+ if (mHelper.stopSampling(&usfReq)) {
+ sensor.setSamplingIdInvalid();
+ success = true;
+ }
+ } else if (sensor.isSamplingIdValid()) {
+ // TODO: See if a reconfigure request can be the same as reissuing a start
+ // request.
+ usf::UsfReconfigSamplingReq usfReq;
+ usfReq.SetReqType(usf::UsfMsgReqType_RECONFIG_SAMPLING);
+ usfReq.SetServerHandle(sensor.getServerHandle());
+ usfReq.SetSamplingId(sensor.getSamplingId());
+ usfReq.SetPeriodNs(request.getInterval().toRawNanoseconds());
+ usfReq.SetMaxLatencyNs(
+ sensor.isContinuous() ? request.getLatency().toRawNanoseconds() : 0);
+
+ success = mHelper.reconfigureSampling(&usfReq);
+ } else {
+ usf::UsfStartSamplingReq usfReq;
+ usfReq.SetReqType(usf::UsfMsgReqType_START_SAMPLING);
+ usfReq.SetServerHandle(sensor.getServerHandle());
+ // TODO(147438885): Make setting the reporting mode of a sensor optional
+ usfReq.SetReportingMode(PlatformSensorTypeHelpersBase::getUsfReportingMode(
+ getReportingMode(sensor)));
+ usfReq.SetPeriodNs(request.getInterval().toRawNanoseconds());
+ usfReq.SetMaxLatencyNs(
+ sensor.isContinuous() ? request.getLatency().toRawNanoseconds() : 0);
+ usfReq.SetPassive(sensorModeIsPassive(request.getMode()));
+ usf::UsfSensorTransformLevel transformLevel =
+ sensor.isCalibrated() ? usf::kUsfSensorTransformLevelAll
+ : usf::kUsfSensorTransformEnvCompensation;
+ usfReq.SetTransformLevel(transformLevel);
+
+ uint32_t samplingId;
+ if (mHelper.startSampling(&usfReq, &samplingId)) {
+ sensor.setSamplingId(samplingId);
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool PlatformSensorManager::configureBiasEvents(const Sensor &sensor,
+ bool enable,
+ uint64_t /* latencyNs */) {
+ bool success = true;
+ if (enable) {
+ success = mHelper.registerForBiasUpdates(sensor.getUsfSensor());
+ } else {
+ // Unconditional success for unregistering
+ mHelper.unregisterForBiasUpdates(sensor.getUsfSensor());
+ }
+
+ return success;
+}
+
+bool PlatformSensorManager::getThreeAxisBias(
+ const Sensor &sensor, struct chreSensorThreeAxisData *bias) const {
+ bool success = sensor.reportsBiasEvents();
+ if (success) {
+ if (!mHelper.getThreeAxisBias(sensor.getUsfSensor(), bias)) {
+ // Set to zero bias + unknown accuracy per CHRE API requirements.
+ memset(bias, 0, sizeof(chreSensorThreeAxisData));
+ bias->header.readingCount = 1;
+ bias->header.accuracy = CHRE_SENSOR_ACCURACY_UNKNOWN;
+ }
+
+ // Overwrite sensorHandle to match the request type.
+ getSensorRequestManager().getSensorHandle(sensor.getSensorType(),
+ &bias->header.sensorHandle);
+ }
+ return success;
+}
+
+bool PlatformSensorManager::flush(const Sensor &sensor,
+ uint32_t *flushRequestId) {
+ NestedDataPtr<uint32_t> cookie(sensor.getSensorType());
+ return mHelper.flushAsync(sensor.getServerHandle(), cookie.dataPtr,
+ flushRequestId);
+}
+
+void PlatformSensorManager::releaseSamplingStatusUpdate(
+ struct chreSensorSamplingStatus *status) {
+ memoryFree(status);
+}
+
+void PlatformSensorManager::releaseSensorDataEvent(void *data) {
+ memoryFree(data);
+}
+
+void PlatformSensorManager::releaseBiasEvent(void *biasData) {
+ memoryFree(biasData);
+}
+
+void PlatformSensorManagerBase::onSamplingStatusUpdate(
+ uint8_t sensorType, const usf::UsfSensorSamplingEvent *update) {
+ uint32_t sensorHandle;
+ if (getSensorRequestManager().getSensorHandle(sensorType, &sensorHandle)) {
+ // Memory will be freed via releaseSamplingStatusUpdate once core framework
+ // performs its updates.
+ auto statusUpdate = MakeUniqueZeroFill<struct chreSensorSamplingStatus>();
+ if (statusUpdate.isNull()) {
+ LOG_OOM();
+ } else {
+ statusUpdate->interval = update->GetPeriodNs();
+ statusUpdate->latency = update->GetMaxLatencyNs();
+ statusUpdate->enabled = update->GetActive();
+ getSensorRequestManager().handleSamplingStatusUpdate(
+ sensorHandle, statusUpdate.release());
+ }
+ }
+}
+
+bool PlatformSensorManagerBase::getSensorInfo(uint8_t sensorType,
+ SensorInfo *sensorInfo) {
+ bool success = false;
+
+ if (getSensorRequestManager().getSensorHandle(sensorType,
+ &sensorInfo->sensorHandle)) {
+ Sensor *sensor =
+ getSensorRequestManager().getSensor(sensorInfo->sensorHandle);
+ if (sensor != nullptr && sensor->isSamplingIdValid()) {
+ success = true;
+ sensorInfo->samplingId = sensor->getSamplingId();
+ } else {
+ sensorInfo->sensorHandle = 0;
+ }
+ }
+ return success;
+}
+
+void PlatformSensorManagerBase::onSensorDataEvent(
+ uint8_t sensorType, UniquePtr<uint8_t> &&eventData) {
+ uint32_t sensorHandle;
+ if (getSensorRequestManager().getSensorHandle(sensorType, &sensorHandle)) {
+ Sensor *sensor = getSensorRequestManager().getSensor(sensorHandle);
+ // Invalidate the sampling ID for one shot sensors after they've fired since
+ // USF automatically removes the sampling ID from its list.
+ if (sensor->isOneShot()) {
+ sensor->setSamplingIdInvalid();
+ }
+
+ getSensorRequestManager().handleSensorDataEvent(sensorHandle,
+ eventData.release());
+ }
+}
+
+void PlatformSensorManagerBase::onFlushComplete(usf::UsfErr err,
+ uint32_t requestId,
+ void *cookie) {
+ NestedDataPtr<uint32_t> nestedSensorType;
+ nestedSensorType.dataPtr = cookie;
+
+ uint32_t sensorHandle;
+ if (getSensorRequestManager().getSensorHandle(nestedSensorType.data,
+ &sensorHandle)) {
+ getSensorRequestManager().handleFlushCompleteEvent(
+ sensorHandle, requestId, UsfHelper::usfErrorToChreError(err));
+ }
+}
+
+void PlatformSensorManagerBase::onBiasUpdate(
+ uint8_t sensorType, UniquePtr<struct chreSensorThreeAxisData> &&eventData) {
+ uint32_t sensorHandle;
+ if (getSensorRequestManager().getSensorHandle(sensorType, &sensorHandle)) {
+ eventData->header.sensorHandle = sensorHandle;
+ getSensorRequestManager().handleBiasEvent(sensorHandle,
+ eventData.release());
+ }
+}
+
+void PlatformSensorManagerBase::onHostWakeSuspendEvent(bool awake) {
+ EventLoopManagerSingleton::get()
+ ->getEventLoop()
+ .getPowerControlManager()
+ .onHostWakeSuspendEvent(awake);
+}
+
+void PlatformSensorManagerBase::addSensorsWithInfo(
+ refcount::reffed_ptr<usf::UsfSensor> &usfSensor,
+ DynamicVector<Sensor> *chreSensors) {
+ uint8_t sensorType;
+ if (PlatformSensorTypeHelpersBase::convertUsfToChreSensorType(
+ usfSensor->GetType(), &sensorType)) {
+ // Only register for a USF sensor once. If it maps to multiple sensors,
+ // code down the line will handle sending multiple updates.
+ if (!mHelper.registerForStatusUpdates(usfSensor)) {
+ LOGE("Failed to register for status updates for %s",
+ usfSensor->GetName());
+ }
+
+ addSensor(usfSensor, sensorType, chreSensors);
+
+ // USF shares the same sensor type for calibrated and uncalibrated sensors
+ // so populate a calibrated / uncalibrated sensor for known calibrated
+ // sensor types
+ uint8_t uncalibratedType =
+ SensorTypeHelpers::toUncalibratedSensorType(sensorType);
+ if (uncalibratedType != sensorType) {
+ addSensor(usfSensor, uncalibratedType, chreSensors);
+ }
+
+ // USF shares a sensor type for both gyro and accel temp.
+ if (sensorType == CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE) {
+ addSensor(usfSensor, CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE,
+ chreSensors);
+ }
+ }
+}
+
+} // namespace chre
diff --git a/platform/usf/platform_sensor_type_helpers.cc b/platform/usf/platform_sensor_type_helpers.cc
new file mode 100644
index 0000000..aa68c2c
--- /dev/null
+++ b/platform/usf/platform_sensor_type_helpers.cc
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+#include "chre/platform/platform_sensor_type_helpers.h"
+#include "chre/target_platform/assert.h"
+#include "chre/util/macros.h"
+
+#ifdef CHREX_SENSOR_SUPPORT
+#include "chre/extensions/platform/vendor_sensor_types.h"
+#endif // CHREX_SENSOR_SUPPORT
+
+namespace chre {
+
+ReportingMode PlatformSensorTypeHelpers::getVendorSensorReportingMode(
+ uint8_t /* sensorType */) {
+ // TODO: Stubbed out, implement this
+ return ReportingMode::Continuous;
+}
+
+bool PlatformSensorTypeHelpers::getVendorSensorIsCalibrated(
+ uint8_t /* sensorType */) {
+ // TODO: Stubbed out, implement this
+ return false;
+}
+
+bool PlatformSensorTypeHelpers::getVendorSensorBiasEventType(
+ uint8_t /* sensorType */, uint16_t * /* eventType */) {
+ // TODO: Stubbed out, implement this
+ return false;
+}
+
+const char *PlatformSensorTypeHelpers::getVendorSensorTypeName(
+ uint8_t sensorType) {
+#ifdef CHREX_SENSOR_SUPPORT
+ return extension::vendorSensorTypeName(sensorType);
+#else
+ UNUSED_VAR(sensorType);
+ return "";
+#endif
+}
+
+size_t PlatformSensorTypeHelpers::getVendorSensorLastEventSize(
+ uint8_t /* sensorType */) {
+ // TODO: Stubbed out, implement this
+ return 0;
+}
+
+void PlatformSensorTypeHelpers::getVendorLastSample(
+ uint8_t /* sensorType */, const ChreSensorData * /* event */,
+ ChreSensorData * /* lastEvent */) {
+ // TODO: Stubbed out, implement this
+}
+
+bool PlatformSensorTypeHelpersBase::reportsBias(uint8_t sensorType) {
+ switch (sensorType) {
+ case CHRE_SENSOR_TYPE_ACCELEROMETER:
+ case CHRE_SENSOR_TYPE_GYROSCOPE:
+ case CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD:
+ case CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER:
+ case CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE:
+ case CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+SensorSampleType
+PlatformSensorTypeHelpersBase::getSensorSampleTypeFromSensorType(
+ uint8_t sensorType) {
+ switch (sensorType) {
+ case CHRE_SENSOR_TYPE_ACCELEROMETER:
+ case CHRE_SENSOR_TYPE_GYROSCOPE:
+ case CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD:
+ case CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER:
+ case CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE:
+ case CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD:
+ return SensorSampleType::ThreeAxis;
+ case CHRE_SENSOR_TYPE_PRESSURE:
+ case CHRE_SENSOR_TYPE_LIGHT:
+ case CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE:
+ case CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE:
+ case CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE:
+ return SensorSampleType::Float;
+ case CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT:
+ case CHRE_SENSOR_TYPE_STATIONARY_DETECT:
+ case CHRE_SENSOR_TYPE_STEP_DETECT:
+ return SensorSampleType::Occurrence;
+ case CHRE_SENSOR_TYPE_PROXIMITY:
+ return SensorSampleType::Byte;
+ case CHRE_SENSOR_TYPE_STEP_COUNTER:
+ return SensorSampleType::Uint64;
+ default:
+#ifdef CHREX_SENSOR_SUPPORT
+ return extension::vendorSensorSampleTypeFromSensorType(sensorType);
+#else
+ // Update implementation to prevent undefined from being used.
+ CHRE_ASSERT(false);
+ return SensorSampleType::Unknown;
+#endif
+ }
+}
+
+usf::UsfSensorReportingMode PlatformSensorTypeHelpersBase::getUsfReportingMode(
+ ReportingMode mode) {
+ if (mode == ReportingMode::OnChange) {
+ return usf::kUsfSensorReportOnChange;
+ } else if (mode == ReportingMode::OneShot) {
+ return usf::kUsfSensorReportOneShot;
+ } else {
+ return usf::kUsfSensorReportContinuous;
+ }
+}
+
+bool PlatformSensorTypeHelpersBase::convertUsfToChreSensorType(
+ usf::UsfSensorType usfSensorType, uint8_t *chreSensorType) {
+ bool success = true;
+ switch (usfSensorType) {
+ case usf::UsfSensorType::kUsfSensorAccelerometer:
+ *chreSensorType = CHRE_SENSOR_TYPE_ACCELEROMETER;
+ break;
+ case usf::UsfSensorType::kUsfSensorGyroscope:
+ *chreSensorType = CHRE_SENSOR_TYPE_GYROSCOPE;
+ break;
+ case usf::UsfSensorType::kUsfSensorProx:
+ *chreSensorType = CHRE_SENSOR_TYPE_PROXIMITY;
+ break;
+ case usf::UsfSensorType::kUsfSensorBaro:
+ *chreSensorType = CHRE_SENSOR_TYPE_PRESSURE;
+ break;
+ case usf::UsfSensorType::kUsfSensorMag:
+ *chreSensorType = CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD;
+ break;
+ case usf::UsfSensorType::kUsfSensorMagTemp:
+ *chreSensorType = CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE;
+ break;
+ case usf::UsfSensorType::kUsfSensorImuTemp:
+ *chreSensorType = CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE;
+ break;
+ case usf::UsfSensorType::kUsfSensorAmbientLight:
+ *chreSensorType = CHRE_SENSOR_TYPE_LIGHT;
+ break;
+ case usf::UsfSensorType::kUsfSensorMotionDetect:
+ *chreSensorType = CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT;
+ break;
+ case usf::UsfSensorType::kUsfSensorStationaryDetect:
+ *chreSensorType = CHRE_SENSOR_TYPE_STATIONARY_DETECT;
+ break;
+ case usf::UsfSensorType::kUsfSensorStepDetector:
+ *chreSensorType = CHRE_SENSOR_TYPE_STEP_DETECT;
+ break;
+ case usf::UsfSensorType::kUsfSensorStepCounter:
+ *chreSensorType = CHRE_SENSOR_TYPE_STEP_COUNTER;
+ break;
+ default:
+#ifdef CHREX_SENSOR_SUPPORT
+ success = extension::vendorConvertUsfToChreSensorType(usfSensorType,
+ chreSensorType);
+#else
+ // Don't print anything as USF exposes sensor types CHRE doesn't care
+ // about (e.g. Camera Vsync, and color)
+ success = false;
+#endif
+ break;
+ }
+ return success;
+}
+
+uint8_t PlatformSensorTypeHelpersBase::convertUsfToChreSampleAccuracy(
+ usf::UsfSampleAccuracy usfSampleAccuracy) {
+ switch (usfSampleAccuracy) {
+ case usf::kUsfSampleAccuracyUnreliable:
+ return CHRE_SENSOR_ACCURACY_UNRELIABLE;
+ case usf::kUsfSampleAccuracyLow:
+ return CHRE_SENSOR_ACCURACY_LOW;
+ case usf::kUsfSampleAccuracyMedium:
+ return CHRE_SENSOR_ACCURACY_MEDIUM;
+ case usf::kUsfSampleAccuracyHigh:
+ return CHRE_SENSOR_ACCURACY_HIGH;
+ case usf::kUsfSampleAccuracyUnknown:
+ default:
+ return CHRE_SENSOR_ACCURACY_UNKNOWN;
+ }
+}
+
+} // namespace chre
diff --git a/platform/usf/usf_helper.cc b/platform/usf/usf_helper.cc
new file mode 100644
index 0000000..f3336d4
--- /dev/null
+++ b/platform/usf/usf_helper.cc
@@ -0,0 +1,706 @@
+/*
+ * 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.
+ */
+
+#include "chre/platform/usf/usf_helper.h"
+
+#include <cinttypes>
+
+#include "chre/core/sensor_type_helpers.h"
+#include "chre/platform/fatal_error.h"
+#include "chre/util/macros.h"
+
+#include "usf/fbs/usf_msg_event_root_generated.h"
+#include "usf/fbs/usf_msg_sample_batch_root_generated.h"
+#include "usf/fbs/usf_msg_sensor_info_resp_root_generated.h"
+#include "usf/fbs/usf_msg_sensor_list_resp_root_generated.h"
+#include "usf/fbs/usf_msg_start_sampling_resp_root_generated.h"
+#include "usf/usf.h"
+#include "usf/usf_device.h"
+#include "usf/usf_flatbuffers.h"
+#include "usf/usf_sensor_defs.h"
+#include "usf/usf_sensor_mgr.h"
+#include "usf/usf_time.h"
+#include "usf/usf_work.h"
+
+using usf::UsfErr;
+using usf::UsfErr::kErrNone;
+
+#define LOG_USF_ERR(x) LOGE("USF failure %" PRIu8 ": line %d", x, __LINE__)
+
+namespace chre {
+namespace {
+
+//! Callback data delivered to async callback from USF. This struct must be
+//! freed using memoryFree.
+struct AsyncCallbackData {
+ UsfHelperCallbackInterface *callback;
+ void *cookie;
+};
+
+void eventHandler(void *context, usf::UsfEvent *event) {
+ const usf::UsfMsgEvent *msgEvent =
+ static_cast<usf::UsfTransportMsgEvent *>(event)->GetMsgEvent();
+
+ if (msgEvent != nullptr) {
+ auto *mgr = static_cast<UsfHelper *>(context);
+
+ switch (msgEvent->event_type()) {
+ case usf::UsfMsgEventType_kSensorReport:
+ mgr->processSensorReport(msgEvent);
+ break;
+ default:
+ LOGD("Received unknown event type %" PRIu8, msgEvent->event_type());
+ break;
+ }
+ }
+}
+
+void statusUpdateHandler(void *context, usf::UsfEvent *event) {
+ const usf::UsfSensorSamplingEvent *updateEvent =
+ static_cast<usf::UsfSensorSamplingEvent *>(event);
+
+ if (context != nullptr && updateEvent != nullptr) {
+ auto *mgr = static_cast<UsfHelper *>(context);
+ mgr->processStatusUpdate(updateEvent);
+ }
+}
+
+void biasUpdateHandler(void *context, usf::UsfEvent *event) {
+ const auto *updateEvent =
+ static_cast<usf::UsfSensorTransformConfigEvent *>(event);
+
+ if (context != nullptr && updateEvent != nullptr) {
+ auto *mgr = static_cast<UsfHelper *>(context);
+ mgr->processBiasUpdate(updateEvent);
+ }
+}
+
+void apPowerStateUpdateHandler(void *context, usf::UsfEvent *event) {
+ const auto *updateEvent = static_cast<usf::UsfApPowerStateEvent *>(event);
+
+ if (context != nullptr && updateEvent != nullptr) {
+ auto *mgr = static_cast<UsfHelper *>(context);
+ mgr->processApPowerStateUpdate(updateEvent);
+ }
+}
+
+void asyncCallback(usf::UsfReq *req, const usf::UsfResp *resp, void *data) {
+ auto callbackData = static_cast<AsyncCallbackData *>(data);
+ callbackData->callback->onFlushComplete(resp->GetErr(), req->GetReqId(),
+ callbackData->cookie);
+
+ memoryFree(data);
+ memoryFree(req);
+}
+
+void *allocateEvent(uint8_t sensorType, size_t numSamples) {
+ SensorSampleType sampleType =
+ PlatformSensorTypeHelpers::getSensorSampleTypeFromSensorType(sensorType);
+ size_t sampleSize = 0;
+ switch (sampleType) {
+ case SensorSampleType::ThreeAxis:
+ sampleSize =
+ sizeof(chreSensorThreeAxisData::chreSensorThreeAxisSampleData);
+ break;
+ case SensorSampleType::Float:
+ sampleSize = sizeof(chreSensorFloatData::chreSensorFloatSampleData);
+ break;
+ case SensorSampleType::Byte:
+ sampleSize = sizeof(chreSensorByteData::chreSensorByteSampleData);
+ break;
+ case SensorSampleType::Occurrence:
+ sampleSize =
+ sizeof(chreSensorOccurrenceData::chreSensorOccurrenceSampleData);
+ break;
+ default:
+ LOGE("Unhandled SensorSampleType for SensorType %" PRIu8,
+ static_cast<uint8_t>(sensorType));
+ sampleType = SensorSampleType::Unknown;
+ }
+
+ size_t memorySize =
+ (sampleType == SensorSampleType::Unknown)
+ ? 0
+ : (sizeof(chreSensorDataHeader) + numSamples * sampleSize);
+ void *event = (memorySize == 0) ? nullptr : memoryAlloc(memorySize);
+
+ if (event == nullptr && memorySize != 0) {
+ LOG_OOM();
+ }
+ return event;
+}
+
+/**
+ * Allocates the memory and sets up the chreSensorDataHeader used to deliver
+ * the sensor samples to nanoapps.
+ *
+ * @param numSamples The number of samples there should be space for in the
+ * sensor event
+ * @param sensorHandle The handle to the sensor that produced the events
+ * @param sensor The CHRE sensor instance for the sensor that produced the
+ * events
+ * @param sensorSamples A non-null UniquePtr used to hold the sensor samples
+ * @return If successful, sensorSamples will point to valid memory that
+ * contains a valid chreSensorDataHeader and enough space to store all
+ * sensor samples
+ */
+bool prepareSensorEvent(size_t numSamples, uint32_t sensorHandle,
+ uint8_t sensorType, UniquePtr<uint8_t> &sensorSamples) {
+ bool success = false;
+
+ UniquePtr<uint8_t> buf(
+ static_cast<uint8_t *>(allocateEvent(sensorType, numSamples)));
+ sensorSamples = std::move(buf);
+
+ if (!sensorSamples.isNull()) {
+ success = true;
+
+ auto *header =
+ reinterpret_cast<chreSensorDataHeader *>(sensorSamples.get());
+ header->sensorHandle = sensorHandle;
+ header->readingCount = numSamples;
+ header->reserved = 0;
+ }
+
+ return success;
+}
+
+/**
+ * Populates CHRE sensor sample structure using USF sensor samples.
+ *
+ * @param sampleReport The USF report that contains sensor samples
+ * @param sensor The CHRE sensor instance for the sensor that produced the
+ * events
+ * @param sensorSample Reference to a UniquePtr instance that points to non-null
+ * memory that will be populated with sensor samples
+ */
+void populateSensorEvent(const usf::UsfSensorSampleReport *sampleReport,
+ uint8_t sensorType, UniquePtr<uint8_t> &sensorSample) {
+ uint64_t prevSampleTimeNs = 0;
+
+ for (int i = 0; i < sampleReport->GetSampleCount(); i++) {
+ usf::UsfSampleEvent sampleEvent;
+ UsfErr err = sampleReport->GetSample(i, &sampleEvent);
+ if (err != kErrNone) {
+ LOG_USF_ERR(err);
+ continue;
+ }
+
+ SensorSampleType sampleType =
+ PlatformSensorTypeHelpers::getSensorSampleTypeFromSensorType(
+ sensorType);
+
+ uint32_t *timestampDelta = nullptr;
+ switch (sampleType) {
+ case SensorSampleType::ThreeAxis: {
+ auto *event =
+ reinterpret_cast<chreSensorThreeAxisData *>(sensorSample.get());
+
+ // TODO(b/143139477): Apply calibration to data from these sensors
+ for (size_t valIndex = 0; valIndex < 3; valIndex++) {
+ event->readings[i].values[valIndex] = sampleEvent.data[valIndex];
+ }
+ timestampDelta = &event->readings[i].timestampDelta;
+ break;
+ }
+ case SensorSampleType::Float: {
+ auto *event =
+ reinterpret_cast<chreSensorFloatData *>(sensorSample.get());
+ event->readings[i].value = sampleEvent.data[0];
+ timestampDelta = &event->readings[i].timestampDelta;
+ break;
+ }
+ case SensorSampleType::Byte: {
+ auto *event =
+ reinterpret_cast<chreSensorByteData *>(sensorSample.get());
+ event->readings[i].value = 0;
+ event->readings[i].isNear = (sampleEvent.data[0] > 0.5f);
+ timestampDelta = &event->readings[i].timestampDelta;
+ break;
+ }
+ case SensorSampleType::Occurrence: {
+ // Occurrence samples don't store any readings
+ auto *event =
+ reinterpret_cast<chreSensorOccurrenceData *>(sensorSample.get());
+ timestampDelta = &event->readings[0].timestampDelta;
+ break;
+ }
+ default:
+ LOGE("Invalid sample type %" PRIu8, static_cast<uint8_t>(sampleType));
+ }
+
+ // First sample determines the base timestamp and accuracy for all other
+ // sensor samples
+ if (i == 0) {
+ auto *header =
+ reinterpret_cast<chreSensorDataHeader *>(sensorSample.get());
+ header->baseTimestamp = sampleEvent.timestamp_ns;
+ if (timestampDelta != nullptr) {
+ *timestampDelta = 0;
+ }
+ auto usfAccuracy = sampleEvent.accuracy;
+ header->accuracy =
+ PlatformSensorTypeHelpersBase::convertUsfToChreSampleAccuracy(
+ usfAccuracy);
+
+ } else {
+ uint64_t delta = sampleEvent.timestamp_ns - prevSampleTimeNs;
+ if (delta > UINT32_MAX) {
+ LOGE("Sensor %" PRIu8 " timestampDelta overflow: prev %" PRIu64
+ " curr %" PRIu64,
+ sensorType, prevSampleTimeNs, sampleEvent.timestamp_ns);
+ delta = UINT32_MAX;
+ }
+ *timestampDelta = static_cast<uint32_t>(delta);
+ }
+
+ prevSampleTimeNs = sampleEvent.timestamp_ns;
+ }
+}
+
+} // namespace
+
+UsfHelper::~UsfHelper() {
+ for (usf::UsfEventListener *listener : mUsfEventListeners) {
+ listener->event_type->RemoveListener(listener);
+ }
+
+ if (mWorker.get() != nullptr) {
+ mWorker->Stop();
+ mWorker.reset();
+ }
+
+ if (mTransportClient.get() != nullptr) {
+ mTransportClient->Disconnect();
+ mTransportClient.reset();
+ }
+}
+
+uint8_t UsfHelper::usfErrorToChreError(UsfErr err) {
+ switch (err) {
+ case UsfErr::kErrNone:
+ return CHRE_ERROR_NONE;
+ case UsfErr::kErrNotSupported:
+ return CHRE_ERROR_NOT_SUPPORTED;
+ case UsfErr::kErrMemAlloc:
+ return CHRE_ERROR_NO_MEMORY;
+ case UsfErr::kErrNotAvailable:
+ return CHRE_ERROR_BUSY;
+ case UsfErr::kErrTimedOut:
+ return CHRE_ERROR_TIMEOUT;
+ default:
+ return CHRE_ERROR;
+ }
+}
+
+void UsfHelper::init(UsfHelperCallbackInterface *callback,
+ usf::UsfWorker *worker) {
+ CHRE_ASSERT(callback != nullptr);
+
+ usf::UsfDeviceMgr::GetDeviceProbeCompletePrecondition()->Wait();
+
+ UsfErr err = UsfErr::kErrAllocation;
+ if (worker != nullptr) {
+ mWorker.reset(worker, refcount::RefTakingMode::kCreate);
+ err = kErrNone;
+ } else {
+ err = usf::UsfWorkMgr::CreateWorker(&mWorker);
+ }
+
+ if (callback == nullptr) {
+ err = UsfErr::kErrBadValue;
+ } else if (!mUsfEventListeners.emplace_back()) {
+ LOG_OOM();
+ } else if ((err != kErrNone) ||
+ ((err = usf::UsfTransportClient::Create(
+ &mTransportClient, usf::kUsfHeapLowPower)) != kErrNone) ||
+ ((err = mTransportClient->Connect()) != kErrNone) ||
+ ((err = mTransportClient->GetMsgEventType()->AddListener(
+ mWorker.get(), eventHandler, this,
+ &mUsfEventListeners.back())) != kErrNone) ||
+ ((err = usf::UsfClientGetServer(mTransportClient.get(),
+ &usf::kUsfSensorMgrServerUuid,
+ &mSensorMgrHandle)) != kErrNone)) {
+ LOG_USF_ERR(err);
+ // TODO(b/143139477): Debate removing the error capture if it proves to be
+ // unneeded in ramdump analysis.
+ mInitError = err;
+ } else {
+ mCallback = callback;
+ }
+
+ if (err != kErrNone) {
+ FATAL_ERROR("Failed to initialize UsfHelper: %" PRIu8, err);
+ }
+}
+
+bool UsfHelper::getSensorList(
+ usf::UsfVector<refcount::reffed_ptr<usf::UsfSensor>> *sensorList) {
+ UsfErr err = usf::UsfSensorMgr::GetSensorList(sensorList);
+ if (err != kErrNone) {
+ LOG_USF_ERR(err);
+ }
+ return err == kErrNone;
+}
+
+bool UsfHelper::startSampling(usf::UsfStartSamplingReq *request,
+ uint32_t *samplingId) {
+ bool success = false;
+
+ usf::UsfReqSyncCallback callback;
+ const usf::UsfMsgStartSamplingResp *resp;
+ if (sendUsfReqSync(request, &callback) && getRespMsg(callback, &resp)) {
+ *samplingId = resp->sampling_id();
+ success = true;
+ }
+
+ return success;
+}
+
+bool UsfHelper::reconfigureSampling(usf::UsfReconfigSamplingReq *request) {
+ usf::UsfReqSyncCallback callback;
+ return sendUsfReqSync(request, &callback);
+}
+
+bool UsfHelper::stopSampling(usf::UsfStopSamplingReq *request) {
+ usf::UsfReqSyncCallback callback;
+ return sendUsfReqSync(request, &callback);
+}
+
+bool UsfHelper::registerForStatusUpdates(
+ refcount::reffed_ptr<usf::UsfSensor> &sensor) {
+ bool success = false;
+ if (!mUsfEventListeners.emplace_back()) {
+ LOG_OOM();
+ } else {
+ UsfErr err = sensor->GetSamplingEventType()->AddListener(
+ mWorker.get(), statusUpdateHandler, this, &mUsfEventListeners.back());
+ success = (err == kErrNone);
+ if (!success) {
+ LOG_USF_ERR(err);
+ }
+ }
+
+ return success;
+}
+
+bool UsfHelper::registerForBiasUpdates(
+ const refcount::reffed_ptr<usf::UsfSensor> &sensor) {
+ // TODO(b/147595659): Obtain the latest bias values when registering for bias
+ // updates to ensure chreSensorGetThreeAxisBias doesn't return stale values.
+ bool success = false;
+ if (!mUsfEventListeners.emplace_back()) {
+ LOG_OOM();
+ } else {
+ UsfErr err = sensor->GetTransformConfigEventType()->AddListener(
+ mWorker.get(), biasUpdateHandler, this, &mUsfEventListeners.back());
+ success = (err == kErrNone);
+ if (!success) {
+ LOG_USF_ERR(err);
+ }
+ }
+
+ return success;
+}
+
+bool UsfHelper::registerForApPowerStateUpdates() {
+ bool success = false;
+ if (!mUsfEventListeners.emplace_back()) {
+ LOG_OOM();
+ } else {
+ UsfErr err = usf::UsfPowerMgr::GetApPowerEventType()->AddListener(
+ mWorker.get(), apPowerStateUpdateHandler, this,
+ &mUsfEventListeners.back());
+ success = (err == kErrNone);
+ if (!success) {
+ LOG_USF_ERR(err);
+ }
+ }
+ return success;
+}
+
+void UsfHelper::unregisterForBiasUpdates(
+ const refcount::reffed_ptr<usf::UsfSensor> &sensor) {
+ for (size_t i = 0; i < mUsfEventListeners.size(); i++) {
+ usf::UsfEventListener *listener = mUsfEventListeners[i];
+ if (listener->event_type.get() == sensor->GetTransformConfigEventType()) {
+ listener->event_type->RemoveListener(listener);
+ mUsfEventListeners.erase(i);
+ break;
+ }
+ }
+}
+
+bool UsfHelper::getThreeAxisBias(
+ const refcount::reffed_ptr<usf::UsfSensor> &sensor,
+ struct chreSensorThreeAxisData *bias) const {
+ bool success = false;
+ size_t index = getCalArrayIndex(sensor->GetType());
+ if (bias != nullptr && index < kNumUsfCalSensors) {
+ const UsfCalData &data = mCalData[index];
+ if (data.hasBias) {
+ bias->header.baseTimestamp = data.timestamp;
+ bias->header.readingCount = 1;
+ bias->header.accuracy = data.accuracy;
+ bias->header.reserved = 0;
+ for (size_t i = 0; i < ARRAY_SIZE(data.bias); i++) {
+ bias->readings[0].bias[i] = data.bias[i];
+ }
+ bias->readings[0].timestampDelta = 0;
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool UsfHelper::flushAsync(const usf::UsfServerHandle usfSensorHandle,
+ void *cookie, uint32_t *requestId) {
+ bool success = false;
+ auto req = MakeUnique<usf::UsfReq>();
+ auto callbackData = MakeUnique<AsyncCallbackData>();
+ if (req.isNull() || callbackData.isNull()) {
+ LOG_OOM();
+ } else {
+ req->SetReqType(usf::UsfMsgReqType_FLUSH_SAMPLES);
+ req->SetServerHandle(usfSensorHandle);
+ callbackData->callback = mCallback;
+ callbackData->cookie = cookie;
+
+ UsfErr err = mTransportClient->SendRequest(req.get(), asyncCallback,
+ callbackData.get());
+ success = (err == kErrNone);
+ if (!success) {
+ LOG_USF_ERR(err);
+ } else {
+ *requestId = req->GetReqId();
+ req.release();
+ callbackData.release();
+ }
+ }
+
+ return success;
+}
+
+void UsfHelper::processSensorReport(const usf::UsfMsgEvent *event) {
+ usf::UsfSensorReport *sensorReport = nullptr;
+ UniquePtr<uint8_t> sensorSample;
+ UsfErr err = usf::UsfSensorReport::Get(event, &sensorReport);
+
+ if (err != kErrNone) {
+ LOG_USF_ERR(err);
+ } else if (sensorReport->GetType() == usf::UsfSensorReport::kTypeSample) {
+ auto *sampleReport =
+ static_cast<usf::UsfSensorSampleReport *>(sensorReport);
+ if (sampleReport->GetSampleCount() == 0) {
+ LOGE("Received empty sample batch");
+ } else {
+ auto usfType = sampleReport->GetSensorType();
+ uint8_t sensorType;
+ if (PlatformSensorTypeHelpersBase::convertUsfToChreSensorType(
+ usfType, &sensorType) &&
+ !createSensorEvent(sampleReport, sensorType, sensorSample)) {
+ // USF shares the same sensor type between calibrated and uncalibrated
+ // sensors. Try the uncalibrated type to see if the sampling ID matches.
+ uint8_t uncalType =
+ SensorTypeHelpers::toUncalibratedSensorType(sensorType);
+ if (uncalType != sensorType) {
+ sensorType = uncalType;
+ createSensorEvent(sampleReport, uncalType, sensorSample);
+ // USF shares a sensor type for both gyro and accel temp.
+ } else if (sensorType == CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE) {
+ createSensorEvent(sampleReport,
+ CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE,
+ sensorSample);
+ }
+ }
+ if (!sensorSample.isNull() && mCallback != nullptr) {
+ mCallback->onSensorDataEvent(sensorType, std::move(sensorSample));
+ }
+ }
+ }
+ delete sensorReport;
+}
+
+void UsfHelper::processStatusUpdate(const usf::UsfSensorSamplingEvent *update) {
+ // TODO(stange): See if the latest status can be shared among sensor types
+ // with the same underlying physical sensor to avoid allocating multiple
+ // updates.
+ uint8_t sensorType;
+ if (PlatformSensorTypeHelpersBase::convertUsfToChreSensorType(
+ update->GetSensor()->GetType(), &sensorType)) {
+ mCallback->onSamplingStatusUpdate(sensorType, update);
+
+ // USF shares the same sensor type for calibrated and uncalibrated sensors
+ // so populate a calibrated / uncalibrated sensor for known calibrated
+ // sensor types
+ uint8_t uncalibratedType =
+ SensorTypeHelpers::toUncalibratedSensorType(sensorType);
+ if (uncalibratedType != sensorType) {
+ mCallback->onSamplingStatusUpdate(uncalibratedType, update);
+ }
+
+ // USF shares a sensor type for both gyro and accel temp.
+ if (sensorType == CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE) {
+ mCallback->onSamplingStatusUpdate(
+ CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE, update);
+ }
+ }
+}
+
+void UsfHelper::processBiasUpdate(
+ const usf::UsfSensorTransformConfigEvent *update) {
+ uint8_t sensorType;
+ if (PlatformSensorTypeHelpersBase::convertUsfToChreSensorType(
+ update->GetSensor()->GetType(), &sensorType)) {
+ UniquePtr<struct chreSensorThreeAxisData> biasData =
+ convertUsfBiasUpdateToData(update);
+ if (!biasData.isNull()) {
+ // USF shares the same sensor type for calibrated and uncalibrated sensors
+ // so populate a calibrated / uncalibrated sensor for known calibrated
+ // sensor types
+ uint8_t uncalibratedType =
+ SensorTypeHelpers::toUncalibratedSensorType(sensorType);
+ if (uncalibratedType != sensorType) {
+ auto uncalBiasData =
+ MakeUniqueZeroFill<struct chreSensorThreeAxisData>();
+ if (uncalBiasData.isNull()) {
+ LOG_OOM();
+ } else {
+ *uncalBiasData = *biasData;
+ mCallback->onBiasUpdate(uncalibratedType, std::move(uncalBiasData));
+ }
+ }
+ mCallback->onBiasUpdate(sensorType, std::move(biasData));
+ }
+ }
+}
+
+void UsfHelper::processApPowerStateUpdate(
+ const usf::UsfApPowerStateEvent *update) {
+ usf::UsfApPowerState state = update->GetState();
+ if (state == usf::kUsfApPowerStateInvalid) {
+ LOGE("Invalid AP power state received.");
+ } else {
+ mCallback->onHostWakeSuspendEvent(state == usf::kUsfApPowerStateOn);
+ }
+}
+
+bool UsfHelper::createSensorEvent(
+ const usf::UsfSensorSampleReport *sampleReport, uint8_t sensorType,
+ UniquePtr<uint8_t> &sensorSample) {
+ bool success = false;
+
+ SensorInfo info;
+ if (mCallback != nullptr && mCallback->getSensorInfo(sensorType, &info) &&
+ (info.samplingId == sampleReport->GetSamplingId())) {
+ if (prepareSensorEvent(sampleReport->GetSampleCount(), info.sensorHandle,
+ sensorType, sensorSample)) {
+ populateSensorEvent(sampleReport, sensorType, sensorSample);
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool UsfHelper::sendUsfReqSync(usf::UsfReq *req,
+ usf::UsfReqSyncCallback *callback) {
+ UsfErr err = mTransportClient->SendRequest(
+ req, usf::UsfReqSyncCallback::Callback, callback);
+ if (err != kErrNone) {
+ LOG_USF_ERR(err);
+ } else {
+ // TODO: Provide a different timeout for operations we expect to take longer
+ // e.g. initial sensor discovery.
+ callback->Wait(kDefaultUsfWaitTimeout.toRawNanoseconds());
+ if ((err = callback->GetResp()->GetErr()) != kErrNone) {
+ LOG_USF_ERR(err);
+ }
+ }
+
+ return err == kErrNone;
+}
+
+template <class T>
+bool UsfHelper::getRespMsg(usf::UsfReqSyncCallback &callback,
+ const T **respMsg) {
+ UsfErr err = usf::UsfRespGetMsg(callback.GetResp(), respMsg);
+ if (err != kErrNone) {
+ LOG_USF_ERR(err);
+ }
+
+ return err == kErrNone;
+}
+
+UniquePtr<struct chreSensorThreeAxisData> UsfHelper::convertUsfBiasUpdateToData(
+ const usf::UsfSensorTransformConfigEvent *update) {
+ UniquePtr<struct chreSensorThreeAxisData> biasData;
+ size_t index = getCalArrayIndex(update->GetSensor()->GetType());
+ if (index < kNumUsfCalSensors &&
+ update->GetLevel() == usf::kUsfSensorTransformRunTimeCalibration) {
+ UsfCalData &data = mCalData[index];
+ UsfErr err = usf::UsfTimeMgr::GetAndroidTimeNs(&data.timestamp);
+ if (err != kErrNone) {
+ LOG_USF_ERR(err);
+ }
+ data.hasBias = (update->GetOffset() != nullptr);
+ for (size_t i = 0; i < ARRAY_SIZE(data.bias); i++) {
+ data.bias[i] = data.hasBias ? update->GetOffset()[i] : 0;
+ }
+ data.accuracy =
+ PlatformSensorTypeHelpersBase::convertUsfToChreSampleAccuracy(
+ update->GetAccuracy());
+
+ biasData = MakeUniqueZeroFill<struct chreSensorThreeAxisData>();
+ if (biasData.isNull()) {
+ LOG_OOM();
+ } else {
+ biasData->header.baseTimestamp = data.timestamp;
+ biasData->header.readingCount = 1;
+ biasData->header.accuracy = data.accuracy;
+ biasData->header.reserved = 0;
+ for (size_t i = 0; i < ARRAY_SIZE(data.bias); i++) {
+ biasData->readings[0].bias[i] = data.bias[i];
+ }
+ biasData->readings[0].timestampDelta = 0;
+ }
+ }
+
+ return biasData;
+}
+
+size_t UsfHelper::getCalArrayIndex(const usf::UsfSensorType usfSensorType) {
+ UsfCalSensor index;
+ switch (usfSensorType) {
+ case usf::UsfSensorType::kUsfSensorAccelerometer:
+ index = UsfCalSensor::AccelCal;
+ break;
+ case usf::UsfSensorType::kUsfSensorGyroscope:
+ index = UsfCalSensor::GyroCal;
+ break;
+ case usf::UsfSensorType::kUsfSensorMag:
+ index = UsfCalSensor::MagCal;
+ break;
+ default:
+ index = UsfCalSensor::NumCalSensors;
+ break;
+ }
+
+ return static_cast<size_t>(index);
+}
+
+} // namespace chre
diff --git a/util/include/chre/util/macros.h b/util/include/chre/util/macros.h
index 92160b8..2fe7d7e 100644
--- a/util/include/chre/util/macros.h
+++ b/util/include/chre/util/macros.h
@@ -24,14 +24,26 @@
/**
* Obtains the number of elements in a C-style array.
*/
+#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+#endif
+
+#ifndef ARRAY_END
#define ARRAY_END(array) (array + ARRAY_SIZE(array))
+#endif
+
+/** Determines if the provided bit is set in the provided value. */
+#ifndef IS_BIT_SET
+#define IS_BIT_SET(value, bit) (((value) & (bit)) == (bit))
+#endif
/**
* Performs macro expansion then converts the value into a string literal
*/
+#ifndef STRINGIFY
#define STRINGIFY(x) STRINGIFY2(x)
#define STRINGIFY2(x) #x
+#endif
// Compiler-specific functionality
#if defined(__clang__) || defined(__GNUC__)
diff --git a/util/include/chre/util/system/napp_header_utils.h b/util/include/chre/util/system/napp_header_utils.h
new file mode 100644
index 0000000..e9ab416
--- /dev/null
+++ b/util/include/chre/util/system/napp_header_utils.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#ifndef CHRE_UTIL_SYSTEM_NAPP_HEADER_UTILS_H_
+#define CHRE_UTIL_SYSTEM_NAPP_HEADER_UTILS_H_
+
+//! Defines various constants used in the nanoapp headers defined inside
+//! build/build_template.mk.
+#define CHRE_NAPP_HEADER_SIGNED 0x00000001
+#define CHRE_NAPP_HEADER_ENCRYPTED 0x00000002
+#define CHRE_NAPP_HEADER_TCM_CAPABLE 0x00000004
+
+#endif // CHRE_UTIL_SYSTEM_NAPP_HEADER_UTILS_H_